From 81ce7dc69f91b3793302556c5386000e7dc3b182 Mon Sep 17 00:00:00 2001 From: Moshe Atlow Date: Wed, 11 Feb 2026 16:10:38 +0200 Subject: [PATCH] test_runner: fix suite rerun --- lib/internal/test_runner/test.js | 6 ++- test/fixtures/test-runner/rerun.js | 10 ++++- .../test-runner-test-rerun-failures.js | 38 ++++++++++++++----- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/lib/internal/test_runner/test.js b/lib/internal/test_runner/test.js index beeb49c1763473..a57bc5995e3b9f 100644 --- a/lib/internal/test_runner/test.js +++ b/lib/internal/test_runner/test.js @@ -706,10 +706,12 @@ class Test extends AsyncResource { this.fn = () => { for (let i = 0; i < (previousAttempt.children?.length ?? 0); i++) { const child = previousAttempt.children[i]; - this.createSubtest(Test, child.name, { __proto__: null }, noop, { + const t = this.createSubtest(Test, child.name, { __proto__: null }, noop, { __proto__: null, loc: [child.line, child.column, child.file], - }, noop).start(); + }, noop); + t.endTime = t.startTime = hrtime(); + t.start(); } }; } diff --git a/test/fixtures/test-runner/rerun.js b/test/fixtures/test-runner/rerun.js index a76a1bd00dd12a..563e92e4c1bb80 100644 --- a/test/fixtures/test-runner/rerun.js +++ b/test/fixtures/test-runner/rerun.js @@ -1,4 +1,4 @@ -const { test } = require('node:test') +const { test, describe } = require('node:test') test('should fail on first two attempts', ({ attempt }) => { if (attempt < 2) { @@ -38,3 +38,11 @@ function nestedAmbiguousTest(expectedAttempts) { test('nested ambiguous (expectedAttempts=0)', nestedAmbiguousTest(0)); test('nested ambiguous (expectedAttempts=1)', nestedAmbiguousTest(2)); + + +describe('describe rerun', { timeout: 1000, concurrency: 1000 }, () => { + test('passed on first attempt', async (t) => { + await t.test('nested', async () => {}); + }); + test('a'); +}); diff --git a/test/parallel/test-runner-test-rerun-failures.js b/test/parallel/test-runner-test-rerun-failures.js index 220ec140b368a6..4a9ea5d4ac0bee 100644 --- a/test/parallel/test-runner-test-rerun-failures.js +++ b/test/parallel/test-runner-test-rerun-failures.js @@ -23,6 +23,10 @@ const expectedStateFile = [ 'test/fixtures/test-runner/rerun.js:39:1': { passed_on_attempt: 0, name: 'nested ambiguous (expectedAttempts=0)' }, 'test/fixtures/test-runner/rerun.js:30:16:(1)': { passed_on_attempt: 0, name: '2 levels deep' }, 'test/fixtures/test-runner/rerun.js:35:13:(1)': { passed_on_attempt: 0, name: 'ok' }, + 'test/fixtures/test-runner/rerun.js:45:13': { passed_on_attempt: 0, name: 'nested' }, + 'test/fixtures/test-runner/rerun.js:44:3': { passed_on_attempt: 0, name: 'passed on first attempt' }, + 'test/fixtures/test-runner/rerun.js:47:3': { passed_on_attempt: 0, name: 'a' }, + 'test/fixtures/test-runner/rerun.js:43:1': { passed_on_attempt: 0, name: 'describe rerun' }, }, { 'test/fixtures/test-runner/rerun.js:9:1': { passed_on_attempt: 0, name: 'ok' }, @@ -34,6 +38,11 @@ const expectedStateFile = [ 'test/fixtures/test-runner/rerun.js:39:1': { passed_on_attempt: 0, name: 'nested ambiguous (expectedAttempts=0)' }, 'test/fixtures/test-runner/rerun.js:30:16:(1)': { passed_on_attempt: 0, name: '2 levels deep' }, 'test/fixtures/test-runner/rerun.js:35:13:(1)': { passed_on_attempt: 0, name: 'ok' }, + 'test/fixtures/test-runner/rerun.js:43:1': { passed_on_attempt: 0, name: 'describe rerun' }, + 'test/fixtures/test-runner/rerun.js:44:3': { passed_on_attempt: 0, name: 'passed on first attempt' }, + 'test/fixtures/test-runner/rerun.js:45:13': { passed_on_attempt: 0, name: 'nested' }, + 'test/fixtures/test-runner/rerun.js:45:13:(1)': { passed_on_attempt: 1, name: 'nested' }, + 'test/fixtures/test-runner/rerun.js:47:3': { passed_on_attempt: 0, name: 'a' }, }, { 'test/fixtures/test-runner/rerun.js:3:1': { passed_on_attempt: 2, name: 'should fail on first two attempts' }, @@ -48,6 +57,11 @@ const expectedStateFile = [ 'test/fixtures/test-runner/rerun.js:30:16:(1)': { passed_on_attempt: 0, name: '2 levels deep' }, 'test/fixtures/test-runner/rerun.js:35:13:(1)': { passed_on_attempt: 0, name: 'ok' }, 'test/fixtures/test-runner/rerun.js:40:1': { passed_on_attempt: 2, name: 'nested ambiguous (expectedAttempts=1)' }, + 'test/fixtures/test-runner/rerun.js:43:1': { passed_on_attempt: 0, name: 'describe rerun' }, + 'test/fixtures/test-runner/rerun.js:44:3': { passed_on_attempt: 0, name: 'passed on first attempt' }, + 'test/fixtures/test-runner/rerun.js:45:13': { passed_on_attempt: 0, name: 'nested' }, + 'test/fixtures/test-runner/rerun.js:45:13:(1)': { passed_on_attempt: 1, name: 'nested' }, + 'test/fixtures/test-runner/rerun.js:47:3': { passed_on_attempt: 0, name: 'a' }, }, ]; @@ -67,23 +81,26 @@ test('test should pass on third rerun', async () => { let { code, stdout, signal } = await common.spawnPromisified(process.execPath, args); assert.strictEqual(code, 1); assert.strictEqual(signal, null); - assert.match(stdout, /pass 8/); + assert.match(stdout, /pass 11/); assert.match(stdout, /fail 4/); + assert.match(stdout, /suites 1/); assert.deepStrictEqual(await getStateFile(), expectedStateFile.slice(0, 1)); ({ code, stdout, signal } = await common.spawnPromisified(process.execPath, args)); assert.strictEqual(code, 1); assert.strictEqual(signal, null); - assert.match(stdout, /pass 9/); + assert.match(stdout, /pass 13/); assert.match(stdout, /fail 3/); + assert.match(stdout, /suites 1/); assert.deepStrictEqual(await getStateFile(), expectedStateFile.slice(0, 2)); ({ code, stdout, signal } = await common.spawnPromisified(process.execPath, args)); assert.strictEqual(code, 0); assert.strictEqual(signal, null); - assert.match(stdout, /pass 12/); + assert.match(stdout, /pass 18/); assert.match(stdout, /fail 0/); + assert.match(stdout, /suites 1/); assert.deepStrictEqual(await getStateFile(), expectedStateFile); }); @@ -93,29 +110,32 @@ test('test should pass on third rerun with `--test`', async () => { let { code, stdout, signal } = await common.spawnPromisified(process.execPath, args); assert.strictEqual(code, 1); assert.strictEqual(signal, null); - assert.match(stdout, /pass 8/); + assert.match(stdout, /pass 11/); assert.match(stdout, /fail 4/); + assert.match(stdout, /suites 1/); assert.deepStrictEqual(await getStateFile(), expectedStateFile.slice(0, 1)); ({ code, stdout, signal } = await common.spawnPromisified(process.execPath, args)); assert.strictEqual(code, 1); assert.strictEqual(signal, null); - assert.match(stdout, /pass 9/); + assert.match(stdout, /pass 13/); assert.match(stdout, /fail 3/); + assert.match(stdout, /suites 1/); assert.deepStrictEqual(await getStateFile(), expectedStateFile.slice(0, 2)); ({ code, stdout, signal } = await common.spawnPromisified(process.execPath, args)); assert.strictEqual(code, 0); assert.strictEqual(signal, null); - assert.match(stdout, /pass 12/); + assert.match(stdout, /pass 18/); assert.match(stdout, /fail 0/); + assert.match(stdout, /suites 1/); assert.deepStrictEqual(await getStateFile(), expectedStateFile); }); test('using `run` api', async () => { let stream = run({ files: [fixture], rerunFailuresFilePath: stateFile }); - stream.on('test:pass', common.mustCall(8)); + stream.on('test:pass', common.mustCall(12)); stream.on('test:fail', common.mustCall(4)); // eslint-disable-next-line no-unused-vars @@ -125,7 +145,7 @@ test('using `run` api', async () => { stream = run({ files: [fixture], rerunFailuresFilePath: stateFile }); - stream.on('test:pass', common.mustCall(9)); + stream.on('test:pass', common.mustCall(14)); stream.on('test:fail', common.mustCall(3)); // eslint-disable-next-line no-unused-vars @@ -135,7 +155,7 @@ test('using `run` api', async () => { stream = run({ files: [fixture], rerunFailuresFilePath: stateFile }); - stream.on('test:pass', common.mustCall(12)); + stream.on('test:pass', common.mustCall(19)); stream.on('test:fail', common.mustNotCall()); // eslint-disable-next-line no-unused-vars