diff --git a/lib/test.js b/lib/test.js index fe1b3f0..f125358 100644 --- a/lib/test.js +++ b/lib/test.js @@ -63,6 +63,8 @@ const _emits = Symbol('_emits') const _nextChildId = Symbol('_nextChildId') const _expectUncaught = Symbol('_expectUncaught') const _createdFixture = Symbol('_createdFixture') +const _beforeCalled = Symbol('_beforeCalled') +const _printedResult = Symbol('_printedResult') const hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key) @@ -620,6 +622,8 @@ class Test extends Base { } printResult (ok, message, extra, front) { + this[_printedResult] = true + if (this.doingStdinOnly) throw new Error('cannot print results in stdinOnly mode') const n = this.count + 1 @@ -1435,6 +1439,33 @@ class Test extends Base { } } + before (fn) { + this.currentAssert = Test.prototype.before + if (this.occupied || this[_printedResult]) + throw new Error('t.before() called after starting tests') + + if (this[_beforeCalled]) + throw new Error('called t.before() more than once') + + this[_beforeCalled] = true + + // if it throws, we let it kill the test + const ret = fn.call(this) + + if (ret && typeof ret.then === 'function') + this.waitOn(ret, w => { + if (w.rejected) { + // sort of a mini bailout, just for this one test + // drop everything from the queue, quit right away + this.queue.length = 0 + this.threw(w.value) + this.planEnd = -1 + this.count = 1 + this.end() + } + }) + } + waitOn (promise, cb, expectReject) { const w = new Waiter(promise, w => { assert.equal(this.occupied, w) diff --git a/tap-snapshots/test-test.js-TAP.test.cjs b/tap-snapshots/test-test.js-TAP.test.cjs index 8591b77..6558121 100644 --- a/tap-snapshots/test-test.js-TAP.test.cjs +++ b/tap-snapshots/test-test.js-TAP.test.cjs @@ -164,6 +164,174 @@ Bail out! whoops ` +exports[`test/test.js TAP assertions and weird stuff before after assertion fails > output 1`] = ` +TAP version 13 +# Subtest: child + ok 1 - this is going to be trouble + not ok 2 - t.before() called after starting tests + --- + stack: | + {STACK} + tapCaught: testFunctionThrow + test: child + ... + + 1..2 + # failed 1 of 2 tests +not ok 1 - child # {time} + +1..1 +# failed 1 test + +` + +exports[`test/test.js TAP assertions and weird stuff before after async test fails > output 1`] = ` +TAP version 13 +# Subtest: child + # Subtest: sync child + 1..0 + ok 1 - sync child # {time} + + not ok 2 - t.before() called after starting tests + --- + stack: | + {STACK} + tapCaught: testFunctionThrow + test: child + ... + + 1..2 + # failed 1 of 2 tests +not ok 1 - child # {time} + +1..1 +# failed 1 test + +` + +exports[`test/test.js TAP assertions and weird stuff before after sync test fails > output 1`] = ` +TAP version 13 +# Subtest: child + # Subtest: sync child + 1..0 + ok 1 - sync child # {time} + + not ok 2 - t.before() called after starting tests + --- + stack: | + {STACK} + tapCaught: testFunctionThrow + test: child + ... + + 1..2 + # failed 1 of 2 tests +not ok 1 - child # {time} + +1..1 +# failed 1 test + +` + +exports[`test/test.js TAP assertions and weird stuff before async > output 1`] = ` +TAP version 13 +ok 1 - before was called +ok 2 - before not done yet +# Subtest: child + ok 1 - tests wait for t.before to finish + 1..1 +ok 3 - child # {time} + +1..3 + +` + +exports[`test/test.js TAP assertions and weird stuff before called more than once fails > output 1`] = ` +TAP version 13 +# Subtest: child + not ok 1 - called t.before() more than once + --- + stack: | + {STACK} + tapCaught: testFunctionThrow + test: child + ... + + 1..1 + # failed 1 test +not ok 1 - child # {time} + +1..1 +# failed 1 test + +` + +exports[`test/test.js TAP assertions and weird stuff before reject > output 1`] = ` +TAP version 13 +# Subtest: child + not ok 1 - poo + --- + at: + line: # + column: # + file: test/test.js + source: |2 + t.test('child', t => { + t.before(async () => {throw new Error('poo')}) + --^ + t.test('async child', t => t.end()) + t.fail('should not print this') + stack: | + {STACK} + test: child + ... + + 1..1 + # failed 1 test +not ok 1 - child # {time} + +1..1 +# failed 1 test + +` + +exports[`test/test.js TAP assertions and weird stuff before sync > output 1`] = ` +TAP version 13 +ok 1 - before was called +1..1 + +` + +exports[`test/test.js TAP assertions and weird stuff before throw > output 1`] = ` +TAP version 13 +# Subtest: child + not ok 1 - poo + --- + at: + line: # + column: # + file: test/test.js + source: |2 + t.test('child', t => { + t.before(() => {throw new Error('poo')}) + --^ + t.test('async child', t => t.end()) + t.fail('should not print this') + stack: | + {STACK} + tapCaught: testFunctionThrow + test: child + ... + + 1..1 + # failed 1 test +not ok 1 - child # {time} + +1..1 +# failed 1 test + +` + exports[`test/test.js TAP assertions and weird stuff beforeEach afterEach > output 1`] = ` TAP version 13 # Subtest: child diff --git a/test/test.js b/test/test.js index 3a2d24b..ce36f17 100644 --- a/test/test.js +++ b/test/test.js @@ -991,6 +991,77 @@ t.test('assertions and weird stuff', t => { ee.emit('pass') t.end() }, + + 'before sync': t => { + let x = false + t.before(() => x = true) + t.equal(x, true, 'before was called') + t.end() + }, + 'before async': t => { + let x = false + let y = false + t.before(async () => { + x = true + await Promise.resolve(true) + y = true + }) + t.equal(x, true, 'before was called') + t.equal(y, false, 'before not done yet') + t.test('child', t => { + t.equal(y, true, 'tests wait for t.before to finish') + t.end() + }) + t.end() + }, + 'before after sync test fails': t => { + t.test('child', t => { + t.test('sync child', t => t.end()) + t.before(() => {}) + t.fail('should not print this') + }) + t.end() + }, + 'before after async test fails': t => { + t.test('child', t => { + t.test('sync child', async t => {}) + t.before(() => {}) + t.fail('should not print this') + }) + t.end() + }, + 'before after assertion fails': t => { + t.test('child', t => { + t.pass('this is going to be trouble') + t.before(() => {}) + t.fail('should not print this') + }) + t.end() + }, + 'before called more than once fails': t => { + t.test('child', t => { + t.before(() => {}) + t.before(() => {}) + t.fail('should not print this') + }) + t.end() + }, + 'before throw': t => { + t.test('child', t => { + t.before(() => {throw new Error('poo')}) + t.test('async child', t => t.end()) + t.fail('should not print this') + }) + t.end() + }, + 'before reject': t => { + t.test('child', t => { + t.before(async () => {throw new Error('poo')}) + t.test('async child', t => t.end()) + t.fail('should not print this') + }) + t.end() + }, } const keys = Object.keys(cases)