Skip to content

Commit e8c3db1

Browse files
MoLowaduh95
authored andcommitted
test_runner: add getTestContext()
PR-URL: #62501 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
1 parent 54e2167 commit e8c3db1

6 files changed

Lines changed: 118 additions & 2 deletions

File tree

‎doc/api/test.md‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3675,6 +3675,44 @@ Emitted when no more tests are queued for execution in watch mode.
36753675

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

3678+
## `getTestContext()`
3679+
3680+
<!-- YAML
3681+
added: REPLACEME
3682+
-->
3683+
3684+
* Returns: {TestContext|SuiteContext|undefined}
3685+
3686+
Returns the [`TestContext`][] or [`SuiteContext`][] object associated with the
3687+
currently executing test or suite, or `undefined` if called outside of a test or
3688+
suite. This function can be used to access context information from within the
3689+
test or suite function or any async operations within them.
3690+
3691+
```mjs
3692+
import { getTestContext } from'node:test';
3693+
3694+
test('example test', async () => {
3695+
constctx=getTestContext();
3696+
console.log(`Running test: ${ctx.name}`);
3697+
});
3698+
3699+
describe('example suite', () => {
3700+
constctx=getTestContext();
3701+
console.log(`Running suite: ${ctx.name}`);
3702+
});
3703+
```
3704+
3705+
When called from a test, returns a [`TestContext`][].
3706+
When called from a suite, returns a [`SuiteContext`][].
3707+
3708+
If called from outside a test or suite (e.g., at the top level of a module or in
3709+
a setTimeout callback after execution has completed), this function returns
3710+
`undefined`.
3711+
3712+
When called from within a hook (before, beforeEach, after, afterEach), this
3713+
function returns the context of the test or suite that the hook is associated
3714+
with.
3715+
36783716
## Test instrumentation and OpenTelemetry
36793717

36803718
<!-- 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+
functiongetTestContext(){
435+
consttest=testResources.get(executionAsyncId());
436+
// Exclude the reporter sentinel
437+
if(test===undefined||test===reporterScope){
438+
returnundefined;
439+
}
440+
// For hooks (hookType is set), return the test/suite being hooked (the parent)
441+
constactualTest=test.hookType!==undefined ? test.parent : test;
442+
if(actualTest===undefined){
443+
returnundefined;
444+
}
445+
// Return SuiteContext for suites, TestContext for tests
446+
if(actualTestinstanceofSuite){
447+
returnnewSuiteContext(actualTest);
448+
}
449+
returnnewTestContext(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
@@ -1831,4 +1831,6 @@ module.exports = {
18311831
kUnwrapErrors,
18321832
Suite,
18331833
Test,
1834+
TestContext,
1835+
SuiteContext,
18341836
};

‎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+
constassert=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+
constctx=getTestContext();
11+
assert.ok(ctx!==undefined);
12+
assert.strictEqual(ctx.name,'getTestContext returns current context inside test');
13+
assert.strictEqual(typeofctx.signal,'object');
14+
assert.strictEqual(typeofctx.fullName,'string');
15+
});
16+
17+
test('getTestContext works in nested test',async(t)=>{
18+
awaitt.test('child',async()=>{
19+
constctx=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+
constctx=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+
constctx=getTestContext();
36+
assert.ok(ctx!==undefined);
37+
// Suite name appears as parent in nested test context
38+
assert.strictEqual(typeofctx.signal,'object');
39+
assert.strictEqual(typeofctx.fullName,'string');
40+
});
41+
});
42+
43+
test('getTestContext works in test body during async operations',async(t)=>{
44+
constctx=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+
constctxInSetImmediate=awaitnewPromise((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+
});

‎tools/doc/type-parser.mjs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@ const customTypesMap = {
252252
'Timer': 'timers.html#timers',
253253

254254
'TestsStream': 'test.html#class-testsstream',
255+
'TestContext': 'test.html#class-testcontext',
256+
'SuiteContext': 'test.html#class-suitecontext',
255257

256258
'tls.SecureContext': 'tls.html#tlscreatesecurecontextoptions',
257259
'tls.Server': 'tls.html#class-tlsserver',

0 commit comments

Comments
 (0)