diff --git a/lib/internal/test_runner/test.js b/lib/internal/test_runner/test.js index 1b9faad58a68b5..38ce54e4ea9b6f 100644 --- a/lib/internal/test_runner/test.js +++ b/lib/internal/test_runner/test.js @@ -1854,6 +1854,11 @@ class Suite extends Test { return { __proto__: null, ctx, args: [ctx] }; } + async filteredRun() { + await this.buildSuite; + return super.filteredRun(); + } + async run() { this.computeInheritedHooks(); const hookArgs = this.getRunArgs(); diff --git a/test/fixtures/test-runner/filtered-suite-async-build.mjs b/test/fixtures/test-runner/filtered-suite-async-build.mjs new file mode 100644 index 00000000000000..0e96ff889fb828 --- /dev/null +++ b/test/fixtures/test-runner/filtered-suite-async-build.mjs @@ -0,0 +1,17 @@ +import test from 'node:test'; +import { setTimeout as delay } from 'node:timers/promises'; + +test.suite('Outer', async () => { + await delay(1); + + // This suite is filtered out by name. Its build is still pending when the + // filtered run starts, so the subtest below is registered late. + test.suite('Nested A', async () => { + await delay(1); + test('Nested A test', async () => {}); + }); + + test.suite('Nested C', async () => { + test('Nested C test', async () => {}); + }); +}); diff --git a/test/parallel/test-runner-no-isolation-filtering.js b/test/parallel/test-runner-no-isolation-filtering.js index e26130c56b196d..f7b0f3f73ff0c1 100644 --- a/test/parallel/test-runner-no-isolation-filtering.js +++ b/test/parallel/test-runner-no-isolation-filtering.js @@ -7,6 +7,8 @@ const { test } = require('node:test'); const fixture1 = fixtures.path('test-runner', 'no-isolation', 'one.test.js'); const fixture2 = fixtures.path('test-runner', 'no-isolation', 'two.test.js'); +const asyncBuildFilteredSuite = + fixtures.path('test-runner', 'filtered-suite-async-build.mjs'); test('works with --test-only', () => { const args = [ @@ -71,6 +73,27 @@ test('works with --test-name-pattern', () => { assert.match(stdout, /# suites 0/); }); +test('filtered suites with an async build do not leave cancelled tests', () => { + const args = [ + '--test', + '--test-reporter=tap', + '--test-isolation=none', + '--test-name-pattern=C', + asyncBuildFilteredSuite, + ]; + const child = spawnSync(process.execPath, args); + const stdout = child.stdout.toString(); + + assert.strictEqual(child.status, 0); + assert.strictEqual(child.signal, null); + assert.match(stdout, /# tests 1/); + assert.match(stdout, /# suites 2/); + assert.match(stdout, /# pass 1/); + assert.match(stdout, /# fail 0/); + assert.match(stdout, /# cancelled 0/); + assert.doesNotMatch(stdout, /parentAlreadyFinished/); +}); + test('works with --test-skip-pattern', () => { const args = [ '--test',