Skip to content

Commit e9d71b4

Browse files
committed
test_runner: pass setup context to global teardown
Allow globalSetup() to return data consumed by globalTeardown(). Resolve the context-sharing TODO and document this behavior.
1 parent c2a0353 commit e9d71b4

File tree

5 files changed

+73
-8
lines changed

5 files changed

+73
-8
lines changed

doc/api/test.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,9 @@ This module can export any of the following:
510510
* A `globalSetup` function which runs once before all tests start
511511
* A `globalTeardown` function which runs once after all tests complete
512512

513+
If `globalSetup` returns a value, that value is passed as the first argument to
514+
`globalTeardown`.
515+
513516
The module is specified using the `--test-global-setup` flag when running tests from the command line.
514517

515518
```cjs
@@ -520,9 +523,10 @@ async function globalSetup() {
520523
// Run servers, create files, prepare databases, etc.
521524
}
522525

523-
async function globalTeardown() {
526+
async function globalTeardown(context) {
524527
// Clean up resources, state, or environment
525528
console.log('Global teardown executed');
529+
console.log(context);
526530
// Close servers, remove files, disconnect from databases, etc.
527531
}
528532

@@ -537,9 +541,10 @@ export async function globalSetup() {
537541
// Run servers, create files, prepare databases, etc.
538542
}
539543

540-
export async function globalTeardown() {
544+
export async function globalTeardown(context) {
541545
// Clean up resources, state, or environment
542546
console.log('Global teardown executed');
547+
console.log(context);
543548
// Close servers, remove files, disconnect from databases, etc.
544549
}
545550
```

lib/internal/test_runner/harness.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ function createTestTree(rootTestOptions, globalOptions) {
7171
counters: null,
7272
shouldColorizeTestFiles: shouldColorizeTestFiles(globalOptions.destinations),
7373
teardown: null,
74+
globalSetupContext: undefined,
7475
snapshotManager: null,
7576
previousRuns: null,
7677
isFilteringByName,
@@ -86,7 +87,7 @@ function createTestTree(rootTestOptions, globalOptions) {
8687
);
8788
harness.globalTeardownFunction = globalSetupFunctions.globalTeardownFunction;
8889
if (typeof globalSetupFunctions.globalSetupFunction === 'function') {
89-
return globalSetupFunctions.globalSetupFunction();
90+
harness.globalSetupContext = await globalSetupFunctions.globalSetupFunction();
9091
}
9192
return PromiseResolve();
9293
},
@@ -275,8 +276,9 @@ function setupProcessState(root, globalOptions) {
275276
kCancelledByParent));
276277

277278
if (root.harness.globalTeardownFunction) {
278-
await root.harness.globalTeardownFunction();
279+
await root.harness.globalTeardownFunction(root.harness.globalSetupContext);
279280
root.harness.globalTeardownFunction = null;
281+
root.harness.globalSetupContext = undefined;
280282
}
281283

282284
hook.disable();
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use strict';
2+
3+
const fs = require('node:fs');
4+
5+
const contextFlagPath = process.env.CONTEXT_FLAG_PATH;
6+
7+
async function globalSetup() {
8+
return {
9+
value: 'context from setup',
10+
};
11+
}
12+
13+
async function globalTeardown(context) {
14+
fs.writeFileSync(contextFlagPath, context?.value ?? 'missing context');
15+
}
16+
17+
module.exports = { globalSetup, globalTeardown };

test/parallel/test-runner-global-setup-teardown.mjs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,29 @@ async function runTest(
205205
assert.strictEqual(content, 'Teardown-only was executed');
206206
});
207207

208-
// TODO(pmarchini): We should be able to share context between setup and teardown
209-
it.todo('should share context between setup and teardown');
208+
it('should share context between setup and teardown', async () => {
209+
const contextFlagPath = tmpdir.resolve('context-shared.tmp');
210+
const setupFlagPath = tmpdir.resolve('setup-for-context.tmp');
211+
212+
// Create a setup file for test-file.js to find
213+
fs.writeFileSync(setupFlagPath, 'Setup was executed');
214+
215+
const { stdout } = await runTest({
216+
isolation,
217+
globalSetupFile: 'context-setup-teardown.js',
218+
env: {
219+
CONTEXT_FLAG_PATH: contextFlagPath,
220+
SETUP_FLAG_PATH: setupFlagPath,
221+
},
222+
runnerEnabled,
223+
});
224+
225+
assert.match(stdout, /pass 2/);
226+
assert.match(stdout, /fail 0/);
227+
assert.ok(fs.existsSync(contextFlagPath), 'Context flag file should exist');
228+
const content = fs.readFileSync(contextFlagPath, 'utf8');
229+
assert.strictEqual(content, 'context from setup');
230+
});
210231

211232
it('should handle async setup and teardown', async () => {
212233
const asyncFlagPath = tmpdir.resolve('async-executed.tmp');

test/parallel/test-runner-run-global-hooks.mjs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,28 @@ describe('require(\'node:test\').run with global hooks', { concurrency: false },
138138
assert.strictEqual(content, 'Teardown-only was executed');
139139
});
140140

141-
// TODO(pmarchini): We should be able to share context between setup and teardown
142-
it.todo('should share context between setup and teardown');
141+
it('should share context between setup and teardown', async () => {
142+
const contextFlagPath = tmpdir.resolve('context-shared.tmp');
143+
const setupFlagPath = tmpdir.resolve('setup-for-context.tmp');
144+
145+
// Create a setup file for test-file.js to find
146+
fs.writeFileSync(setupFlagPath, 'Setup was executed');
147+
148+
const { results } = await runTestWithGlobalHooks({
149+
globalSetupFile: 'context-setup-teardown.js',
150+
runnerEnv: {
151+
CONTEXT_FLAG_PATH: contextFlagPath,
152+
SETUP_FLAG_PATH: setupFlagPath,
153+
},
154+
isolation,
155+
});
156+
157+
assert.strictEqual(results.passed, 2);
158+
assert.strictEqual(results.failed, 0);
159+
assert.ok(fs.existsSync(contextFlagPath), 'Context flag file should exist');
160+
const content = fs.readFileSync(contextFlagPath, 'utf8');
161+
assert.strictEqual(content, 'context from setup');
162+
});
143163

144164
it('should handle async setup and teardown', async () => {
145165
const asyncFlagPath = tmpdir.resolve('async-executed.tmp');

0 commit comments

Comments
 (0)