From 0fbedac6ce3f266e9854d0b2160fb780aa4190de Mon Sep 17 00:00:00 2001 From: Moshe Atlow Date: Thu, 30 Jun 2022 10:14:35 +0300 Subject: [PATCH] test_runner: add before/after/each hooks PR-URL: https://github.com/nodejs/node/pull/43730 Fixes: https://github.com/nodejs/node/issues/43403 Reviewed-By: Benjamin Gruenbaum --- doc/api/test.md | 194 ++++++++++++++++++++ lib/internal/test_runner/harness.js | 11 ++ lib/internal/test_runner/test.js | 147 ++++++++++++--- lib/internal/test_runner/utils.js | 6 + lib/test.js | 6 +- test/message/test_runner_describe_it.js | 6 +- test/message/test_runner_hooks.js | 111 ++++++++++++ test/message/test_runner_hooks.out | 229 ++++++++++++++++++++++++ 8 files changed, 680 insertions(+), 30 deletions(-) create mode 100644 test/message/test_runner_hooks.js create mode 100644 test/message/test_runner_hooks.out diff --git a/doc/api/test.md b/doc/api/test.md index 6465a4e070d600..90a46ee773e0f9 100644 --- a/doc/api/test.md +++ b/doc/api/test.md @@ -446,6 +446,120 @@ same as [`it([name], { skip: true }[, fn])`][it options]. Shorthand for marking a test as `TODO`, same as [`it([name], { todo: true }[, fn])`][it options]. +### `before([, fn][, options])` + + + +* `fn` {Function|AsyncFunction} The hook function. + If the hook uses callbacks, + the callback function is passed as the second argument. **Default:** A no-op + function. +* `options` {Object} Configuration options for the hook. The following + properties are supported: + * `signal` {AbortSignal} Allows aborting an in-progress hook + * `timeout` {number} A number of milliseconds the hook will fail after. + If unspecified, subtests inherit this value from their parent. + **Default:** `Infinity`. + +This function is used to create a hook running before running a suite. + +```js +describe('tests', async () => { + before(() => console.log('about to run some test')); + it('is a subtest', () => { + assert.ok('some relevant assertion here'); + }); +}); +``` + +### `after([, fn][, options])` + + + +* `fn` {Function|AsyncFunction} The hook function. + If the hook uses callbacks, + the callback function is passed as the second argument. **Default:** A no-op + function. +* `options` {Object} Configuration options for the hook. The following + properties are supported: + * `signal` {AbortSignal} Allows aborting an in-progress hook + * `timeout` {number} A number of milliseconds the hook will fail after. + If unspecified, subtests inherit this value from their parent. + **Default:** `Infinity`. + +This function is used to create a hook running after running a suite. + +```js +describe('tests', async () => { + after(() => console.log('finished running tests')); + it('is a subtest', () => { + assert.ok('some relevant assertion here'); + }); +}); +``` + +### `beforeEach([, fn][, options])` + + + +* `fn` {Function|AsyncFunction} The hook function. + If the hook uses callbacks, + the callback function is passed as the second argument. **Default:** A no-op + function. +* `options` {Object} Configuration options for the hook. The following + properties are supported: + * `signal` {AbortSignal} Allows aborting an in-progress hook + * `timeout` {number} A number of milliseconds the hook will fail after. + If unspecified, subtests inherit this value from their parent. + **Default:** `Infinity`. + +This function is used to create a hook running +before each subtest of the current suite. + +```js +describe('tests', async () => { + beforeEach(() => t.diagnostics('about to run a test')); + it('is a subtest', () => { + assert.ok('some relevant assertion here'); + }); +}); +``` + +### `afterEach([, fn][, options])` + + + +* `fn` {Function|AsyncFunction} The hook function. + If the hook uses callbacks, + the callback function is passed as the second argument. **Default:** A no-op + function. +* `options` {Object} Configuration options for the hook. The following + properties are supported: + * `signal` {AbortSignal} Allows aborting an in-progress hook + * `timeout` {number} A number of milliseconds the hook will fail after. + If unspecified, subtests inherit this value from their parent. + **Default:** `Infinity`. + +This function is used to create a hook running +after each subtest of the current test. + +```js +describe('tests', async () => { + afterEach(() => t.diagnostics('about to run a test')); + it('is a subtest', () => { + assert.ok('some relevant assertion here'); + }); +}); +``` + ## Class: `TestContext` + +* `fn` {Function|AsyncFunction} The hook function. The first argument + to this function is a [`TestContext`][] object. If the hook uses callbacks, + the callback function is passed as the second argument. **Default:** A no-op + function. +* `options` {Object} Configuration options for the hook. The following + properties are supported: + * `signal` {AbortSignal} Allows aborting an in-progress hook + * `timeout` {number} A number of milliseconds the hook will fail after. + If unspecified, subtests inherit this value from their parent. + **Default:** `Infinity`. + +This function is used to create a hook running +before each subtest of the current test. + +```js +test('top level test', async (t) => { + t.beforeEach((t) => t.diagnostics(`about to run ${t.name}`)); + await t.test( + 'This is a subtest', + (t) => { + assert.ok('some relevant assertion here'); + } + ); +}); +``` + +### `context.afterEach([, fn][, options])` + + + +* `fn` {Function|AsyncFunction} The hook function. The first argument + to this function is a [`TestContext`][] object. If the hook uses callbacks, + the callback function is passed as the second argument. **Default:** A no-op + function. +* `options` {Object} Configuration options for the hook. The following + properties are supported: + * `signal` {AbortSignal} Allows aborting an in-progress hook + * `timeout` {number} A number of milliseconds the hook will fail after. + If unspecified, subtests inherit this value from their parent. + **Default:** `Infinity`. + +This function is used to create a hook running +after each subtest of the current test. + +```js +test('top level test', async (t) => { + t.afterEach((t) => t.diagnostics(`finished running ${t.name}`)); + await t.test( + 'This is a subtest', + (t) => { + assert.ok('some relevant assertion here'); + } + ); +}); +``` + ### `context.diagnostic(message)` + +The name of the test + ### `context.runOnly(shouldRunOnlyTests)` + +The name of the suite + ### `context.signal`