diff --git a/doc/api/test.md b/doc/api/test.md index b17c3fd4f6685b..231aa956bd2a45 100644 --- a/doc/api/test.md +++ b/doc/api/test.md @@ -3435,6 +3435,9 @@ added: - v18.9.0 - v16.19.0 changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/63435 + description: Added `parentId` to test events that carry a `testId`. - version: - v20.0.0 - v19.9.0 @@ -3522,6 +3525,9 @@ Emitted when code coverage is enabled and all tests have completed. `undefined` if the test was run through the REPL. * `name` {string} The test name. * `nesting` {number} The nesting level of the test. + * `parentId` {number|undefined} The `testId` of the enclosing test, or + `undefined` for top-level tests. Lets custom reporters track lineage + when concurrent siblings at the same nesting level interleave. * `tags` {string\[]} The flattened lowercased tags declared on the test and its ancestor suites, in declaration order. Empty for untagged tests. See [Test tags][]. @@ -3548,6 +3554,9 @@ The corresponding declaration ordered events are `'test:pass'` and `'test:fail'` `undefined` if the test was run through the REPL. * `name` {string} The test name. * `nesting` {number} The nesting level of the test. + * `parentId` {number|undefined} The `testId` of the enclosing test, or + `undefined` for top-level tests. Lets custom reporters track lineage + when concurrent siblings at the same nesting level interleave. * `tags` {string\[]} The flattened lowercased tags declared on the test and its ancestor suites, in declaration order. Empty for untagged tests. See [Test tags][]. @@ -3592,6 +3601,9 @@ defined. `undefined` if the test was run through the REPL. * `name` {string} The test name. * `nesting` {number} The nesting level of the test. + * `parentId` {number|undefined} The `testId` of the enclosing test, or + `undefined` for top-level tests. Lets custom reporters track lineage + when concurrent siblings at the same nesting level interleave. * `tags` {string\[]} The flattened lowercased tags declared on the test and its ancestor suites, in declaration order. Empty for untagged tests. See [Test tags][]. @@ -3621,6 +3633,9 @@ Emitted when a test is enqueued for execution. `undefined` if the test was run through the REPL. * `name` {string} The test name. * `nesting` {number} The nesting level of the test. + * `parentId` {number|undefined} The `testId` of the enclosing test, or + `undefined` for top-level tests. Lets custom reporters track lineage + when concurrent siblings at the same nesting level interleave. * `tags` {string\[]} The flattened lowercased tags declared on the test and its ancestor suites, in declaration order. Empty for untagged tests. See [Test tags][]. @@ -3683,6 +3698,9 @@ since the parent runner only knows about file-level tests. When using `undefined` if the test was run through the REPL. * `name` {string} The test name. * `nesting` {number} The nesting level of the test. + * `parentId` {number|undefined} The `testId` of the enclosing test, or + `undefined` for top-level tests. Lets custom reporters track lineage + when concurrent siblings at the same nesting level interleave. * `tags` {string\[]} The flattened lowercased tags declared on the test and its ancestor suites, in declaration order. Empty for untagged tests. See [Test tags][]. @@ -3725,6 +3743,9 @@ defined. `undefined` if the test was run through the REPL. * `name` {string} The test name. * `nesting` {number} The nesting level of the test. + * `parentId` {number|undefined} The `testId` of the enclosing test, or + `undefined` for top-level tests. Lets custom reporters track lineage + when concurrent siblings at the same nesting level interleave. * `tags` {string\[]} The flattened lowercased tags declared on the test and its ancestor suites, in declaration order. Empty for untagged tests. See [Test tags][]. diff --git a/lib/internal/test_runner/test.js b/lib/internal/test_runner/test.js index eb888905798bcd..5fdd80e432dc9d 100644 --- a/lib/internal/test_runner/test.js +++ b/lib/internal/test_runner/test.js @@ -942,7 +942,7 @@ class Test extends AsyncResource { const deferred = this.dequeuePendingSubtest(); const test = deferred.test; this.assignReportOrder(test); - test.reporter.dequeue(test.nesting, test.loc, test.name, this.reportedType, test.testId, test.tags); + test.reporter.dequeue(test.nesting, test.loc, test.name, this.reportedType, test.testId, this.testId, test.tags); await test.run(); deferred.resolve(); } @@ -1199,7 +1199,8 @@ class Test extends AsyncResource { // it. Otherwise, return a Promise to the caller and mark the test as // pending for later execution. this.parent.unfinishedSubtests.add(this); - this.reporter.enqueue(this.nesting, this.loc, this.name, this.reportedType, this.testId, this.tags); + this.reporter.enqueue(this.nesting, this.loc, this.name, this.reportedType, + this.testId, this.parent?.testId, this.tags); if (this.root.harness.buildPromise || !this.parent.hasConcurrency()) { const deferred = PromiseWithResolvers(); @@ -1222,7 +1223,8 @@ class Test extends AsyncResource { } this.parent.assignReportOrder(this); - this.reporter.dequeue(this.nesting, this.loc, this.name, this.reportedType, this.testId, this.tags); + this.reporter.dequeue(this.nesting, this.loc, this.name, this.reportedType, + this.testId, this.parent?.testId, this.tags); return this.run(); } @@ -1496,7 +1498,7 @@ class Test extends AsyncResource { this.testNumber ||= ++this.parent.outputSubtestCount; this.reporter.complete( this.nesting, this.loc, this.testNumber, this.name, - report.details, report.directive, this.testId, this.tags, + report.details, report.directive, this.testId, this.parent?.testId, this.tags, ); this.parent.activeSubtests--; } @@ -1652,12 +1654,12 @@ class Test extends AsyncResource { if (this.passed) { this.reporter.ok( this.nesting, this.loc, this.testNumber, this.name, - report.details, report.directive, this.testId, this.tags, + report.details, report.directive, this.testId, this.parent?.testId, this.tags, ); } else { this.reporter.fail( this.nesting, this.loc, this.testNumber, this.name, - report.details, report.directive, this.testId, this.tags, + report.details, report.directive, this.testId, this.parent?.testId, this.tags, ); } @@ -1672,7 +1674,7 @@ class Test extends AsyncResource { } this.#reportedSubtest = true; this.parent.reportStarted(); - this.reporter.start(this.nesting, this.loc, this.name, this.testId, this.tags); + this.reporter.start(this.nesting, this.loc, this.name, this.testId, this.parent?.testId, this.tags); } clearExecutionTime() { @@ -1734,7 +1736,7 @@ class TestHook extends Test { __proto__: null, duration_ms: this.duration(), error, - }, undefined, undefined, parent.tags); + }, undefined, undefined, undefined, parent.tags); } } } diff --git a/lib/internal/test_runner/tests_stream.js b/lib/internal/test_runner/tests_stream.js index 622e6372fa1d13..8be33f90dfa0f2 100644 --- a/lib/internal/test_runner/tests_stream.js +++ b/lib/internal/test_runner/tests_stream.js @@ -35,13 +35,14 @@ class TestsStream extends Readable { } } - fail(nesting, loc, testNumber, name, details, directive, testId, tags) { + fail(nesting, loc, testNumber, name, details, directive, testId, parentId, tags) { this[kEmitMessage]('test:fail', { __proto__: null, name, nesting, testNumber, testId, + parentId, details, tags: ArrayPrototypeSlice(tags), ...loc, @@ -49,13 +50,14 @@ class TestsStream extends Readable { }); } - ok(nesting, loc, testNumber, name, details, directive, testId, tags) { + ok(nesting, loc, testNumber, name, details, directive, testId, parentId, tags) { this[kEmitMessage]('test:pass', { __proto__: null, name, nesting, testNumber, testId, + parentId, details, tags: ArrayPrototypeSlice(tags), ...loc, @@ -63,13 +65,14 @@ class TestsStream extends Readable { }); } - complete(nesting, loc, testNumber, name, details, directive, testId, tags) { + complete(nesting, loc, testNumber, name, details, directive, testId, parentId, tags) { this[kEmitMessage]('test:complete', { __proto__: null, name, nesting, testNumber, testId, + parentId, details, tags: ArrayPrototypeSlice(tags), ...loc, @@ -98,36 +101,39 @@ class TestsStream extends Readable { return { __proto__: null, expectFailure: expectation ?? true }; } - enqueue(nesting, loc, name, type, testId, tags) { + enqueue(nesting, loc, name, type, testId, parentId, tags) { this[kEmitMessage]('test:enqueue', { __proto__: null, nesting, name, type, testId, + parentId, tags: ArrayPrototypeSlice(tags), ...loc, }); } - dequeue(nesting, loc, name, type, testId, tags) { + dequeue(nesting, loc, name, type, testId, parentId, tags) { this[kEmitMessage]('test:dequeue', { __proto__: null, nesting, name, type, testId, + parentId, tags: ArrayPrototypeSlice(tags), ...loc, }); } - start(nesting, loc, name, testId, tags) { + start(nesting, loc, name, testId, parentId, tags) { this[kEmitMessage]('test:start', { __proto__: null, nesting, name, testId, + parentId, tags: ArrayPrototypeSlice(tags), ...loc, });