Skip to content

Commit 5aaa29d

Browse files
committed
test_runner: add getTestContext()
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 5aaa29d

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)