test_runner: fix hooks test context

Signed-off-by: Moshe Atlow <moshe@atlow.co.il>
PR-URL: https://github.com/nodejs/node/pull/63285
Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
Moshe Atlow authored and GitHub committed 2026-05-15 10:46:21 +00:00
1 parent fa1e1f4fd4
commit 57508024c3
4 files changed
+91 -17

No files matched your search

+2 -11
View File
@@ -21,7 +21,7 @@ const {
},
} = require('internal/errors');
const { exitCodes: { kGenericUserError } } = internalBinding('errors');
const { kCancelledByParent, Test, Suite, TestContext, SuiteContext } = require('internal/test_runner/test');
const { kCancelledByParent, Test, Suite } = require('internal/test_runner/test');
const {
parseCommandLine,
reporterScope,
@@ -439,16 +439,7 @@ function getTestContext() {
if (test === undefined || test === reporterScope) {
return undefined;
}
// For hooks (hookType is set), return the test/suite being hooked (the parent)
const actualTest = test.hookType !== undefined ? test.parent : test;
if (actualTest === undefined) {
return undefined;
}
// Return SuiteContext for suites, TestContext for tests
if (actualTest instanceof Suite) {
return new SuiteContext(actualTest);
}
return new TestContext(actualTest);
return test.getCtx();
}
module.exports = {
+18 -3
View File
@@ -1233,8 +1233,14 @@ class Test extends AsyncResource {
}
}
#ctx;
getCtx() {
this.#ctx ??= new TestContext(this);
return this.#ctx;
}
getRunArgs() {
const ctx = new TestContext(this);
const ctx = this.getCtx();
return { __proto__: null, ctx, args: [ctx] };
}
@@ -1703,6 +1709,11 @@ class TestHook extends Test {
this.#args = args;
return super.run();
}
getCtx() {
return this.parentTest.getCtx();
}
getRunArgs() {
return this.#args;
}
@@ -1794,6 +1805,12 @@ class Suite extends Test {
this.buildPhaseFinished = true;
}
#ctx;
getCtx() {
this.#ctx ??= new TestContext(this);
return this.#ctx;
}
getRunArgs() {
const ctx = new SuiteContext(this);
return { __proto__: null, ctx, args: [ctx] };
@@ -1866,6 +1883,4 @@ module.exports = {
kUnwrapErrors,
Suite,
Test,
TestContext,
SuiteContext,
};
+68 -2
View File
@@ -1,7 +1,16 @@
'use strict';
require('../common');
const common = require('../common');
const assert = require('node:assert');
const { test, getTestContext, describe, it } = require('node:test');
const {
test,
getTestContext,
describe,
it,
before,
after,
beforeEach,
afterEach,
} = require('node:test');
// Outside a test — must return undefined
assert.strictEqual(getTestContext(), undefined);
@@ -40,6 +49,63 @@ describe('getTestContext returns SuiteContext in suite', () => {
});
});
describe('getTestContext inside hooks', () => {
const suiteName = 'getTestContext inside hooks';
before(common.mustCall((t) => {
const ctx = getTestContext();
assert.ok(ctx !== undefined);
assert.strictEqual(ctx.name, suiteName);
assert.strictEqual(ctx.name, t.name);
}));
beforeEach(common.mustCall(() => {
const ctx = getTestContext();
assert.ok(ctx !== undefined);
assert.strictEqual(ctx.name, suiteName);
}));
afterEach(common.mustCall(() => {
const ctx = getTestContext();
assert.ok(ctx !== undefined);
assert.strictEqual(ctx.name, suiteName);
}));
after(common.mustCall((t) => {
const ctx = getTestContext();
assert.ok(ctx !== undefined);
assert.strictEqual(ctx.name, suiteName);
assert.strictEqual(ctx.name, t.name);
}));
it('runs inside the suite', () => {
const ctx = getTestContext();
assert.ok(ctx !== undefined);
assert.strictEqual(ctx.name, 'runs inside the suite');
});
});
test('getTestContext inside test-level hooks returns the parent test', async (t) => {
const parentName = t.name;
t.beforeEach(common.mustCall(() => {
const ctx = getTestContext();
assert.ok(ctx !== undefined);
assert.strictEqual(ctx.name, parentName);
}));
t.afterEach(common.mustCall(() => {
const ctx = getTestContext();
assert.ok(ctx !== undefined);
assert.strictEqual(ctx.name, parentName);
}));
await t.test('child', () => {
const ctx = getTestContext();
assert.ok(ctx !== undefined);
assert.strictEqual(ctx.name, 'child');
});
});
test('getTestContext works in test body during async operations', async (t) => {
const ctx = getTestContext();
assert.ok(ctx !== undefined);
+3 -1
View File
@@ -63,7 +63,9 @@ function isMustCallOrMustCallAtLeast(str) {
}
function isMustCallOrTest(str) {
return str === 'test' || str === 'it' || isMustCallOrMustCallAtLeast(str);
return str === 'test' || str === 'it' || str === 'describe' || str === 'suite' ||
str === 'before' || str === 'after' || str === 'beforeEach' || str === 'afterEach' ||
isMustCallOrMustCallAtLeast(str);
}
module.exports = {