Skip to content

Commit a7d7def

Browse files
committed
test_runner: add getTestContext() public API
Exposes getTestContext() function to access test context information from within tests and async operations. Returns the TestContext object for the currently executing test or undefined if called outside a test.
1 parent d9645d7 commit a7d7def

File tree

5 files changed

+117
-2
lines changed

5 files changed

+117
-2
lines changed

doc/api/test.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3563,6 +3563,45 @@ Emitted when no more tests are queued for execution in watch mode.
35633563

35643564
Emitted when one or more tests are restarted due to a file change in watch mode.
35653565

3566+
## `getTestContext()`
3567+
3568+
<!-- YAML
3569+
added:
3570+
- v26.0.0
3571+
-->
3572+
3573+
* Returns: {TestContext|SuiteContext|undefined}
3574+
3575+
Returns the [`TestContext`][] or [`SuiteContext`][] object associated with the
3576+
currently executing test or suite, or `undefined` if called outside of a test or
3577+
suite. This function can be used to access context information from within the
3578+
test or suite function or any async operations within them.
3579+
3580+
```mjs
3581+
import { getTestContext } from 'node:test';
3582+
3583+
test('example test', async () => {
3584+
const ctx = getTestContext();
3585+
console.log(`Running test: ${ctx.name}`);
3586+
});
3587+
3588+
describe('example suite', () => {
3589+
const ctx = getTestContext();
3590+
console.log(`Running suite: ${ctx.name}`);
3591+
});
3592+
```
3593+
3594+
When called from a test, returns a [`TestContext`][].
3595+
When called from a suite, returns a [`SuiteContext`][].
3596+
3597+
If called from outside a test or suite (e.g., at the top level of a module or in
3598+
a setTimeout callback after execution has completed), this function returns
3599+
`undefined`.
3600+
3601+
When called from within a hook (before, beforeEach, after, afterEach), this
3602+
function returns the context of the test or suite that the hook is associated
3603+
with.
3604+
35663605
## Class: `TestContext`
35673606

35683607
<!-- YAML

lib/internal/test_runner/harness.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const {
2121
},
2222
} = require('internal/errors');
2323
const { exitCodes: { kGenericUserError } } = internalBinding('errors');
24-
const { kCancelledByParent, Test, Suite } = require('internal/test_runner/test');
24+
const { kCancelledByParent, Test, Suite, TestContext, SuiteContext } = require('internal/test_runner/test');
2525
const {
2626
parseCommandLine,
2727
reporterScope,
@@ -431,8 +431,27 @@ function hook(hook) {
431431
};
432432
}
433433

434+
function getTestContext() {
435+
const test = testResources.get(executionAsyncId());
436+
// Exclude the reporter sentinel
437+
if (test === undefined || test === reporterScope) {
438+
return undefined;
439+
}
440+
// For hooks (hookType is set), return the test/suite being hooked (the parent)
441+
const actualTest = test.hookType !== undefined ? test.parent : test;
442+
if (actualTest === undefined) {
443+
return undefined;
444+
}
445+
// Return SuiteContext for suites, TestContext for tests
446+
if (actualTest instanceof Suite) {
447+
return new SuiteContext(actualTest);
448+
}
449+
return new TestContext(actualTest);
450+
}
451+
434452
module.exports = {
435453
createTestTree,
454+
getTestContext,
436455
test: runInParentContext(Test),
437456
suite: runInParentContext(Suite),
438457
before: hook('before'),

lib/internal/test_runner/test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,4 +1656,6 @@ module.exports = {
16561656
kUnwrapErrors,
16571657
Suite,
16581658
Test,
1659+
TestContext,
1660+
SuiteContext,
16591661
};

lib/test.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const {
55
ObjectDefineProperty,
66
} = primordials;
77

8-
const { test, suite, before, after, beforeEach, afterEach } = require('internal/test_runner/harness');
8+
const { test, suite, before, after, beforeEach, afterEach, getTestContext } = require('internal/test_runner/harness');
99
const { run } = require('internal/test_runner/runner');
1010

1111
module.exports = test;
@@ -15,6 +15,7 @@ ObjectAssign(module.exports, {
1515
before,
1616
beforeEach,
1717
describe: suite,
18+
getTestContext,
1819
it: test,
1920
run,
2021
suite,
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
'use strict';
2+
require('../common');
3+
const assert = require('node:assert');
4+
const { test, getTestContext, describe, it } = require('node:test');
5+
6+
// Outside a test — must return undefined
7+
assert.strictEqual(getTestContext(), undefined);
8+
9+
test('getTestContext returns current context inside test', async () => {
10+
const ctx = getTestContext();
11+
assert.ok(ctx !== undefined);
12+
assert.strictEqual(ctx.name, 'getTestContext returns current context inside test');
13+
assert.strictEqual(typeof ctx.signal, 'object');
14+
assert.strictEqual(typeof ctx.fullName, 'string');
15+
});
16+
17+
test('getTestContext works in nested test', async (t) => {
18+
await t.test('child', async () => {
19+
const ctx = getTestContext();
20+
assert.ok(ctx !== undefined);
21+
assert.strictEqual(ctx.name, 'child');
22+
});
23+
});
24+
25+
describe('getTestContext works in describe/it', () => {
26+
it('has correct name', () => {
27+
const ctx = getTestContext();
28+
assert.ok(ctx !== undefined);
29+
assert.strictEqual(ctx.name, 'has correct name');
30+
});
31+
});
32+
33+
describe('getTestContext returns SuiteContext in suite', () => {
34+
it('suite context is available', () => {
35+
const ctx = getTestContext();
36+
assert.ok(ctx !== undefined);
37+
// Suite name appears as parent in nested test context
38+
assert.strictEqual(typeof ctx.signal, 'object');
39+
assert.strictEqual(typeof ctx.fullName, 'string');
40+
});
41+
});
42+
43+
test('getTestContext works in test body during async operations', async (t) => {
44+
const ctx = getTestContext();
45+
assert.ok(ctx !== undefined);
46+
assert.strictEqual(ctx.name, 'getTestContext works in test body during async operations');
47+
48+
// Also works in nested async context
49+
const ctxInSetImmediate = await new Promise((resolve) => {
50+
setImmediate(() => resolve(getTestContext()));
51+
});
52+
assert.ok(ctxInSetImmediate !== undefined);
53+
assert.strictEqual(ctxInSetImmediate.name, 'getTestContext works in test body during async operations');
54+
});

0 commit comments

Comments
 (0)