Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e97eefa

Browse filesBrowse files
atlowChemiMoLow
authored andcommitted
test_runner: execute before hook on test
Execute the before hook for Test as well, and execute it on root before executing any tests. Fixes: #47518 PR-URL: #47586 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
1 parent 980bf05 commit e97eefa
Copy full SHA for e97eefa

File tree

Expand file treeCollapse file tree

4 files changed

+107
-16
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

4 files changed

+107
-16
lines changed
Open diff view settings
Collapse file

‎doc/api/test.md‎

Copy file name to clipboardExpand all lines: doc/api/test.md
+24Lines changed: 24 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -1429,12 +1429,36 @@ Emitted when a test starts.
14291429

14301430
<!-- YAML
14311431
added: v18.0.0
1432+
changes:
1433+
- version: REPLACEME
1434+
pr-url: https://github.com/nodejs/node/pull/47586
1435+
description: The `before` function was added to TestContext.
14321436
-->
14331437

14341438
An instance of `TestContext` is passed to each test function in order to
14351439
interact with the test runner. However, the `TestContext` constructor is not
14361440
exposed as part of the API.
14371441

1442+
### `context.before([fn][, options])`
1443+
1444+
<!-- YAML
1445+
added: REPLACEME
1446+
-->
1447+
1448+
* `fn` {Function|AsyncFunction} The hook function. The first argument
1449+
to this function is a [`TestContext`][] object. If the hook uses callbacks,
1450+
the callback function is passed as the second argument. **Default:** A no-op
1451+
function.
1452+
* `options` {Object} Configuration options for the hook. The following
1453+
properties are supported:
1454+
* `signal` {AbortSignal} Allows aborting an in-progress hook.
1455+
* `timeout` {number} A number of milliseconds the hook will fail after.
1456+
If unspecified, subtests inherit this value from their parent.
1457+
**Default:** `Infinity`.
1458+
1459+
This function is used to create a hook running before
1460+
subtest of the current test.
1461+
14381462
### `context.beforeEach([fn][, options])`
14391463

14401464
<!-- YAML
Collapse file

‎lib/internal/test_runner/test.js‎

Copy file name to clipboardExpand all lines: lib/internal/test_runner/test.js
+12-2Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ class TestContext {
127127
return subtest.start();
128128
}
129129

130+
before(fn, options) {
131+
this.#test.createHook('before', fn, options);
132+
}
133+
130134
after(fn, options) {
131135
this.#test.createHook('after', fn, options);
132136
}
@@ -414,6 +418,9 @@ class Test extends AsyncResource {
414418
validateOneOf(name, 'hook name', kHookNames);
415419
// eslint-disable-next-line no-use-before-define
416420
const hook = new TestHook(fn, options);
421+
if (name === 'before') {
422+
hook.run = runOnce(hook.run);
423+
}
417424
ArrayPrototypePush(this.hooks[name], hook);
418425
return hook;
419426
}
@@ -525,6 +532,9 @@ class Test extends AsyncResource {
525532
if (this.parent?.hooks.beforeEach.length > 0) {
526533
await this.parent.runHook('beforeEach', { args, ctx });
527534
}
535+
if (this.parent?.hooks.before.length > 0) {
536+
await this.parent.runHook('before', this.parent.getRunArgs());
537+
}
528538
const stopPromise = stopTest(this.timeout, this.signal);
529539
const runArgs = ArrayPrototypeSlice(args);
530540
ArrayPrototypeUnshift(runArgs, this.fn, ctx);
@@ -561,7 +571,7 @@ class Test extends AsyncResource {
561571
this.pass();
562572
} catch (err) {
563573
try { await after(); } catch { /* Ignore error. */ }
564-
try { await afterEach(); } catch { /* test is already failing, let's the error */ }
574+
try { await afterEach(); } catch { /* test is already failing, let's ignore the error */ }
565575
if (isTestFailureError(err)) {
566576
if (err.failureType === kTestTimeoutFailure) {
567577
this.#cancel(err);
@@ -793,7 +803,7 @@ class Suite extends Test {
793803

794804
this.pass();
795805
} catch (err) {
796-
try { await afterEach(); } catch { /* test is already failing, let's the error */ }
806+
try { await afterEach(); } catch { /* test is already failing, let's ignore the error */ }
797807
if (isTestFailureError(err)) {
798808
this.fail(err);
799809
} else {
Collapse file

‎test/fixtures/test-runner/output/hooks.js‎

Copy file name to clipboardExpand all lines: test/fixtures/test-runner/output/hooks.js
+13-1Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ const common = require('../../../common');
44
const assert = require('assert');
55
const { test, describe, it, before, after, beforeEach, afterEach } = require('node:test');
66

7+
before((t) => t.diagnostic('before 1 called'));
8+
79
describe('describe hooks', () => {
810
const testArr = [];
911
before(function() {
@@ -91,6 +93,7 @@ describe('afterEach throws and test fails', () => {
9193
test('test hooks', async (t) => {
9294
const testArr = [];
9395

96+
t.before((t) => testArr.push('before ' + t.name));
9497
t.after(common.mustCall((t) => testArr.push('after ' + t.name)));
9598
t.beforeEach((t) => testArr.push('beforeEach ' + t.name));
9699
t.afterEach((t) => testArr.push('afterEach ' + t.name));
@@ -105,7 +108,7 @@ test('test hooks', async (t) => {
105108
});
106109

107110
assert.deepStrictEqual(testArr, [
108-
'beforeEach 1', '1', 'afterEach 1',
111+
'beforeEach 1', 'before test hooks', '1', 'afterEach 1',
109112
'beforeEach 2', '2', 'afterEach 2',
110113
'beforeEach nested',
111114
'nested beforeEach nested 1', 'nested1', 'nested afterEach nested 1',
@@ -114,6 +117,13 @@ test('test hooks', async (t) => {
114117
]);
115118
});
116119

120+
test('t.before throws', async (t) => {
121+
t.after(common.mustCall());
122+
t.before(() => { throw new Error('before'); });
123+
await t.test('1', () => {});
124+
await t.test('2', () => {});
125+
});
126+
117127
test('t.beforeEach throws', async (t) => {
118128
t.after(common.mustCall());
119129
t.beforeEach(() => { throw new Error('beforeEach'); });
@@ -149,3 +159,5 @@ test('t.after() is called if test body throws', (t) => {
149159
});
150160
throw new Error('bye');
151161
});
162+
163+
before((t) => t.diagnostic('before 2 called'));
Collapse file

‎test/fixtures/test-runner/output/hooks.snapshot‎

Copy file name to clipboardExpand all lines: test/fixtures/test-runner/output/hooks.snapshot
+58-13Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ not ok 2 - before throws
6767
*
6868
*
6969
*
70+
*
7071
...
7172
# Subtest: after throws
7273
# Subtest: 1
@@ -307,6 +308,53 @@ ok 8 - test hooks
307308
---
308309
duration_ms: *
309310
...
311+
# Subtest: t.before throws
312+
# Subtest: 1
313+
not ok 1 - 1
314+
---
315+
duration_ms: *
316+
failureType: 'hookFailed'
317+
error: 'failed running before hook'
318+
code: 'ERR_TEST_FAILURE'
319+
stack: |-
320+
*
321+
*
322+
*
323+
*
324+
*
325+
*
326+
*
327+
*
328+
*
329+
*
330+
...
331+
# Subtest: 2
332+
not ok 2 - 2
333+
---
334+
duration_ms: *
335+
failureType: 'hookFailed'
336+
error: 'failed running before hook'
337+
code: 'ERR_TEST_FAILURE'
338+
stack: |-
339+
*
340+
*
341+
*
342+
*
343+
*
344+
*
345+
*
346+
*
347+
*
348+
*
349+
...
350+
1..2
351+
not ok 9 - t.before throws
352+
---
353+
duration_ms: *
354+
failureType: 'subtestsFailed'
355+
error: '2 subtests failed'
356+
code: 'ERR_TEST_FAILURE'
357+
...
310358
# Subtest: t.beforeEach throws
311359
# Subtest: 1
312360
not ok 1 - 1
@@ -347,7 +395,7 @@ ok 8 - test hooks
347395
*
348396
...
349397
1..2
350-
not ok 9 - t.beforeEach throws
398+
not ok 10 - t.beforeEach throws
351399
---
352400
duration_ms: *
353401
failureType: 'subtestsFailed'
@@ -394,7 +442,7 @@ not ok 9 - t.beforeEach throws
394442
*
395443
...
396444
1..2
397-
not ok 10 - t.afterEach throws
445+
not ok 11 - t.afterEach throws
398446
---
399447
duration_ms: *
400448
failureType: 'subtestsFailed'
@@ -419,15 +467,14 @@ not ok 10 - t.afterEach throws
419467
*
420468
*
421469
*
422-
*
423470
...
424471
# Subtest: 2
425472
ok 2 - 2
426473
---
427474
duration_ms: *
428475
...
429476
1..2
430-
not ok 11 - afterEach when test fails
477+
not ok 12 - afterEach when test fails
431478
---
432479
duration_ms: *
433480
failureType: 'subtestsFailed'
@@ -452,7 +499,6 @@ not ok 11 - afterEach when test fails
452499
*
453500
*
454501
*
455-
*
456502
...
457503
# Subtest: 2
458504
not ok 2 - 2
@@ -474,15 +520,15 @@ not ok 11 - afterEach when test fails
474520
*
475521
...
476522
1..2
477-
not ok 12 - afterEach throws and test fails
523+
not ok 13 - afterEach throws and test fails
478524
---
479525
duration_ms: *
480526
failureType: 'subtestsFailed'
481527
error: '2 subtests failed'
482528
code: 'ERR_TEST_FAILURE'
483529
...
484530
# Subtest: t.after() is called if test body throws
485-
not ok 13 - t.after() is called if test body throws
531+
not ok 14 - t.after() is called if test body throws
486532
---
487533
duration_ms: *
488534
failureType: 'testCodeFailure'
@@ -493,16 +539,15 @@ not ok 13 - t.after() is called if test body throws
493539
*
494540
*
495541
*
496-
*
497-
*
498-
*
499542
...
500543
# - after() called
501-
1..13
502-
# tests 35
544+
1..14
545+
# before 1 called
546+
# before 2 called
547+
# tests 38
503548
# suites 8
504549
# pass 14
505-
# fail 19
550+
# fail 22
506551
# cancelled 2
507552
# skipped 0
508553
# todo 0

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.