diff --git a/lib/eltro.mjs b/lib/eltro.mjs index 277b6ed..f4f39ba 100644 --- a/lib/eltro.mjs +++ b/lib/eltro.mjs @@ -12,6 +12,8 @@ function Group(e, name) { this.isExclusive = false this.before = null this.after = null + this.beforeEach = null + this.afterEach = null } Group.prototype.timeout = function(time) { @@ -74,6 +76,15 @@ Test.prototype.only = function() { this.group.__hasonly(true) } +Test.prototype.clone = function(prefix = '') { + var t = new Test(this.e, this.group, prefix + this.name, this.func) + let properties = ['skipTest', 'isExclusive', 'customTimeout', 'isBasic', 'error'] + for (let key of properties) { + t[key] = this[key] + } + return t +} + function Eltro() { this.__timeout = 2000 this.hasExclusive = false @@ -85,6 +96,7 @@ function Eltro() { this.failedTests = [] this.hasTests = false this.starting = false + this.logger = null this.filename = '' this.prefix = '' this.temporary = { @@ -111,13 +123,15 @@ Eltro.prototype.begin = function() { this.fileGroupMap.clear() } -Eltro.prototype.__runTest = async function(stats, test, prefix = 'Test') { +Eltro.prototype.__runTest = async function(stats, test, prefix = 'Test', child = null) { if (this.reporter === 'list') { process.stdout.write(' \x1b[90m? ' + test.name + '\x1b[0m') } + let markRealTest = child || test + if (!test.skipTest) { - await new Promise((resolve, reject) => { + let err = await new Promise((resolve, reject) => { // Flag to check if we finished let finished = false let timeout = test.customTimeout || this.__timeout @@ -193,7 +207,8 @@ Eltro.prototype.__runTest = async function(stats, test, prefix = 'Test') { if (prefix === 'Test') { stats.passed++ } - }, function(err) { + return null + }, (err) => { let saveError = err if (!saveError) { saveError = new Error(prefix + ' promise rejected with empty message') @@ -205,32 +220,34 @@ Eltro.prototype.__runTest = async function(stats, test, prefix = 'Test') { } saveError.originalError = err } - test.error = saveError - stats.failed++ + return saveError } ) + + if (err) { + markRealTest.error = err + this.failedTests.push(markRealTest.clone(child ? prefix : '')) + + stats.failed++ + } } else { stats.skipped++ } - if (test.error) { - this.failedTests.push(test) - } - if (this.reporter === 'list') { process.stdout.clearLine(); process.stdout.cursorTo(0); - if (test.skipTest) { - 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') + 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 + '\x1b[0m\n') } else if (prefix === 'Test') { - process.stdout.write(' \x1b[31m' + this.failedTests.length + ') ' + test.name + '\x1b[0m\n') + process.stdout.write(' \x1b[31m' + this.failedTests.length + ') ' + markRealTest.name + '\x1b[0m\n') } } else if (this.reporter === 'dot') { - if (test.skipTest) { + if (markRealTest.skipTest) { process.stdout.write('\x1b[94m.\x1b[0m') - } else if (!test.error) { + } else if (!markRealTest.error) { process.stdout.write('\x1b[32m.\x1b[0m') } else if (prefix === 'Test') { process.stdout.write('\x1b[31m.\x1b[0m') @@ -250,12 +267,31 @@ Eltro.prototype.__runGroup = async function(g, stats) { } 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]) + + if (g.beforeEach) { + await this.__runTest(stats, g.beforeEach, 'Before each: ', g.tests[x]) + if (!g.tests[x].error) { + await this.__runTest(stats, g.tests[x]) + } + } else { + await this.__runTest(stats, g.tests[x]) + } + if (g.afterEach) { + await this.__runTest(stats, g.afterEach, 'After each: ', g.tests[x]) + } } } for (let x = 0; x < g.groups.length; x++) { - if (!g.groups[x].skipTest && g.hasExclusive === (g.groups[x].hasExclusive || g.groups[x].isExclusive)) - await this.__runGroup(g.groups[x], stats) + 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') @@ -263,7 +299,7 @@ Eltro.prototype.__runGroup = async function(g, stats) { } Eltro.prototype.run = async function() { - if (this.reporter) { + if (this.reporter && this.reporter !== 'test') { console.log('') console.log('') } @@ -283,7 +319,16 @@ Eltro.prototype.run = async function() { let end = process.hrtime(start) - if (this.reporter) { + 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) + } + } + } + } else if (this.reporter) { console.log('') console.log('') if (stats.passed) { @@ -321,37 +366,30 @@ 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 beforesandafters = [ + ['before', 'Before'], + ['after', 'After'], + ['beforeEach', 'Before each'], + ['afterEach', 'After each'], +] - 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 - } +beforesandafters.forEach(function(item) { + Eltro.prototype[item[0]] = function(func) { + if (!this.activeGroup) { + throw new Error('Tests outside groups are not allowed.') + } - 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 - } + let test = new Test(this, this.activeGroup, item[1] + ': ' + this.activeGroup.name, func) - this.activeGroup.after = test - return test -} + if (this.temporary.timeout || this.activeGroup.customTimeout) { + test.timeout(this.temporary.timeout || this.activeGroup.customTimeout) + this.temporary.timeout = 0 + } + + this.activeGroup[item[0]] = test + return test + } +}) Eltro.prototype.describe = function(name, func) { let before = this.activeGroup diff --git a/lib/sinon.mjs b/lib/sinon.mjs index c938ef2..616fac8 100644 --- a/lib/sinon.mjs +++ b/lib/sinon.mjs @@ -12,6 +12,8 @@ export function stub(returnFunc = null) { if (returnFunc && typeof(returnFunc) !== 'function') { throw new Error('stub() was called with non-function argument') } + let manualReturners = new Map() + let nextManual = null let returner = returnFunc ? returnFunc : null let calls = [] let func = function(...args) { @@ -22,6 +24,9 @@ export function stub(returnFunc = null) { func[indexMap[func.callCount]] = args } func.callCount++ + if (manualReturners.has(func.callCount -1)) { + return manualReturners.get(func.callCount -1)(...args) + } if (returner) { return returner(...args) } @@ -29,36 +34,63 @@ export function stub(returnFunc = null) { func.lastCall = null func.called = false func.callCount = 0 - func.onCall = function(i) { + + func.getCall = function(i) { return calls[i] } + func.getCallN = function(i) { + return calls[i - 1] + } + func.onCall = function(i) { + if (i !== null && typeof(i) !== 'number') { + throw new Error('onCall must be called with either null or number') + } + nextManual = i + return func + } + func.onCallN = function(i) { + return func.onCall(i - 1) + } + func.reset = function() { func.lastCall = null func.called = false func.callCount = 0 + manualReturners.clear() for (let i = 0; i < indexMap.length; i++) { func[indexMap[i]] = null } returner = returnFunc ? returnFunc : null calls.splice(0, calls.length) + return func } func.returns = function(data) { - returner = function() { return data } + func.returnWith(function() { return data }) + return func + } + func.throws = function(data) { + func.returnWith(function() { throw data }) + return func + } + func.resolves = function(data) { + func.returnWith(function() { return Promise.resolve(data) }) + return func + } + func.rejects = function(data) { + func.returnWith(function() { return Promise.reject(data) }) + return func } func.returnWith = function(returnFunc) { if (typeof(returnFunc) !== 'function') { throw new Error('stub() was called with non-function argument') } - returner = returnFunc - } - func.throws = function(data) { - returner = function() { throw data } - } - func.resolves = function(data) { - returner = function() { return Promise.resolve(data) } - } - func.rejects = function(data) { - returner = function() { return Promise.reject(data) } + if (nextManual !== null) { + manualReturners.set(nextManual, returnFunc) + nextManual = null + } else { + returner = returnFunc + } + return func } for (let i = 0; i < indexMap.length; i++) { func[indexMap[i]] = null diff --git a/package.json b/package.json index 7527a63..a863ef6 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Eltro is a tiny no-dependancy test framework for node", "main": "index.mjs", "scripts": { - "test": "node cli.mjs test/**/*.test.mjs -r dot" + "test": "node cli.mjs test/**/*.test.mjs" }, "repository": { "type": "git", diff --git a/test/eltro.flow.test.mjs b/test/eltro.flow.test.mjs new file mode 100644 index 0000000..e3b587d --- /dev/null +++ b/test/eltro.flow.test.mjs @@ -0,0 +1,583 @@ +import e from '../lib/eltro.mjs' +import { stub } from '../lib/sinon.mjs' +import assert from '../lib/assert.mjs' + +function CreateT() { + const t = new e.Eltro() + t.reporter = 'test' + t.logger = { + log: stub() + } + return t +} + +e.describe('#before()', function() { + e.test('should support functions in describe group', async function() { + 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.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.beforeEach(function() { + outside++ + }) + + t.describe('', function() { + t.before(function() { + assert.strictEqual(outside, 2) + }) + let inside = 0 + t.beforeEach(function() { + inside++ + }) + + t.test('', function() { assert.strictEqual(inside, 1) }) + t.test('', function() { assert.strictEqual(inside, 2) }) + t.test('', function() { assert.strictEqual(inside, 3) }) + }) + + t.describe('', function() { + t.before(function() { + assert.strictEqual(outside, 3) + }) + + let insideSecond = 0 + t.beforeEach(function() { + assert.strictEqual(insideSecond, 0) + insideSecond++ + }) + + t.test('', function() { assert.strictEqual(insideSecond, 1) }) + }) + + t.test('', function() { assert.strictEqual(outside, 1) }) + }) + 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 be able to keep track of every error that occurs', async function() { + let counter = 0 + const t = CreateT() + t.begin() + t.describe('BBBB', function() { + t.beforeEach(function() { + throw new Error(`Counter at ${++counter}`) + }) + + t.describe('CCCC', function() { + t.test('', function() { }) + }) + + t.describe('DDDD', function() { + t.test('', 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/) + }) +}) + +e.describe('#after()', function() { + e.test('should support functions in describe group', async function() { + 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.describe('#afterEach()', function() { + e.test('should support functions in describe group', async function() { + const t = CreateT() + t.begin() + t.describe('', function() { + let outside = 0 + t.afterEach(function() { + outside++ + }) + + t.describe('', function() { + t.before(function() { assert.strictEqual(outside, 1) }) + + let inside = 0 + t.afterEach(function() { + inside++ + }) + + 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.describe('', function() { + t.before(function() { assert.strictEqual(outside, 2) }) + + let inside = 0 + t.afterEach(function() { + inside++ + }) + + t.test('', function() { assert.strictEqual(inside, 0) }) + + t.after(function() { assert.strictEqual(inside, 1) }) + }) + + t.test('', function() { assert.strictEqual(outside, 0) }) + + t.after(function() { assert.strictEqual(outside, 3) }) + }) + 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 be able to keep track of every error that occurs', async function() { + let counter = 0 + const t = CreateT() + t.begin() + t.describe('YYYY', function() { + t.afterEach(function() { + throw new Error(`Counter at ${++counter}`) + }) + + t.describe('HHHH', function() { + t.test('', function() { }) + }) + + t.describe('JJJJ', function() { + t.test('', function() { }) + }) + + t.test('AAAA', function() { }) + t.test('BBBB', function() { }) + t.test('CCCC', 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/) + }) +}) + +let commonBeforeTests = ['before', 'beforeEach'] +commonBeforeTests.forEach(function(before) { + e.describe(`#${before}()`, function() { + e.test('should not be possible outside of groups', async function() { + const t = CreateT() + t.begin() + assert.throws(function() { + t[before](function() {}) + }, function(err) { + assert.match(err.message, /group/i) + return true + }) + }) + + e.test('should support functions in describe timing out', async function() { + 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('should support functions in describe late timing out', async function() { + 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('should support functions in describe timing out in front', async function() { + 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('should support functions in describe being promised', async function() { + 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('should support functions in describe with callback', async function() { + 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('should support functions in describe with directly thrown errors', async function() { + 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('should support functions in describe with rejected promises', async function() { + 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('should support functions in describe with callback rejected', async function() { + 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) + }) + }) +}) + +let commonAfterTests = ['after', 'afterEach'] +commonAfterTests.forEach(function(after) { + e.describe(`#${after}()`, function() { + e.test('should not be possible outside of groups', async function() { + const t = CreateT() + t.begin() + assert.throws(function() { + t[after](function() {}) + }, function(err) { + assert.match(err.message, /group/i) + return true + }) + }) + + e.test('should support functions in describe, timing out', async function() { + 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('should support functions in describe, late timing out', async function() { + 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('should support functions in describe, timing out in front', async function() { + 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('should support functions in describe, being promised', async function() { + 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('should support functions in describe, support callback', async function() { + 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('should support functions in describe, support directly thrown errors', async function() { + 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('should support functions in describe, support rejected promises', async function() { + 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('should support functions in describe, support callback rejected', async function() { + 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) + }) + }) +}) diff --git a/test/eltro.test.mjs b/test/eltro.test.mjs index b4f2ecd..7c6a838 100644 --- a/test/eltro.test.mjs +++ b/test/eltro.test.mjs @@ -283,380 +283,6 @@ 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 diff --git a/test/sinon.test.mjs b/test/sinon.test.mjs index b1e6ee4..ead55e2 100644 --- a/test/sinon.test.mjs +++ b/test/sinon.test.mjs @@ -86,10 +86,10 @@ import { spy, stub } from '../index.mjs' assert.strictEqual(spyer.lastCall[1], assertThirdArgs[1]) assert.strictEqual(spyer.callCount, 3) - assert.strictEqual(spyer.onCall(0)[0], assertFirstArgs) - assert.strictEqual(spyer.onCall(1)[0], assertSecondArgs) - assert.strictEqual(spyer.onCall(2)[0], assertThirdArgs[0]) - assert.strictEqual(spyer.onCall(2)[1], assertThirdArgs[1]) + assert.strictEqual(spyer.getCall(0)[0], assertFirstArgs) + assert.strictEqual(spyer.getCall(1)[0], assertSecondArgs) + assert.strictEqual(spyer.getCall(2)[0], assertThirdArgs[0]) + assert.strictEqual(spyer.getCall(2)[1], assertThirdArgs[1]) assert.strictEqual(spyer.firstCall[0], assertFirstArgs) assert.strictEqual(spyer.secondCall[0], assertSecondArgs) @@ -295,4 +295,59 @@ t.describe('#stub()', function() { s.reset() assert.strictEqual(s(), 'success') }) + + t.test('should support chaining', function() { + let s = stub() + + s.reset() + .returnWith(function() {}) + .resolves(null) + .rejects(null) + .throws(null) + .returns(null) + .reset() + }) + + t.test('onCall should throw if not a number', function() { + let s = stub() + assert.throws(function() { s.onCall(undefined) }) + assert.throws(function() { s.onCall([]) }) + assert.throws(function() { s.onCall({}) }) + assert.throws(function() { s.onCall('') }) + assert.throws(function() { s.onCall('asdf') }) + }) + + t.test('should support arbitrary call returns', function() { + let s = stub() + s.onCall(0).returns(1) + .onCall(1).returns(2) + + assert.strictEqual(s(), 1) + assert.strictEqual(s(), 2) + + s.reset() + assert.strictEqual(s(), undefined) + assert.strictEqual(s(), undefined) + }) + + t.test('should send propert arguments in defined calls', function() { + const assertInput = { a : 1 } + const assertReturns = { b: 3 } + let ourCallCount = 0 + let s = stub() + s.onCall(3).returnWith(function(input) { + ourCallCount++ + assert.strictEqual(input, assertInput) + return assertReturns + }) + assert.strictEqual(s(null), undefined) + assert.strictEqual(s(null), undefined) + assert.strictEqual(s(null), undefined) + assert.strictEqual(s(assertInput), assertReturns) + assert.ok(s.called) + assert.strictEqual(s.callCount, 4) + assert.strictEqual(ourCallCount, 1) + assert.strictEqual(s.getCall(3)[0], assertInput) + assert.strictEqual(s(null), undefined) + }) })