From 084b751b5b26177356ffc11868a282472f2330ee Mon Sep 17 00:00:00 2001 From: Jonatan Nilsson Date: Fri, 18 Jun 2021 02:06:08 +0000 Subject: [PATCH] Eltro: Added both before() and after() support and functionality --- README.md | 76 ++++++++- lib/eltro.mjs | 59 ++++++- package.json | 2 +- test/eltro.test.mjs | 406 ++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 517 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index d787f75..848f565 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,19 @@ Next in your favourite editor, create `test/test.mjs`: import { Eltro as t, assert} from 'eltro' t.describe('Array', function() { + t.before(function() { + // Prepare our test if needed + }) + t.describe('#indexOf()', function() { t.test('should return -1 when value is not present', function() { assert.equal([1,2,3].indexOf(4), -1) }) }) + + t.after(function() { + // Cleanup after if needed + }) }) ``` @@ -140,12 +148,12 @@ import { Eltro as t, assert} from 'eltro' function someFunction() { return true } -t.test('#someFunction()', function() { +t.describe('#someFunction()', function() { t.test('should always return true', function() { assert.strictEqual(someFunction(), true) assert.strictEqual(someFunction(), true) assert.strictEqual(someFunction(), true) - }).skip() + }) }) ``` @@ -155,6 +163,70 @@ will output: √ #someFunction() should always return true ``` +### t.before(func) + +Queue up the `func` to run before any test or groups within current active group. + +```node +import { Eltro as t, assert} from 'eltro' + +t.before(function() { + // Prepare something before we start any of the below tests +}) + +t.describe('#myTest()', function() { + t.before(function() { + // Runs before the test below + }) + + t.test('true should always be true', function() { + assert.strictEqual(true, true) + }) +}) + +t.describe('#anotherTest()', function() { + t.before(function() { + // Runs before the test below + }) + + t.test('false should always be false', function() { + assert.strictEqual(false, false) + }) +}) +``` + +### t.after(func) + +Queue up the `func` to run after any test or groups within current active group. + +```node +import { Eltro as t, assert} from 'eltro' + +t.after(function() { + // After we finish all the tests below, this gets run +}) + +t.describe('#myTest()', function() { + t.after(function() { + // Runs after the test below + }) + + t.test('true should always be true', function() { + assert.strictEqual(true, true) + }) +}) + +t.describe('#anotherTest()', function() { + t.after(function() { + // Runs after the test below + }) + + t.test('false should always be false', function() { + assert.strictEqual(false, false) + }) +}) +``` + ### t.only() Eltro supports exclusivity when running tests. When specified, only tests marked with only will be run. diff --git a/lib/eltro.mjs b/lib/eltro.mjs index 5d997c5..277b6ed 100644 --- a/lib/eltro.mjs +++ b/lib/eltro.mjs @@ -10,6 +10,8 @@ function Group(e, name) { this.customTimeout = null this.skipTest = false this.isExclusive = false + this.before = null + this.after = null } Group.prototype.timeout = function(time) { @@ -53,6 +55,7 @@ function Test(e, group, name, func) { this.skipTest = false this.isExclusive = false this.customTimeout = null + this.isBasic = false this.name = name this.func = func this.error = null @@ -108,7 +111,7 @@ Eltro.prototype.begin = function() { this.fileGroupMap.clear() } -Eltro.prototype.__runTest = async function(stats, test) { +Eltro.prototype.__runTest = async function(stats, test, prefix = 'Test') { if (this.reporter === 'list') { process.stdout.write(' \x1b[90m? ' + test.name + '\x1b[0m') } @@ -187,16 +190,18 @@ Eltro.prototype.__runTest = async function(stats, test) { } }) .then(function() { - stats.passed++ + if (prefix === 'Test') { + stats.passed++ + } }, function(err) { let saveError = err if (!saveError) { - saveError = new Error('Test promise rejected with empty message') + saveError = new Error(prefix + ' promise rejected with empty message') } else if (typeof(saveError) !== 'object' || saveError.message == null || saveError.stack == null) { try { - saveError = new Error('Test promise rejected with ' + JSON.stringify(saveError)) + saveError = new Error(prefix + ' promise rejected with ' + JSON.stringify(saveError)) } catch (parseError) { - saveError = new Error('Test promise rejected with ' + saveError + ' (Error stringifying: ' + parseError.message + ')') + saveError = new Error(prefix + ' promise rejected with ' + saveError + ' (Error stringifying: ' + parseError.message + ')') } saveError.originalError = err } @@ -219,7 +224,7 @@ Eltro.prototype.__runTest = async function(stats, test) { process.stdout.write(' \x1b[94m- ' + test.name + '\x1b[0m\n') } else if (!test.error) { process.stdout.write(' \x1b[32m√\x1b[90m ' + test.name + '\x1b[0m\n') - } else { + } else if (prefix === 'Test') { process.stdout.write(' \x1b[31m' + this.failedTests.length + ') ' + test.name + '\x1b[0m\n') } } else if (this.reporter === 'dot') { @@ -227,7 +232,7 @@ Eltro.prototype.__runTest = async function(stats, test) { process.stdout.write('\x1b[94m.\x1b[0m') } else if (!test.error) { process.stdout.write('\x1b[32m.\x1b[0m') - } else { + } else if (prefix === 'Test') { process.stdout.write('\x1b[31m.\x1b[0m') } } @@ -239,6 +244,10 @@ Eltro.prototype.__runGroup = async function(g, stats) { console.log(' ' + g.name) } } + if (g.before) { + await this.__runTest(stats, g.before, 'Before') + if (g.before.error) return + } for (let x = 0; x < g.tests.length; x++) { if (!g.tests[x].skipTest && g.tests[x].isExclusive === g.hasExclusive) { await this.__runTest(stats, g.tests[x]) @@ -248,6 +257,9 @@ Eltro.prototype.__runGroup = async function(g, stats) { if (!g.groups[x].skipTest && g.hasExclusive === (g.groups[x].hasExclusive || g.groups[x].isExclusive)) await this.__runGroup(g.groups[x], stats) } + if (g.after) { + await this.__runTest(stats, g.after, 'After') + } } Eltro.prototype.run = async function() { @@ -293,6 +305,7 @@ Eltro.prototype.run = async function() { } } } + return stats } Eltro.prototype.setFilename = function(filename) { @@ -308,6 +321,38 @@ Eltro.prototype.resetFilename = function() { this.activeGroup = null } +Eltro.prototype.before = function(func) { + if (!this.activeGroup) { + throw new Error('Tests outside groups are not allowed.') + } + + let test = new Test(this, this.activeGroup, 'Before: ' + 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.before = test + return test +} + +Eltro.prototype.after = function(func) { + if (!this.activeGroup) { + throw new Error('Tests outside groups are not allowed.') + } + + let test = new Test(this, this.activeGroup, 'After: ' + 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.after = test + return test +} + Eltro.prototype.describe = function(name, func) { let before = this.activeGroup diff --git a/package.json b/package.json index 3bf6568..815ed4a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eltro", - "version": "1.0.2", + "version": "1.1.0", "description": "Eltro is a tiny no-dependancy test framework for node", "main": "index.mjs", "scripts": { diff --git a/test/eltro.test.mjs b/test/eltro.test.mjs index 60254ff..b4f2ecd 100644 --- a/test/eltro.test.mjs +++ b/test/eltro.test.mjs @@ -224,6 +224,22 @@ e.test('Eltro should support timed out tests on late tests', async function() { assert.match(t.failedTests[0].error.message, /50ms/) }) +e.test('Eltro should support timed out tests in front', async function() { + testsWereRun = true + const t = CreateT() + t.begin() + + t.describe('', function() { + t.timeout(25).test('', function(cb) { setTimeout(cb, 50) }) + t.test('', function(cb) { setTimeout(cb, 50) }) + }) + + await t.run() + assert.strictEqual(t.failedTests.length, 1) + assert.ok(t.failedTests[0].error) + assert.match(t.failedTests[0].error.message, /25ms/) +}) + e.test('Eltro should support skipped tests', async function() { testsWereRun = true const t = CreateT() @@ -252,22 +268,6 @@ e.test('Eltro should support only tests', async function() { assert.strictEqual(assertIsTrue, true) }) -e.test('Eltro should support timed out tests in front', async function() { - testsWereRun = true - const t = CreateT() - t.begin() - - t.describe('', function() { - t.timeout(25).test('', function(cb) { setTimeout(cb, 50) }) - t.test('', function(cb) { setTimeout(cb, 50) }) - }) - - await t.run() - assert.strictEqual(t.failedTests.length, 1) - assert.ok(t.failedTests[0].error) - assert.match(t.failedTests[0].error.message, /25ms/) -}) - e.test('Eltro should support skipped tests in front of the test', async function() { testsWereRun = true let assertIsTrue = false @@ -283,6 +283,380 @@ e.test('Eltro should support skipped tests in front of the test', async function assert.strictEqual(assertIsTrue, true) }) +e.test('Eltro should support before() functions in describe group', async function() { + testsWereRun = true + let assertRan = 0 + let firstBefore = 0 + let secondBefore = 0 + let thirdBefore = 0 + const t = CreateT() + t.begin() + t.describe('', function() { + t.before(function() { + firstBefore = assertRan + }) + + t.describe('', function() { + t.before(function() { + secondBefore = assertRan + }) + + t.test('', function() { assertRan++ }) + t.test('', function() { assertRan++ }) + t.test('', function() { assertRan++ }) + }) + + t.describe('', function() { + t.before(function() { + thirdBefore = assertRan + }) + + t.test('', function() { assertRan++ }) + }) + + t.test('', function() { 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, 1) + assert.strictEqual(thirdBefore, 4) +}) + +e.test('Eltro should support before() functions in describe, timing out', async function() { + testsWereRun = true + let assertRan = 0 + const t = CreateT() + t.begin() + t.describe('', function() { + t.before(function(cb) { }).timeout(50) + t.test('', function() { assertRan++ }) + }) + await t.run() + assert.strictEqual(t.failedTests.length, 1) + assert.ok(t.failedTests[0].error) + assert.match(t.failedTests[0].error.message, /50ms/) + assert.strictEqual(assertRan, 0) +}) + +e.test('Eltro should support before() functions in describe, late timing out', async function() { + testsWereRun = true + let assertRan = 0 + const t = CreateT() + t.begin() + t.describe('', function() { + t.before(function(cb) { + setTimeout(cb, 100) + }).timeout(50) + t.test('', function() { assertRan++ }) + }) + t.describe('', function() { + t.test('', function(cb) { assertRan++; setTimeout(cb, 25) }) + t.test('', function(cb) { assertRan++; setTimeout(cb, 25) }) + }) + await t.run() + assert.strictEqual(t.failedTests.length, 1) + assert.ok(t.failedTests[0].error) + assert.match(t.failedTests[0].error.message, /50ms/) + assert.strictEqual(assertRan, 2) +}) + +e.test('Eltro should support before() functions in describe, timing out in front', async function() { + testsWereRun = true + let assertRan = 0 + const t = CreateT() + t.begin() + t.describe('', function() { + t.timeout(25).before(function(cb) { setTimeout(cb, 50) }) + t.test('', function() { assertRan++ }) + }) + t.describe('', function() { + t.test('', function(cb) { assertRan++; setTimeout(cb, 25) }) + t.test('', function(cb) { assertRan++; setTimeout(cb, 25) }) + }) + await t.run() + assert.strictEqual(t.failedTests.length, 1) + assert.ok(t.failedTests[0].error) + assert.match(t.failedTests[0].error.message, /25ms/) + assert.strictEqual(assertRan, 2) +}) + +e.test('Eltro should support before() functions in describe, being promised', async function() { + testsWereRun = true + let assertIsTrue = false + const t = CreateT() + t.begin() + t.describe('', function() { + t.before(function() { + return new Promise(function(res) { + assertIsTrue = true + res() + }) + }) + t.test('', function() { }) + }) + await t.run() + assert.strictEqual(t.failedTests.length, 0) + assert.strictEqual(assertIsTrue, true) +}) + +e.test('Eltro should support before() functions in describe, support callback', async function() { + testsWereRun = true + let assertIsTrue = false + const t = CreateT() + t.begin() + t.describe('', function() { + t.before(function(cb) { + setTimeout(function() { + assertIsTrue = true + cb() + }, 25) + }) + t.test('', function() { }) + }) + await t.run() + assert.strictEqual(t.failedTests.length, 0) + assert.strictEqual(assertIsTrue, true) +}) + +e.test('Eltro should support before() functions in describe, support directly thrown errors', async function() { + testsWereRun = true + const assertError = new Error() + const t = CreateT() + t.begin() + t.describe('', function() { + t.before(function() { + throw assertError + }) + t.test('', function() { }) + }) + await t.run() + assert.strictEqual(t.failedTests.length, 1) + assert.strictEqual(t.failedTests[0].error, assertError) +}) + +e.test('Eltro should support before() functions in describe, support rejected promises', async function() { + testsWereRun = true + const assertError = new Error() + const t = CreateT() + t.begin() + t.describe('', function() { + t.before(function() { + return new Promise(function(res, rej) { + rej(assertError) + }) + }) + t.test('', function() {}) + }) + await t.run() + assert.strictEqual(t.failedTests.length, 1) + assert.strictEqual(t.failedTests[0].error, assertError) +}) + +e.test('Eltro should support before() functions in describe, support callback rejected', async function() { + testsWereRun = true + const assertError = new Error() + const t = CreateT() + t.begin() + t.describe('', function() { + t.before(function(cb) { cb(assertError) }) + t.test('', function() { }) + }) + await t.run() + assert.strictEqual(t.failedTests.length, 1) + assert.strictEqual(t.failedTests[0].error, assertError) +}) + + +e.test('Eltro should support after() functions in describe group', async function() { + testsWereRun = true + let assertRan = 0 + let firstAfter = 0 + let secondAfter = 0 + let thirdAfter = 0 + const t = CreateT() + t.begin() + t.describe('', function() { + t.after(function() { + firstAfter = assertRan + }) + + t.describe('', function() { + t.after(function() { + secondAfter = assertRan + }) + + t.test('', function() { assertRan++ }) + t.test('', function() { assertRan++ }) + t.test('', function() { assertRan++ }) + }) + + t.describe('', function() { + t.after(function() { + thirdAfter = assertRan + }) + + t.test('', function() { assertRan++ }) + }) + + t.test('', function() { 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, 4) + assert.strictEqual(thirdAfter, 5) +}) + +e.test('Eltro should support after() functions in describe, timing out', async function() { + testsWereRun = true + let assertRan = 0 + const t = CreateT() + t.begin() + t.describe('', function() { + t.after(function(cb) { }).timeout(50) + t.test('', function() { assertRan++ }) + }) + await t.run() + assert.strictEqual(t.failedTests.length, 1) + assert.ok(t.failedTests[0].error) + assert.match(t.failedTests[0].error.message, /50ms/) + assert.strictEqual(assertRan, 1) +}) + +e.test('Eltro should support after() functions in describe, late timing out', async function() { + testsWereRun = true + let assertRan = 0 + const t = CreateT() + t.begin() + t.describe('', function() { + t.after(function(cb) { + setTimeout(cb, 100) + }).timeout(50) + t.test('', function() { assertRan++ }) + }) + t.describe('', function() { + t.test('', function(cb) { assertRan++; setTimeout(cb, 25) }) + t.test('', function(cb) { assertRan++; setTimeout(cb, 25) }) + }) + await t.run() + assert.strictEqual(t.failedTests.length, 1) + assert.ok(t.failedTests[0].error) + assert.match(t.failedTests[0].error.message, /50ms/) + assert.strictEqual(assertRan, 3) +}) + +e.test('Eltro should support after() functions in describe, timing out in front', async function() { + testsWereRun = true + let assertRan = 0 + const t = CreateT() + t.begin() + t.describe('', function() { + t.timeout(25).after(function(cb) { setTimeout(cb, 50) }) + t.test('', function() { assertRan++ }) + }) + t.describe('', function() { + t.test('', function(cb) { assertRan++; setTimeout(cb, 25) }) + t.test('', function(cb) { assertRan++; setTimeout(cb, 25) }) + }) + await t.run() + assert.strictEqual(t.failedTests.length, 1) + assert.ok(t.failedTests[0].error) + assert.match(t.failedTests[0].error.message, /25ms/) + assert.strictEqual(assertRan, 3) +}) + + +e.test('Eltro should support after() functions in describe, being promised', async function() { + testsWereRun = true + let assertIsTrue = false + const t = CreateT() + t.begin() + t.describe('', function() { + t.after(function() { + return new Promise(function(res) { + assertIsTrue = true + res() + }) + }) + t.test('', function() { }) + }) + await t.run() + assert.strictEqual(t.failedTests.length, 0) + assert.strictEqual(assertIsTrue, true) +}) + +e.test('Eltro should support after() functions in describe, support callback', async function() { + testsWereRun = true + let assertIsTrue = false + const t = CreateT() + t.begin() + t.describe('', function() { + t.after(function(cb) { + setTimeout(function() { + assertIsTrue = true + cb() + }, 25) + }) + t.test('', function() { }) + }) + await t.run() + assert.strictEqual(t.failedTests.length, 0) + assert.strictEqual(assertIsTrue, true) +}) + +e.test('Eltro should support after() functions in describe, support directly thrown errors', async function() { + testsWereRun = true + const assertError = new Error() + const t = CreateT() + t.begin() + t.describe('', function() { + t.after(function() { + throw assertError + }) + t.test('', function() { }) + }) + await t.run() + assert.strictEqual(t.failedTests.length, 1) + assert.strictEqual(t.failedTests[0].error, assertError) +}) + +e.test('Eltro should support after() functions in describe, support rejected promises', async function() { + testsWereRun = true + const assertError = new Error() + const t = CreateT() + t.begin() + t.describe('', function() { + t.after(function() { + return new Promise(function(res, rej) { + rej(assertError) + }) + }) + t.test('', function() {}) + }) + await t.run() + assert.strictEqual(t.failedTests.length, 1) + assert.strictEqual(t.failedTests[0].error, assertError) +}) + +e.test('Eltro should support after() functions in describe, support callback rejected', async function() { + testsWereRun = true + const assertError = new Error() + const t = CreateT() + t.begin() + t.describe('', function() { + t.after(function(cb) { cb(assertError) }) + t.test('', function() { }) + }) + await t.run() + assert.strictEqual(t.failedTests.length, 1) + assert.strictEqual(t.failedTests[0].error, assertError) +}) + e.test('Eltro should support only tests in front of the test', async function() { testsWereRun = true let assertIsTrue = false