Skip to content

Commit 0fbedac

Browse files
MoLowjuanarbol
authored andcommittedOct 11, 2022
test_runner: add before/after/each hooks
PR-URL: #43730 Fixes: #43403 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
1 parent 5d3b8ab commit 0fbedac

File tree

8 files changed

+680
-30
lines changed

8 files changed

+680
-30
lines changed
 

‎doc/api/test.md

+194
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,120 @@ same as [`it([name], { skip: true }[, fn])`][it options].
446446
Shorthand for marking a test as `TODO`,
447447
same as [`it([name], { todo: true }[, fn])`][it options].
448448

449+
### `before([, fn][, options])`
450+
451+
<!-- YAML
452+
added: REPLACEME
453+
-->
454+
455+
* `fn` {Function|AsyncFunction} The hook function.
456+
If the hook uses callbacks,
457+
the callback function is passed as the second argument. **Default:** A no-op
458+
function.
459+
* `options` {Object} Configuration options for the hook. The following
460+
properties are supported:
461+
* `signal` {AbortSignal} Allows aborting an in-progress hook
462+
* `timeout` {number} A number of milliseconds the hook will fail after.
463+
If unspecified, subtests inherit this value from their parent.
464+
**Default:** `Infinity`.
465+
466+
This function is used to create a hook running before running a suite.
467+
468+
```js
469+
describe('tests', async () => {
470+
before(() => console.log('about to run some test'));
471+
it('is a subtest', () => {
472+
assert.ok('some relevant assertion here');
473+
});
474+
});
475+
```
476+
477+
### `after([, fn][, options])`
478+
479+
<!-- YAML
480+
added: REPLACEME
481+
-->
482+
483+
* `fn` {Function|AsyncFunction} The hook function.
484+
If the hook uses callbacks,
485+
the callback function is passed as the second argument. **Default:** A no-op
486+
function.
487+
* `options` {Object} Configuration options for the hook. The following
488+
properties are supported:
489+
* `signal` {AbortSignal} Allows aborting an in-progress hook
490+
* `timeout` {number} A number of milliseconds the hook will fail after.
491+
If unspecified, subtests inherit this value from their parent.
492+
**Default:** `Infinity`.
493+
494+
This function is used to create a hook running after running a suite.
495+
496+
```js
497+
describe('tests', async () => {
498+
after(() => console.log('finished running tests'));
499+
it('is a subtest', () => {
500+
assert.ok('some relevant assertion here');
501+
});
502+
});
503+
```
504+
505+
### `beforeEach([, fn][, options])`
506+
507+
<!-- YAML
508+
added: REPLACEME
509+
-->
510+
511+
* `fn` {Function|AsyncFunction} The hook function.
512+
If the hook uses callbacks,
513+
the callback function is passed as the second argument. **Default:** A no-op
514+
function.
515+
* `options` {Object} Configuration options for the hook. The following
516+
properties are supported:
517+
* `signal` {AbortSignal} Allows aborting an in-progress hook
518+
* `timeout` {number} A number of milliseconds the hook will fail after.
519+
If unspecified, subtests inherit this value from their parent.
520+
**Default:** `Infinity`.
521+
522+
This function is used to create a hook running
523+
before each subtest of the current suite.
524+
525+
```js
526+
describe('tests', async () => {
527+
beforeEach(() => t.diagnostics('about to run a test'));
528+
it('is a subtest', () => {
529+
assert.ok('some relevant assertion here');
530+
});
531+
});
532+
```
533+
534+
### `afterEach([, fn][, options])`
535+
536+
<!-- YAML
537+
added: REPLACEME
538+
-->
539+
540+
* `fn` {Function|AsyncFunction} The hook function.
541+
If the hook uses callbacks,
542+
the callback function is passed as the second argument. **Default:** A no-op
543+
function.
544+
* `options` {Object} Configuration options for the hook. The following
545+
properties are supported:
546+
* `signal` {AbortSignal} Allows aborting an in-progress hook
547+
* `timeout` {number} A number of milliseconds the hook will fail after.
548+
If unspecified, subtests inherit this value from their parent.
549+
**Default:** `Infinity`.
550+
551+
This function is used to create a hook running
552+
after each subtest of the current test.
553+
554+
```js
555+
describe('tests', async () => {
556+
afterEach(() => t.diagnostics('about to run a test'));
557+
it('is a subtest', () => {
558+
assert.ok('some relevant assertion here');
559+
});
560+
});
561+
```
562+
449563
## Class: `TestContext`
450564

451565
<!-- YAML
@@ -456,6 +570,70 @@ An instance of `TestContext` is passed to each test function in order to
456570
interact with the test runner. However, the `TestContext` constructor is not
457571
exposed as part of the API.
458572

573+
### `context.beforeEach([, fn][, options])`
574+
575+
<!-- YAML
576+
added: REPLACEME
577+
-->
578+
579+
* `fn` {Function|AsyncFunction} The hook function. The first argument
580+
to this function is a [`TestContext`][] object. If the hook uses callbacks,
581+
the callback function is passed as the second argument. **Default:** A no-op
582+
function.
583+
* `options` {Object} Configuration options for the hook. The following
584+
properties are supported:
585+
* `signal` {AbortSignal} Allows aborting an in-progress hook
586+
* `timeout` {number} A number of milliseconds the hook will fail after.
587+
If unspecified, subtests inherit this value from their parent.
588+
**Default:** `Infinity`.
589+
590+
This function is used to create a hook running
591+
before each subtest of the current test.
592+
593+
```js
594+
test('top level test', async (t) => {
595+
t.beforeEach((t) => t.diagnostics(`about to run ${t.name}`));
596+
await t.test(
597+
'This is a subtest',
598+
(t) => {
599+
assert.ok('some relevant assertion here');
600+
}
601+
);
602+
});
603+
```
604+
605+
### `context.afterEach([, fn][, options])`
606+
607+
<!-- YAML
608+
added: REPLACEME
609+
-->
610+
611+
* `fn` {Function|AsyncFunction} The hook function. The first argument
612+
to this function is a [`TestContext`][] object. If the hook uses callbacks,
613+
the callback function is passed as the second argument. **Default:** A no-op
614+
function.
615+
* `options` {Object} Configuration options for the hook. The following
616+
properties are supported:
617+
* `signal` {AbortSignal} Allows aborting an in-progress hook
618+
* `timeout` {number} A number of milliseconds the hook will fail after.
619+
If unspecified, subtests inherit this value from their parent.
620+
**Default:** `Infinity`.
621+
622+
This function is used to create a hook running
623+
after each subtest of the current test.
624+
625+
```js
626+
test('top level test', async (t) => {
627+
t.afterEach((t) => t.diagnostics(`finished running ${t.name}`));
628+
await t.test(
629+
'This is a subtest',
630+
(t) => {
631+
assert.ok('some relevant assertion here');
632+
}
633+
);
634+
});
635+
```
636+
459637
### `context.diagnostic(message)`
460638

461639
<!-- YAML
@@ -474,6 +652,14 @@ test('top level test', (t) => {
474652
});
475653
```
476654

655+
### `context.name`
656+
657+
<!-- YAML
658+
added: REPLACEME
659+
-->
660+
661+
The name of the test
662+
477663
### `context.runOnly(shouldRunOnlyTests)`
478664

479665
<!-- YAML
@@ -617,6 +803,14 @@ An instance of `SuiteContext` is passed to each suite function in order to
617803
interact with the test runner. However, the `SuiteContext` constructor is not
618804
exposed as part of the API.
619805

806+
### `context.name`
807+
808+
<!-- YAML
809+
added: REPLACEME
810+
-->
811+
812+
The name of the suite
813+
620814
### `context.signal`
621815

622816
<!-- YAML

‎lib/internal/test_runner/harness.js

+11
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,19 @@ function runInParentContext(Factory) {
174174
return cb;
175175
}
176176

177+
function hook(hook) {
178+
return (fn, options) => {
179+
const parent = testResources.get(executionAsyncId()) || setup(root);
180+
parent.createHook(hook, fn, options);
181+
};
182+
}
183+
177184
module.exports = {
178185
test: FunctionPrototypeBind(test, root),
179186
describe: runInParentContext(Suite),
180187
it: runInParentContext(ItTest),
188+
before: hook('before'),
189+
after: hook('after'),
190+
beforeEach: hook('beforeEach'),
191+
afterEach: hook('afterEach'),
181192
};

‎lib/internal/test_runner/test.js

+121-26
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
'use strict';
22
const {
33
ArrayPrototypePush,
4+
ArrayPrototypeReduce,
45
ArrayPrototypeShift,
6+
ArrayPrototypeSlice,
57
ArrayPrototypeUnshift,
68
FunctionPrototype,
79
MathMax,
810
Number,
11+
ObjectSeal,
912
PromisePrototypeThen,
1013
PromiseResolve,
1114
ReflectApply,
@@ -22,12 +25,11 @@ const {
2225
ERR_INVALID_ARG_TYPE,
2326
ERR_TEST_FAILURE,
2427
},
25-
kIsNodeError,
2628
AbortError,
2729
} = require('internal/errors');
2830
const { getOptionValue } = require('internal/options');
2931
const { TapStream } = require('internal/test_runner/tap_stream');
30-
const { createDeferredCallback } = require('internal/test_runner/utils');
32+
const { createDeferredCallback, isTestFailureError } = require('internal/test_runner/utils');
3133
const {
3234
createDeferredPromise,
3335
kEmptyObject,
@@ -36,6 +38,7 @@ const { isPromise } = require('internal/util/types');
3638
const {
3739
validateAbortSignal,
3840
validateNumber,
41+
validateOneOf,
3942
validateUint32,
4043
} = require('internal/validators');
4144
const { setTimeout } = require('timers/promises');
@@ -48,6 +51,7 @@ const kParentAlreadyFinished = 'parentAlreadyFinished';
4851
const kSubtestsFailed = 'subtestsFailed';
4952
const kTestCodeFailure = 'testCodeFailure';
5053
const kTestTimeoutFailure = 'testTimeoutFailure';
54+
const kHookFailure = 'hookFailed';
5155
const kDefaultIndent = ' ';
5256
const kDefaultTimeout = null;
5357
const noop = FunctionPrototype;
@@ -56,6 +60,8 @@ const testOnlyFlag = !isTestRunner && getOptionValue('--test-only');
5660
// TODO(cjihrig): Use uv_available_parallelism() once it lands.
5761
const rootConcurrency = isTestRunner ? MathMax(cpus().length - 1, 1) : 1;
5862
const kShouldAbort = Symbol('kShouldAbort');
63+
const kRunHook = Symbol('kRunHook');
64+
const kHookNames = ObjectSeal(['before', 'after', 'beforeEach', 'afterEach']);
5965

6066

6167
function stopTest(timeout, signal) {
@@ -81,6 +87,10 @@ class TestContext {
8187
return this.#test.signal;
8288
}
8389

90+
get name() {
91+
return this.#test.name;
92+
}
93+
8494
diagnostic(message) {
8595
this.#test.diagnostic(message);
8696
}
@@ -103,6 +113,14 @@ class TestContext {
103113

104114
return subtest.start();
105115
}
116+
117+
beforeEach(fn, options) {
118+
this.#test.createHook('beforeEach', fn, options);
119+
}
120+
121+
afterEach(fn, options) {
122+
this.#test.createHook('afterEach', fn, options);
123+
}
106124
}
107125

108126
class Test extends AsyncResource {
@@ -207,6 +225,12 @@ class Test extends AsyncResource {
207225
this.pendingSubtests = [];
208226
this.readySubtests = new SafeMap();
209227
this.subtests = [];
228+
this.hooks = {
229+
before: [],
230+
after: [],
231+
beforeEach: [],
232+
afterEach: [],
233+
};
210234
this.waitingOn = 0;
211235
this.finished = false;
212236
}
@@ -325,10 +349,19 @@ class Test extends AsyncResource {
325349
kCancelledByParent
326350
)
327351
);
352+
this.startTime = this.startTime || this.endTime; // If a test was canceled before it was started, e.g inside a hook
328353
this.cancelled = true;
329354
this.#abortController.abort();
330355
}
331356

357+
createHook(name, fn, options) {
358+
validateOneOf(name, 'hook name', kHookNames);
359+
// eslint-disable-next-line no-use-before-define
360+
const hook = new TestHook(fn, options);
361+
ArrayPrototypePush(this.hooks[name], hook);
362+
return hook;
363+
}
364+
332365
fail(err) {
333366
if (this.error !== null) {
334367
return;
@@ -392,8 +425,27 @@ class Test extends AsyncResource {
392425
return { ctx, args: [ctx] };
393426
}
394427

428+
async [kRunHook](hook, args) {
429+
validateOneOf(hook, 'hook name', kHookNames);
430+
try {
431+
await ArrayPrototypeReduce(this.hooks[hook], async (prev, hook) => {
432+
await prev;
433+
await hook.run(args);
434+
if (hook.error) {
435+
throw hook.error;
436+
}
437+
}, PromiseResolve());
438+
} catch (err) {
439+
const error = new ERR_TEST_FAILURE(`failed running ${hook} hook`, kHookFailure);
440+
error.cause = isTestFailureError(err) ? err.cause : err;
441+
throw error;
442+
}
443+
}
444+
395445
async run() {
396-
this.parent.activeSubtests++;
446+
if (this.parent !== null) {
447+
this.parent.activeSubtests++;
448+
}
397449
this.startTime = hrtime();
398450

399451
if (this[kShouldAbort]()) {
@@ -402,16 +454,20 @@ class Test extends AsyncResource {
402454
}
403455

404456
try {
405-
const stopPromise = stopTest(this.timeout, this.signal);
406457
const { args, ctx } = this.getRunArgs();
407-
ArrayPrototypeUnshift(args, this.fn, ctx); // Note that if it's not OK to mutate args, we need to first clone it.
458+
if (this.parent?.hooks.beforeEach.length > 0) {
459+
await this.parent[kRunHook]('beforeEach', { args, ctx });
460+
}
461+
const stopPromise = stopTest(this.timeout, this.signal);
462+
const runArgs = ArrayPrototypeSlice(args);
463+
ArrayPrototypeUnshift(runArgs, this.fn, ctx);
408464

409-
if (this.fn.length === args.length - 1) {
465+
if (this.fn.length === runArgs.length - 1) {
410466
// This test is using legacy Node.js error first callbacks.
411467
const { promise, cb } = createDeferredCallback();
412468

413-
ArrayPrototypePush(args, cb);
414-
const ret = ReflectApply(this.runInAsyncScope, this, args);
469+
ArrayPrototypePush(runArgs, cb);
470+
const ret = ReflectApply(this.runInAsyncScope, this, runArgs);
415471

416472
if (isPromise(ret)) {
417473
this.fail(new ERR_TEST_FAILURE(
@@ -424,7 +480,7 @@ class Test extends AsyncResource {
424480
}
425481
} else {
426482
// This test is synchronous or using Promises.
427-
const promise = ReflectApply(this.runInAsyncScope, this, args);
483+
const promise = ReflectApply(this.runInAsyncScope, this, runArgs);
428484
await SafePromiseRace([PromiseResolve(promise), stopPromise]);
429485
}
430486

@@ -433,9 +489,13 @@ class Test extends AsyncResource {
433489
return;
434490
}
435491

492+
if (this.parent?.hooks.afterEach.length > 0) {
493+
await this.parent[kRunHook]('afterEach', { args, ctx });
494+
}
495+
436496
this.pass();
437497
} catch (err) {
438-
if (err?.code === 'ERR_TEST_FAILURE' && kIsNodeError in err) {
498+
if (isTestFailureError(err)) {
439499
if (err.failureType === kTestTimeoutFailure) {
440500
this.cancel(err);
441501
} else {
@@ -549,20 +609,38 @@ class Test extends AsyncResource {
549609
}
550610
}
551611

612+
class TestHook extends Test {
613+
#args;
614+
constructor(fn, options) {
615+
if (options === null || typeof options !== 'object') {
616+
options = kEmptyObject;
617+
}
618+
const { timeout, signal } = options;
619+
super({ __proto__: null, fn, timeout, signal });
620+
}
621+
run(args) {
622+
this.#args = args;
623+
return super.run();
624+
}
625+
getRunArgs() {
626+
return this.#args;
627+
}
628+
}
629+
552630
class ItTest extends Test {
553631
constructor(opt) { super(opt); } // eslint-disable-line no-useless-constructor
554632
getRunArgs() {
555-
return { ctx: { signal: this.signal }, args: [] };
633+
return { ctx: { signal: this.signal, name: this.name }, args: [] };
556634
}
557635
}
558636
class Suite extends Test {
559637
constructor(options) {
560638
super(options);
561639

562640
try {
563-
const context = { signal: this.signal };
641+
const { ctx, args } = this.getRunArgs();
564642
this.buildSuite = PromisePrototypeThen(
565-
PromiseResolve(this.runInAsyncScope(this.fn, context, [context])),
643+
PromiseResolve(this.runInAsyncScope(this.fn, ctx, args)),
566644
undefined,
567645
(err) => {
568646
this.fail(new ERR_TEST_FAILURE(err, kTestCodeFailure));
@@ -574,23 +652,40 @@ class Suite extends Test {
574652
this.buildPhaseFinished = true;
575653
}
576654

655+
getRunArgs() {
656+
return { ctx: { signal: this.signal, name: this.name }, args: [] };
657+
}
658+
577659
async run() {
578-
this.parent.activeSubtests++;
579-
await this.buildSuite;
580-
this.startTime = hrtime();
660+
try {
661+
this.parent.activeSubtests++;
662+
await this.buildSuite;
663+
this.startTime = hrtime();
581664

582-
if (this[kShouldAbort]()) {
583-
this.subtests = [];
584-
this.postRun();
585-
return;
586-
}
665+
if (this[kShouldAbort]()) {
666+
this.subtests = [];
667+
this.postRun();
668+
return;
669+
}
670+
671+
672+
const hookArgs = this.getRunArgs();
673+
await this[kRunHook]('before', hookArgs);
674+
const stopPromise = stopTest(this.timeout, this.signal);
675+
const subtests = this.skipped || this.error ? [] : this.subtests;
676+
const promise = SafePromiseAll(subtests, (subtests) => subtests.start());
587677

588-
const stopPromise = stopTest(this.timeout, this.signal);
589-
const subtests = this.skipped || this.error ? [] : this.subtests;
590-
const promise = SafePromiseAll(subtests, (subtests) => subtests.start());
678+
await SafePromiseRace([promise, stopPromise]);
679+
await this[kRunHook]('after', hookArgs);
680+
this.pass();
681+
} catch (err) {
682+
if (isTestFailureError(err)) {
683+
this.fail(err);
684+
} else {
685+
this.fail(new ERR_TEST_FAILURE(err, kTestCodeFailure));
686+
}
687+
}
591688

592-
await SafePromiseRace([promise, stopPromise]);
593-
this.pass();
594689
this.postRun();
595690
}
596691
}

‎lib/internal/test_runner/utils.js

+6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const {
66
codes: {
77
ERR_TEST_FAILURE,
88
},
9+
kIsNodeError,
910
} = require('internal/errors');
1011

1112
const kMultipleCallbackInvocations = 'multipleCallbackInvocations';
@@ -49,8 +50,13 @@ function createDeferredCallback() {
4950
return { promise, cb };
5051
}
5152

53+
function isTestFailureError(err) {
54+
return err?.code === 'ERR_TEST_FAILURE' && kIsNodeError in err;
55+
}
56+
5257
module.exports = {
5358
createDeferredCallback,
5459
doesPathMatchFilter,
5560
isSupportedFileType,
61+
isTestFailureError,
5662
};

‎lib/test.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
'use strict';
2-
const { test, describe, it } = require('internal/test_runner/harness');
2+
const { test, describe, it, before, after, beforeEach, afterEach } = require('internal/test_runner/harness');
33
const { emitExperimentalWarning } = require('internal/util');
44

55
emitExperimentalWarning('The test runner');
@@ -8,3 +8,7 @@ module.exports = test;
88
module.exports.test = test;
99
module.exports.describe = describe;
1010
module.exports.it = it;
11+
module.exports.before = before;
12+
module.exports.after = after;
13+
module.exports.beforeEach = beforeEach;
14+
module.exports.afterEach = afterEach;

‎test/message/test_runner_describe_it.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -214,15 +214,15 @@ it('callback fail', (done) => {
214214
});
215215

216216
it('sync t is this in test', function() {
217-
assert.deepStrictEqual(this, { signal: this.signal });
217+
assert.deepStrictEqual(this, { signal: this.signal, name: this.name });
218218
});
219219

220220
it('async t is this in test', async function() {
221-
assert.deepStrictEqual(this, { signal: this.signal });
221+
assert.deepStrictEqual(this, { signal: this.signal, name: this.name });
222222
});
223223

224224
it('callback t is this in test', function(done) {
225-
assert.deepStrictEqual(this, { signal: this.signal });
225+
assert.deepStrictEqual(this, { signal: this.signal, name: this.name });
226226
done();
227227
});
228228

‎test/message/test_runner_hooks.js

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// Flags: --no-warnings
2+
'use strict';
3+
require('../common');
4+
const assert = require('assert');
5+
const { test, describe, it, before, after, beforeEach, afterEach } = require('node:test');
6+
7+
describe('describe hooks', () => {
8+
const testArr = [];
9+
before(function() {
10+
testArr.push('before ' + this.name);
11+
});
12+
after(function() {
13+
testArr.push('after ' + this.name);
14+
assert.deepStrictEqual(testArr, [
15+
'before describe hooks',
16+
'beforeEach 1', '1', 'afterEach 1',
17+
'beforeEach 2', '2', 'afterEach 2',
18+
'before nested',
19+
'beforeEach nested 1', 'nested 1', 'afterEach nested 1',
20+
'beforeEach nested 2', 'nested 2', 'afterEach nested 2',
21+
'after nested',
22+
'after describe hooks',
23+
]);
24+
});
25+
beforeEach(function() {
26+
testArr.push('beforeEach ' + this.name);
27+
});
28+
afterEach(function() {
29+
testArr.push('afterEach ' + this.name);
30+
});
31+
32+
it('1', () => testArr.push('1'));
33+
it('2', () => testArr.push('2'));
34+
35+
describe('nested', () => {
36+
before(function() {
37+
testArr.push('before ' + this.name);
38+
});
39+
after(function() {
40+
testArr.push('after ' + this.name);
41+
});
42+
beforeEach(function() {
43+
testArr.push('beforeEach ' + this.name);
44+
});
45+
afterEach(function() {
46+
testArr.push('afterEach ' + this.name);
47+
});
48+
it('nested 1', () => testArr.push('nested 1'));
49+
it('nested 2', () => testArr.push('nested 2'));
50+
});
51+
});
52+
53+
describe('before throws', () => {
54+
before(() => { throw new Error('before'); });
55+
it('1', () => {});
56+
it('2', () => {});
57+
});
58+
59+
describe('after throws', () => {
60+
after(() => { throw new Error('after'); });
61+
it('1', () => {});
62+
it('2', () => {});
63+
});
64+
65+
describe('beforeEach throws', () => {
66+
beforeEach(() => { throw new Error('beforeEach'); });
67+
it('1', () => {});
68+
it('2', () => {});
69+
});
70+
71+
describe('afterEach throws', () => {
72+
afterEach(() => { throw new Error('afterEach'); });
73+
it('1', () => {});
74+
it('2', () => {});
75+
});
76+
77+
test('test hooks', async (t) => {
78+
const testArr = [];
79+
t.beforeEach((t) => testArr.push('beforeEach ' + t.name));
80+
t.afterEach((t) => testArr.push('afterEach ' + t.name));
81+
await t.test('1', () => testArr.push('1'));
82+
await t.test('2', () => testArr.push('2'));
83+
84+
await t.test('nested', async (t) => {
85+
t.beforeEach((t) => testArr.push('nested beforeEach ' + t.name));
86+
t.afterEach((t) => testArr.push('nested afterEach ' + t.name));
87+
await t.test('nested 1', () => testArr.push('nested1'));
88+
await t.test('nested 2', () => testArr.push('nested 2'));
89+
});
90+
91+
assert.deepStrictEqual(testArr, [
92+
'beforeEach 1', '1', 'afterEach 1',
93+
'beforeEach 2', '2', 'afterEach 2',
94+
'beforeEach nested',
95+
'nested beforeEach nested 1', 'nested1', 'nested afterEach nested 1',
96+
'nested beforeEach nested 2', 'nested 2', 'nested afterEach nested 2',
97+
'afterEach nested',
98+
]);
99+
});
100+
101+
test('t.beforeEach throws', async (t) => {
102+
t.beforeEach(() => { throw new Error('beforeEach'); });
103+
await t.test('1', () => {});
104+
await t.test('2', () => {});
105+
});
106+
107+
test('t.afterEach throws', async (t) => {
108+
t.afterEach(() => { throw new Error('afterEach'); });
109+
await t.test('1', () => {});
110+
await t.test('2', () => {});
111+
});

‎test/message/test_runner_hooks.out

+229
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
TAP version 13
2+
# Subtest: describe hooks
3+
# Subtest: 1
4+
ok 1 - 1
5+
---
6+
duration_ms: *
7+
...
8+
# Subtest: 2
9+
ok 2 - 2
10+
---
11+
duration_ms: *
12+
...
13+
# Subtest: nested
14+
# Subtest: nested 1
15+
ok 1 - nested 1
16+
---
17+
duration_ms: *
18+
...
19+
# Subtest: nested 2
20+
ok 2 - nested 2
21+
---
22+
duration_ms: *
23+
...
24+
1..2
25+
ok 3 - nested
26+
---
27+
duration_ms: *
28+
...
29+
1..3
30+
ok 1 - describe hooks
31+
---
32+
duration_ms: *
33+
...
34+
# Subtest: before throws
35+
# Subtest: 1
36+
not ok 1 - 1
37+
---
38+
duration_ms: *
39+
failureType: 'cancelledByParent'
40+
error: 'test did not finish before its parent and was cancelled'
41+
code: 'ERR_TEST_FAILURE'
42+
...
43+
# Subtest: 2
44+
not ok 2 - 2
45+
---
46+
duration_ms: *
47+
failureType: 'cancelledByParent'
48+
error: 'test did not finish before its parent and was cancelled'
49+
code: 'ERR_TEST_FAILURE'
50+
...
51+
1..2
52+
not ok 2 - before throws
53+
---
54+
duration_ms: *
55+
failureType: 'hookFailed'
56+
error: 'failed running before hook'
57+
code: 'ERR_TEST_FAILURE'
58+
...
59+
# Subtest: after throws
60+
# Subtest: 1
61+
ok 1 - 1
62+
---
63+
duration_ms: *
64+
...
65+
# Subtest: 2
66+
ok 2 - 2
67+
---
68+
duration_ms: *
69+
...
70+
1..2
71+
not ok 3 - after throws
72+
---
73+
duration_ms: *
74+
failureType: 'hookFailed'
75+
error: 'failed running after hook'
76+
code: 'ERR_TEST_FAILURE'
77+
...
78+
# Subtest: beforeEach throws
79+
# Subtest: 1
80+
not ok 1 - 1
81+
---
82+
duration_ms: *
83+
failureType: 'hookFailed'
84+
error: 'failed running beforeEach hook'
85+
code: 'ERR_TEST_FAILURE'
86+
stack: |-
87+
*
88+
...
89+
# Subtest: 2
90+
not ok 2 - 2
91+
---
92+
duration_ms: *
93+
failureType: 'hookFailed'
94+
error: 'failed running beforeEach hook'
95+
code: 'ERR_TEST_FAILURE'
96+
...
97+
1..2
98+
not ok 4 - beforeEach throws
99+
---
100+
duration_ms: *
101+
failureType: 'subtestsFailed'
102+
error: '2 subtests failed'
103+
code: 'ERR_TEST_FAILURE'
104+
...
105+
# Subtest: afterEach throws
106+
# Subtest: 1
107+
not ok 1 - 1
108+
---
109+
duration_ms: *
110+
failureType: 'hookFailed'
111+
error: 'failed running afterEach hook'
112+
code: 'ERR_TEST_FAILURE'
113+
stack: |-
114+
*
115+
...
116+
# Subtest: 2
117+
not ok 2 - 2
118+
---
119+
duration_ms: *
120+
failureType: 'hookFailed'
121+
error: 'failed running afterEach hook'
122+
code: 'ERR_TEST_FAILURE'
123+
...
124+
1..2
125+
not ok 5 - afterEach throws
126+
---
127+
duration_ms: *
128+
failureType: 'subtestsFailed'
129+
error: '2 subtests failed'
130+
code: 'ERR_TEST_FAILURE'
131+
...
132+
# Subtest: test hooks
133+
# Subtest: 1
134+
ok 1 - 1
135+
---
136+
duration_ms: *
137+
...
138+
# Subtest: 2
139+
ok 2 - 2
140+
---
141+
duration_ms: *
142+
...
143+
# Subtest: nested
144+
# Subtest: nested 1
145+
ok 1 - nested 1
146+
---
147+
duration_ms: *
148+
...
149+
# Subtest: nested 2
150+
ok 2 - nested 2
151+
---
152+
duration_ms: *
153+
...
154+
1..2
155+
ok 3 - nested
156+
---
157+
duration_ms: *
158+
...
159+
1..3
160+
ok 6 - test hooks
161+
---
162+
duration_ms: *
163+
...
164+
# Subtest: t.beforeEach throws
165+
# Subtest: 1
166+
not ok 1 - 1
167+
---
168+
duration_ms: *
169+
failureType: 'hookFailed'
170+
error: 'failed running beforeEach hook'
171+
code: 'ERR_TEST_FAILURE'
172+
stack: |-
173+
*
174+
...
175+
# Subtest: 2
176+
not ok 2 - 2
177+
---
178+
duration_ms: *
179+
failureType: 'hookFailed'
180+
error: 'failed running beforeEach hook'
181+
code: 'ERR_TEST_FAILURE'
182+
stack: |-
183+
*
184+
...
185+
1..2
186+
not ok 7 - t.beforeEach throws
187+
---
188+
duration_ms: *
189+
failureType: 'subtestsFailed'
190+
error: '2 subtests failed'
191+
code: 'ERR_TEST_FAILURE'
192+
...
193+
# Subtest: t.afterEach throws
194+
# Subtest: 1
195+
not ok 1 - 1
196+
---
197+
duration_ms: *
198+
failureType: 'hookFailed'
199+
error: 'failed running afterEach hook'
200+
code: 'ERR_TEST_FAILURE'
201+
stack: |-
202+
*
203+
...
204+
# Subtest: 2
205+
not ok 2 - 2
206+
---
207+
duration_ms: *
208+
failureType: 'hookFailed'
209+
error: 'failed running afterEach hook'
210+
code: 'ERR_TEST_FAILURE'
211+
stack: |-
212+
*
213+
...
214+
1..2
215+
not ok 8 - t.afterEach throws
216+
---
217+
duration_ms: *
218+
failureType: 'subtestsFailed'
219+
error: '2 subtests failed'
220+
code: 'ERR_TEST_FAILURE'
221+
...
222+
1..8
223+
# tests 8
224+
# pass 2
225+
# fail 6
226+
# cancelled 0
227+
# skipped 0
228+
# todo 0
229+
# duration_ms *

0 commit comments

Comments
 (0)
Please sign in to comment.