From 25f50483e1b1f204ab1512ca525eae7b1d96ec6e Mon Sep 17 00:00:00 2001 From: Jonatan Nilsson Date: Sun, 3 Sep 2023 19:44:45 +0000 Subject: [PATCH] eltro: Fixed flow. before/after/beforeEach/afterEach now can be defined multiple times in a group. In addition, beforeEach and afterEach get called for each children of said group. --- lib/eltro.mjs | 89 ++++--- test/eltro.flow.test.mjs | 504 +++++++++++++++++++++++++++++++++++---- 2 files changed, 515 insertions(+), 78 deletions(-) diff --git a/lib/eltro.mjs b/lib/eltro.mjs index dcae736..7b750f4 100644 --- a/lib/eltro.mjs +++ b/lib/eltro.mjs @@ -276,7 +276,9 @@ Eltro.prototype.__runTest = async function(stats, test, prefix = 'Test', child = if (markRealTest.skipTest) { process.stdout.write(' \x1b[94m- ' + markRealTest.name + '\x1b[0m\n') } else if (!markRealTest.error) { - process.stdout.write(' \x1b[32m√\x1b[90m ' + markRealTest.name + ' (' + markRealTest.totalTime + 'ms)\x1b[0m\n') + if (!test.name.startsWith('~')) { + process.stdout.write(' \x1b[32m√\x1b[90m ' + markRealTest.name + ' (' + markRealTest.totalTime + 'ms)\x1b[0m\n') + } } else if (prefix === 'Test') { process.stdout.write(' \x1b[31m' + this.failedTests.length + ') ' + markRealTest.name + ' (' + markRealTest.totalTime + 'ms)\x1b[0m\n') } @@ -298,14 +300,17 @@ Eltro.prototype.__runGroup = async function(g, stats) { } } if (g.before) { - await this.__runTest(stats, g.before, 'Before') - if (g.before.error) return + for (let i = 0; i < g.before.length; i++) { + await this.__runTest(stats, g.before[i], 'Before') + if (g.before[i].error) return + } } for (let x = 0; x < g.tests.length; x++) { if (!g.tests[x].skipTest && g.tests[x].isExclusive === g.hasExclusive) { - if (g.beforeEach) { - await this.__runTest(stats, g.beforeEach, 'Before each: ', g.tests[x]) + for (let i = 0; i < g.beforeEach.length && !g.tests[x].error; i++) { + await this.__runTest(stats, g.beforeEach[i], 'Before each: ', g.tests[x]) + } if (!g.tests[x].error) { await this.__runTest(stats, g.tests[x]) } @@ -313,24 +318,26 @@ Eltro.prototype.__runGroup = async function(g, stats) { await this.__runTest(stats, g.tests[x]) } if (g.afterEach) { - await this.__runTest(stats, g.afterEach, 'After each: ', g.tests[x]) + let oldError = g.tests[x].error + g.tests[x].error = null + for (let i = 0; i < g.afterEach.length && !g.tests[x].error; i++) { + await this.__runTest(stats, g.afterEach[i], 'After each: ', g.tests[x]) + } + if (oldError) { + g.tests[x].error = oldError + } } } } for (let x = 0; x < g.groups.length; x++) { if (!g.groups[x].skipTest && g.hasExclusive === (g.groups[x].hasExclusive || g.groups[x].isExclusive)) { - if (g.beforeEach) { - await this.__runTest(stats, g.beforeEach, g.groups[x].name + ': ', g.beforeEach) - if (g.beforeEach.error) continue - } await this.__runGroup(g.groups[x], stats) - if (g.afterEach) { - await this.__runTest(stats, g.afterEach, g.groups[x].name + ': ', g.afterEach) - } } } if (g.after) { - await this.__runTest(stats, g.after, 'After') + for (let i = 0; i < g.after.length && !g.after.error; i++) { + await this.__runTest(stats, g.after[i], 'After') + } } } @@ -360,11 +367,9 @@ Eltro.prototype.run = async function() { if (this.reporter === 'test') { if (this.logger && this.logger.log) { - if (this.failedTests.length) { - for (let x = 0; x < this.failedTests.length; x++) { - let test = this.failedTests[x]; - this.logger.log(test.name, test.error) - } + for (let x = 0; x < this.failedTests.length; x++) { + let test = this.failedTests[x]; + this.logger.log(test.name, test.error) } } } else if (this.reporter) { @@ -406,30 +411,48 @@ Eltro.prototype.resetFilename = function() { } let beforesandafters = [ - ['before', 'Before'], - ['after', 'After'], - ['beforeEach', 'Before each'], - ['afterEach', 'After each'], + ['before', '~Before', false], + ['after', '~After', false], + ['beforeEach', '~Before each', true], + ['afterEach', '~After each', true], ] beforesandafters.forEach(function(item) { - Eltro.prototype[item[0]] = function(func) { + let beforeAfter = item[0] + let fullName = item[1] + let bringToChildren = item[2] + + Eltro.prototype[beforeAfter] = function(func) { if (!this.activeGroup) { throw new Error('Tests outside groups are not allowed.') } - - let test = new Test(this, this.activeGroup, item[1] + ': ' + this.activeGroup.name, func) + + let test = func + + if (!(test instanceof Test)) { + test = new Test(this, this.activeGroup, fullName + ': ' + this.activeGroup.name, func) + } if (this.temporary.timeout || this.activeGroup.customTimeout) { test.timeout(this.temporary.timeout || this.activeGroup.customTimeout) this.temporary.timeout = 0 } - - this.activeGroup[item[0]] = test + + this.activeGroup[beforeAfter] = this.activeGroup[beforeAfter] || [] + this.activeGroup[beforeAfter].push(test) + + if (bringToChildren) { + for (let group of this.activeGroup.groups) { + group[beforeAfter].push(test) + } + } + return test } }) +let bringToChildren = ['beforeEach', 'afterEach'] + Eltro.prototype.describe = function(name, func) { let before = this.activeGroup @@ -458,6 +481,16 @@ Eltro.prototype.describe = function(name, func) { this.temporary.only = false } + if (before) { + for (let beforeAfter of bringToChildren) { + if (!before[beforeAfter]) continue + + for (let test of before[beforeAfter]) { + this[beforeAfter](test) + } + } + } + func() this.activeGroup = before diff --git a/test/eltro.flow.test.mjs b/test/eltro.flow.test.mjs index e3b587d..85fc960 100644 --- a/test/eltro.flow.test.mjs +++ b/test/eltro.flow.test.mjs @@ -52,23 +52,74 @@ e.describe('#before()', function() { assert.strictEqual(secondBefore, 1) assert.strictEqual(thirdBefore, 4) }) -}) + e.test('should support multiple functions in describe group', async function() { + let assertRan = 0 + let firstBefore = -1 + let secondBefore = -1 + let thirdBefore = -1 + let fourthBefore = -1 -e.describe('#beforeEach()', function() { - e.test('should support functions in describe group and run before each test and group', async function() { const t = CreateT() t.begin() t.describe('', function() { - let outside = 0 + t.before(function() { + firstBefore = assertRan + }) + + t.describe('', function() { + t.before(function() { + thirdBefore = assertRan + }) + + t.test('', function() { assertRan++ }) + t.test('', function() { assertRan++ }) + t.test('', function() { assertRan++ }) + }) + + t.describe('', function() { + t.before(function() { + fourthBefore = assertRan + }) + + t.test('', function() { assertRan++ }) + }) + + t.test('', function() { assertRan++ }) + + t.before(function() { + secondBefore = assertRan + }) + }) + let stats = await t.run() + assert.strictEqual(t.failedTests.length, 0) + assert.strictEqual(assertRan, 5) + assert.strictEqual(stats.passed, 5) + assert.strictEqual(firstBefore, 0) + assert.strictEqual(secondBefore, 0) + assert.strictEqual(thirdBefore, 1) + assert.strictEqual(fourthBefore, 4) + }) +}) + +e.describe('#beforeEach()', function() { + e.test('should support functions in describe group and run before each test and each test in every group', async function() { + let outside = 0 + + const t = CreateT() + t.begin() + t.describe('', function() { + t.beforeEach(function() { outside++ }) t.describe('', function() { - t.before(function() { - assert.strictEqual(outside, 2) - }) let inside = 0 + + t.before(function() { + assert.strictEqual(outside, 1) + }) + t.beforeEach(function() { inside++ }) @@ -79,11 +130,12 @@ e.describe('#beforeEach()', function() { }) t.describe('', function() { - t.before(function() { - assert.strictEqual(outside, 3) - }) + let insideSecond = 0 - let insideSecond = 0 + t.before(function() { + assert.strictEqual(outside, 4) + }) + t.beforeEach(function() { assert.strictEqual(insideSecond, 0) insideSecond++ @@ -99,6 +151,97 @@ e.describe('#beforeEach()', function() { assert.strictEqual(stats.passed, 5) assert.strictEqual(stats.failed, 0) assert.strictEqual(stats.skipped, 0) + assert.strictEqual(outside, 5) + }) + + e.test('should work even if before is specifed after all the tests', async function() { + let outside = 0 + + const t = CreateT() + t.begin() + t.describe('', function() { + t.describe('', function() { + let inside = 0 + + t.before(function() { + assert.strictEqual(outside, 1) + }) + + t.test('', function() { assert.strictEqual(inside, 1) }) + t.test('', function() { assert.strictEqual(inside, 2) }) + t.test('', function() { assert.strictEqual(inside, 3) }) + + t.beforeEach(function() { + inside++ + }) + }) + + t.describe('', function() { + let insideSecond = 0 + + t.before(function() { + assert.strictEqual(outside, 4) + }) + + t.test('', function() { assert.strictEqual(insideSecond, 1) }) + + t.beforeEach(function() { + assert.strictEqual(insideSecond, 0) + insideSecond++ + }) + }) + + t.test('', function() { assert.strictEqual(outside, 1) }) + + t.beforeEach(function() { + outside++ + }) + }) + let stats = await t.run() + assert.strictEqual(t.failedTests.length, 0) + assert.strictEqual(stats.passed, 5) + assert.strictEqual(stats.failed, 0) + assert.strictEqual(stats.skipped, 0) + assert.strictEqual(outside, 5) + }) + + e.test('should support multiple beforeEach', async function() { + let outside = 0 + let inside = 0 + + const t = CreateT() + t.begin() + t.describe('', function() { + t.beforeEach(function() { + outside++ + }) + + t.describe('', function() { + t.beforeEach(function() { + inside++ + }) + + t.test('', function() { assert.strictEqual(inside, 2) }) + t.test('', function() { assert.strictEqual(inside, 4) }) + t.test('', function() { assert.strictEqual(inside, 6) }) + + t.beforeEach(function() { + inside++ + }) + }) + + t.test('', function() { assert.strictEqual(outside, 2) }) + + t.beforeEach(function() { + outside++ + }) + }) + let stats = await t.run() + assert.strictEqual(t.failedTests.length, 0) + assert.strictEqual(stats.passed, 4) + assert.strictEqual(stats.failed, 0) + assert.strictEqual(stats.skipped, 0) + assert.strictEqual(outside, 8) }) e.test('should be able to keep track of every error that occurs', async function() { @@ -112,6 +255,7 @@ e.describe('#beforeEach()', function() { t.describe('CCCC', function() { t.test('', function() { }) + t.test('', function() { }) }) t.describe('DDDD', function() { @@ -121,20 +265,24 @@ e.describe('#beforeEach()', function() { t.test('AAAA', function() { }) }) let stats = await t.run() - assert.strictEqual(t.failedTests.length, 3) - assert.strictEqual(t.logger.log.callCount, 3) - assert.match(t.logger.log.firstCall[1].message, /1/) - assert.match(t.logger.log.firstCall[0], /before each/i) - assert.match(t.logger.log.firstCall[0], /AAAA/) - assert.match(t.logger.log.firstCall[0], /BBBB/) - assert.match(t.logger.log.secondCall[1].message, /2/) - assert.match(t.logger.log.secondCall[0], /before each/i) - assert.match(t.logger.log.secondCall[0], /CCCC/) - assert.match(t.logger.log.secondCall[0], /BBBB/) - assert.match(t.logger.log.thirdCall[1].message, /3/) - assert.match(t.logger.log.thirdCall[0], /before each/i) - assert.match(t.logger.log.thirdCall[0], /DDDD/) - assert.match(t.logger.log.thirdCall[0], /BBBB/) + assert.strictEqual(t.failedTests.length, 4) + assert.strictEqual(t.logger.log.callCount, 4) + assert.match(t.logger.log.getCallN(1)[1].message, /1/) + assert.match(t.logger.log.getCallN(1)[0], /before each/i) + assert.match(t.logger.log.getCallN(1)[0], /AAAA/) + assert.match(t.logger.log.getCallN(1)[0], /BBBB/) + assert.match(t.logger.log.getCallN(2)[1].message, /2/) + assert.match(t.logger.log.getCallN(2)[0], /before each/i) + assert.match(t.logger.log.getCallN(2)[0], /CCCC/) + assert.match(t.logger.log.getCallN(2)[0], /BBBB/) + assert.match(t.logger.log.getCallN(3)[1].message, /3/) + assert.match(t.logger.log.getCallN(3)[0], /before each/i) + assert.match(t.logger.log.getCallN(3)[0], /CCCC/) + assert.match(t.logger.log.getCallN(3)[0], /BBBB/) + assert.match(t.logger.log.getCallN(4)[1].message, /4/) + assert.match(t.logger.log.getCallN(4)[0], /before each/i) + assert.match(t.logger.log.getCallN(4)[0], /DDDD/) + assert.match(t.logger.log.getCallN(4)[0], /BBBB/) }) }) @@ -179,6 +327,81 @@ e.describe('#after()', function() { assert.strictEqual(secondAfter, 4) assert.strictEqual(thirdAfter, 5) }) + e.test('should support multiple functions in describe group', async function() { + let assertRan = 0 + let firstAfter = -1 + let secondAfter = -1 + let thirdAfter = -1 + let fourthAfter = -1 + const t = CreateT() + t.begin() + t.describe('', function() { + t.after(function() { + firstAfter = assertRan + }) + + t.describe('', function() { + t.after(function() { + thirdAfter = assertRan + }) + + t.test('', function() { assertRan++ }) + t.test('', function() { assertRan++ }) + t.test('', function() { assertRan++ }) + }) + + t.describe('', function() { + t.after(function() { + fourthAfter = assertRan + }) + + t.test('', function() { assertRan++ }) + }) + + t.test('', function() { assertRan++ }) + + t.after(function() { + secondAfter = assertRan + }) + }) + let stats = await t.run() + assert.strictEqual(t.failedTests.length, 0) + assert.strictEqual(stats.passed, 5) + assert.strictEqual(assertRan, 5) + + assert.strictEqual(firstAfter, 5) + assert.strictEqual(secondAfter, 5) + assert.strictEqual(thirdAfter, 4) + assert.strictEqual(fourthAfter, 5) + }) + + e.test('should log even if it throws and test throws', async function() { + const assertError = new Error('test') + const t = CreateT() + t.begin() + t.describe('', function() { + t.after(function() { throw assertError }) + + t.describe('', function() { + t.after(function() { throw assertError }) + + t.test('', function() { throw assertError }) + t.test('', function() { }) + t.test('', function() { throw assertError }) + }) + + t.test('', function() { throw assertError }) + + t.after(function() { throw assertError }) + }) + let stats = await t.run() + assert.strictEqual(t.failedTests.length, 6) + assert.strictEqual(stats.passed, 1) + + for (let failedTest of t.failedTests) { + assert.strictEqual(failedTest.error, assertError) + } + }) }) e.describe('#afterEach()', function() { @@ -187,14 +410,16 @@ e.describe('#afterEach()', function() { t.begin() t.describe('', function() { let outside = 0 + t.afterEach(function() { outside++ }) t.describe('', function() { + let inside = 0 + t.before(function() { assert.strictEqual(outside, 1) }) - let inside = 0 t.afterEach(function() { inside++ }) @@ -206,10 +431,11 @@ e.describe('#afterEach()', function() { t.after(function() { assert.strictEqual(inside, 3) }) }) - t.describe('', function() { - t.before(function() { assert.strictEqual(outside, 2) }) + t.describe('', function() { + let inside = 0 + + t.before(function() { assert.strictEqual(outside, 4) }) - let inside = 0 t.afterEach(function() { inside++ }) @@ -221,7 +447,185 @@ e.describe('#afterEach()', function() { t.test('', function() { assert.strictEqual(outside, 0) }) - t.after(function() { assert.strictEqual(outside, 3) }) + t.after(function() { assert.strictEqual(outside, 5) }) + }) + let stats = await t.run() + assert.strictEqual(t.failedTests.length, 0) + assert.strictEqual(stats.passed, 5) + assert.strictEqual(stats.failed, 0) + assert.strictEqual(stats.skipped, 0) + }) + + e.test('should work even if after is specified after the tests', async function() { + const t = CreateT() + t.begin() + t.describe('', function() { + let outside = 0 + + t.describe('', function() { + let inside = 0 + + t.before(function() { assert.strictEqual(outside, 1) }) + + t.test('', function() { assert.strictEqual(inside, 0) }) + t.test('', function() { assert.strictEqual(inside, 1) }) + t.test('', function() { assert.strictEqual(inside, 2) }) + + t.after(function() { assert.strictEqual(inside, 3) }) + + t.afterEach(function() { + inside++ + }) + }) + + t.describe('', function() { + let inside = 0 + + t.before(function() { assert.strictEqual(outside, 4) }) + + t.test('', function() { assert.strictEqual(inside, 0) }) + + t.after(function() { assert.strictEqual(inside, 1) }) + + t.afterEach(function() { + inside++ + }) + }) + + t.test('', function() { assert.strictEqual(outside, 0) }) + + t.after(function() { assert.strictEqual(outside, 5) }) + + t.afterEach(function() { + outside++ + }) + }) + let stats = await t.run() + assert.strictEqual(t.failedTests.length, 0) + assert.strictEqual(stats.passed, 5) + assert.strictEqual(stats.failed, 0) + assert.strictEqual(stats.skipped, 0) + }) + + e.test('should run even if each test throws', async function() { + let outside = 0 + let inside = 0 + const assertError = new Error('test') + + const t = CreateT() + t.begin() + t.describe('', function() { + t.describe('', function() { + t.test('', function() { throw assertError }) + t.test('', function() { throw assertError }) + t.test('', function() { throw assertError }) + + t.afterEach(function() { + inside++ + }) + }) + + t.test('', function() { throw assertError }) + + t.afterEach(function() { + outside++ + }) + }) + let stats = await t.run() + assert.strictEqual(t.failedTests.length, 4) + for (let failedTest of t.failedTests) { + assert.strictEqual(failedTest.error, assertError) + } + assert.strictEqual(stats.passed, 0) + assert.strictEqual(stats.failed, 4) + assert.strictEqual(stats.skipped, 0) + assert.strictEqual(outside, 4) + assert.strictEqual(inside, 3) + }) + + e.test('should log even if afterEach fails', async function() { + const assertError = new Error('test') + + const t = CreateT() + t.begin() + t.describe('', function() { + t.describe('', function() { + t.test('', function() { throw assertError }) + t.test('', function() { throw assertError }) + t.test('', function() { throw assertError }) + + t.afterEach(function() { throw assertError }) + }) + + t.test('', function() { throw assertError }) + + t.afterEach(function() { throw assertError }) + }) + let stats = await t.run() + assert.strictEqual(t.failedTests.length, 8) + for (let failedTest of t.failedTests) { + assert.strictEqual(failedTest.error, assertError) + } + assert.strictEqual(stats.passed, 0) + assert.strictEqual(stats.failed, 8) + assert.strictEqual(stats.skipped, 0) + }) + + e.test('should support multiple afterEach', async function() { + const t = CreateT() + t.begin() + t.describe('', function() { + let outside = 0 + + t.afterEach(function() { + outside++ + }) + + t.describe('', function() { + let inside = 0 + + t.before(function() { assert.strictEqual(outside, 2) }) + + t.afterEach(function() { + inside++ + }) + + t.test('', function() { assert.strictEqual(inside, 0) }) + t.test('', function() { assert.strictEqual(inside, 2) }) + t.test('', function() { assert.strictEqual(inside, 4) }) + + t.after(function() { assert.strictEqual(inside, 6) }) + + t.afterEach(function() { + inside++ + }) + }) + + t.describe('', function() { + let inside = 0 + + t.before(function() { assert.strictEqual(outside, 8) }) + + t.afterEach(function() { + inside++ + }) + + t.test('', function() { assert.strictEqual(inside, 0) }) + + t.after(function() { assert.strictEqual(inside, 2) }) + + t.afterEach(function() { + inside++ + }) + }) + + t.test('', function() { assert.strictEqual(outside, 0) }) + + t.after(function() { assert.strictEqual(outside, 10) }) + + t.afterEach(function() { + outside++ + }) }) let stats = await t.run() assert.strictEqual(t.failedTests.length, 0) @@ -254,26 +658,26 @@ e.describe('#afterEach()', function() { let stats = await t.run() assert.strictEqual(t.failedTests.length, 5) assert.strictEqual(t.logger.log.callCount, 5) - assert.match(t.logger.log.getCall(0)[1].message, /1/) - assert.match(t.logger.log.getCall(0)[0], /after each/i) - assert.match(t.logger.log.getCall(0)[0], /AAAA/) - assert.match(t.logger.log.getCall(0)[0], /YYYY/) - assert.match(t.logger.log.getCall(1)[1].message, /2/) - assert.match(t.logger.log.getCall(1)[0], /after each/i) - assert.match(t.logger.log.getCall(1)[0], /BBBB/) - assert.match(t.logger.log.getCall(1)[0], /YYYY/) - assert.match(t.logger.log.getCall(2)[1].message, /3/) - assert.match(t.logger.log.getCall(2)[0], /after each/i) - assert.match(t.logger.log.getCall(2)[0], /CCCC/) - assert.match(t.logger.log.getCall(2)[0], /YYYY/) - assert.match(t.logger.log.getCall(3)[1].message, /4/) - assert.match(t.logger.log.getCall(3)[0], /after each/i) - assert.match(t.logger.log.getCall(3)[0], /HHHH/) - assert.match(t.logger.log.getCall(3)[0], /YYYY/) - assert.match(t.logger.log.getCall(4)[1].message, /5/) - assert.match(t.logger.log.getCall(4)[0], /after each/i) - assert.match(t.logger.log.getCall(4)[0], /JJJJ/) - assert.match(t.logger.log.getCall(4)[0], /YYYY/) + assert.match(t.logger.log.getCallN(1)[1].message, /1/) + assert.match(t.logger.log.getCallN(1)[0], /after each/i) + assert.match(t.logger.log.getCallN(1)[0], /AAAA/) + assert.match(t.logger.log.getCallN(1)[0], /YYYY/) + assert.match(t.logger.log.getCallN(2)[1].message, /2/) + assert.match(t.logger.log.getCallN(2)[0], /after each/i) + assert.match(t.logger.log.getCallN(2)[0], /BBBB/) + assert.match(t.logger.log.getCallN(2)[0], /YYYY/) + assert.match(t.logger.log.getCallN(3)[1].message, /3/) + assert.match(t.logger.log.getCallN(3)[0], /after each/i) + assert.match(t.logger.log.getCallN(3)[0], /CCCC/) + assert.match(t.logger.log.getCallN(3)[0], /YYYY/) + assert.match(t.logger.log.getCallN(4)[1].message, /4/) + assert.match(t.logger.log.getCallN(4)[0], /after each/i) + assert.match(t.logger.log.getCallN(4)[0], /HHHH/) + assert.match(t.logger.log.getCallN(4)[0], /YYYY/) + assert.match(t.logger.log.getCallN(5)[1].message, /5/) + assert.match(t.logger.log.getCallN(5)[0], /after each/i) + assert.match(t.logger.log.getCallN(5)[0], /JJJJ/) + assert.match(t.logger.log.getCallN(5)[0], /YYYY/) }) })