test_runner: add context.filePath

This commit adds a filePath getter to the TestContext and
SuiteContext classes. This allows a context to be mapped back to
the original test file that created it, even if it was imported
from another file. This is useful for mapping features like test
snapshots to the correct test file. This is also prep work for
supporting running test files in the test runner process.

PR-URL: https://github.com/nodejs/node/pull/53853
Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
This commit is contained in:
cjihrig 2024-07-14 23:37:10 -04:00 committed by Node.js GitHub Bot
parent 0b1ff6965e
commit 2ce7c69fce
4 changed files with 84 additions and 2 deletions

View File

@ -3204,6 +3204,16 @@ test('top level test', (t) => {
}); });
``` ```
### `context.filePath`
<!-- YAML
added: REPLACEME
-->
The absolute path of the test file that created the current test. If a test file
imports additional modules that generate tests, the imported tests will return
the path of the root test file.
### `context.fullName` ### `context.fullName`
<!-- YAML <!-- YAML
@ -3438,6 +3448,16 @@ An instance of `SuiteContext` is passed to each suite function in order to
interact with the test runner. However, the `SuiteContext` constructor is not interact with the test runner. However, the `SuiteContext` constructor is not
exposed as part of the API. exposed as part of the API.
### `context.filePath`
<!-- YAML
added: REPLACEME
-->
The absolute path of the test file that created the current suite. If a test
file imports additional modules that generate suites, the imported suites will
return the path of the root test file.
### `context.name` ### `context.name`
<!-- YAML <!-- YAML

View File

@ -221,7 +221,7 @@ let globalRoot;
let reportersSetup; let reportersSetup;
function getGlobalRoot() { function getGlobalRoot() {
if (!globalRoot) { if (!globalRoot) {
globalRoot = createTestTree(); globalRoot = createTestTree({ __proto__: null, entryFile: process.argv?.[1] });
globalRoot.reporter.on('test:fail', (data) => { globalRoot.reporter.on('test:fail', (data) => {
if (data.todo === undefined || data.todo === false) { if (data.todo === undefined || data.todo === false) {
process.exitCode = kGenericUserError; process.exitCode = kGenericUserError;

View File

@ -225,6 +225,10 @@ class TestContext {
return this.#test.name; return this.#test.name;
} }
get filePath() {
return this.#test.entryFile;
}
get fullName() { get fullName() {
return getFullName(this.#test); return getFullName(this.#test);
} }
@ -343,6 +347,10 @@ class SuiteContext {
return this.#suite.name; return this.#suite.name;
} }
get filePath() {
return this.#suite.entryFile;
}
get fullName() { get fullName() {
return getFullName(this.#suite); return getFullName(this.#suite);
} }
@ -357,7 +365,7 @@ class Test extends AsyncResource {
super('Test'); super('Test');
let { fn, name, parent } = options; let { fn, name, parent } = options;
const { concurrency, loc, only, timeout, todo, skip, signal, plan } = options; const { concurrency, entryFile, loc, only, timeout, todo, skip, signal, plan } = options;
if (typeof fn !== 'function') { if (typeof fn !== 'function') {
fn = noop; fn = noop;
@ -386,6 +394,7 @@ class Test extends AsyncResource {
this.runOnlySubtests = this.only; this.runOnlySubtests = this.only;
this.childNumber = 0; this.childNumber = 0;
this.timeout = kDefaultTimeout; this.timeout = kDefaultTimeout;
this.entryFile = entryFile;
this.root = this; this.root = this;
this.hooks = { this.hooks = {
__proto__: null, __proto__: null,
@ -406,6 +415,7 @@ class Test extends AsyncResource {
this.runOnlySubtests = !this.only; this.runOnlySubtests = !this.only;
this.childNumber = parent.subtests.length + 1; this.childNumber = parent.subtests.length + 1;
this.timeout = parent.timeout; this.timeout = parent.timeout;
this.entryFile = parent.entryFile;
this.root = parent.root; this.root = parent.root;
this.hooks = { this.hooks = {
__proto__: null, __proto__: null,

View File

@ -0,0 +1,52 @@
'use strict';
require('../common');
const tmpdir = require('../common/tmpdir');
const { strictEqual } = require('node:assert');
const { writeFileSync } = require('node:fs');
const { suite, test } = require('node:test');
tmpdir.refresh();
suite('suite', (t) => {
strictEqual(t.filePath, __filename);
test('test', (t) => {
strictEqual(t.filePath, __filename);
t.test('subtest', (t) => {
strictEqual(t.filePath, __filename);
t.test('subsubtest', (t) => {
strictEqual(t.filePath, __filename);
});
});
});
});
test((t) => {
strictEqual(t.filePath, __filename);
});
const importedTestFile = tmpdir.resolve('temp.js');
writeFileSync(importedTestFile, `
'use strict';
const { strictEqual } = require('node:assert');
const { suite, test } = require('node:test');
suite('imported suite', (t) => {
strictEqual(t.filePath, ${JSON.stringify(__filename)});
test('imported test', (t) => {
strictEqual(t.filePath, ${JSON.stringify(__filename)});
t.test('imported subtest', (t) => {
strictEqual(t.filePath, ${JSON.stringify(__filename)});
t.test('imported subsubtest', (t) => {
strictEqual(t.filePath, ${JSON.stringify(__filename)});
});
});
});
});
`);
require(importedTestFile);