diff --git a/lib/internal/test_runner/runner.js b/lib/internal/test_runner/runner.js index 958ad1e060fbbe..31d2220ab7a016 100644 --- a/lib/internal/test_runner/runner.js +++ b/lib/internal/test_runner/runner.js @@ -707,6 +707,17 @@ function run(options = kEmptyObject) { cwd, globalSetupPath, }; + + if (isolation === 'none') { + if (testNamePatterns && !globalOptions.testNamePatterns) { + globalOptions.testNamePatterns = testNamePatterns; + } + + if (testSkipPatterns && !globalOptions.testSkipPatterns) { + globalOptions.testSkipPatterns = testSkipPatterns; + } + } + const root = createTestTree(rootTestOptions, globalOptions); let testFiles = files ?? createTestFileList(globPatterns, cwd); const { isTestRunner } = globalOptions; diff --git a/test/parallel/test-runner-run.mjs b/test/parallel/test-runner-run.mjs index 7ddd8c1dcd83e5..7696b7499ad6c4 100644 --- a/test/parallel/test-runner-run.mjs +++ b/test/parallel/test-runner-run.mjs @@ -5,6 +5,54 @@ import { describe, it, run } from 'node:test'; import { dot, spec, tap } from 'node:test/reporters'; import assert from 'node:assert'; import util from 'node:util'; +import { spawn } from 'node:child_process'; + +import { writeFileSync, unlinkSync } from 'node:fs'; +import tmpdir from '../common/tmpdir.js'; +import { randomBytes } from 'node:crypto'; + +async function runTestInSubprocess(testFile, options = {}) { + tmpdir.refresh(); + + const tmpFile = tmpdir.resolve(`test-subprocess-${randomBytes(8).toString('hex')}.mjs`); + + const testNamePatterns = options.testNamePatterns ? JSON.stringify(options.testNamePatterns) : undefined; + const testSkipPatterns = options.testSkipPatterns ? JSON.stringify(options.testSkipPatterns) : undefined; + const isolation = options.isolation ? JSON.stringify(options.isolation) : undefined; + + const code = ` + import { run } from 'node:test'; + import { tap } from 'node:test/reporters'; + import { join } from 'node:path'; + const files = [${JSON.stringify(testFile)}]; + const opts = { + files, + reporter: 'tap', + ${testNamePatterns ? `testNamePatterns: ${testNamePatterns},` : ''} + ${testSkipPatterns ? `testSkipPatterns: ${testSkipPatterns},` : ''} + ${isolation ? `isolation: ${isolation},` : ''} + }; + const result = await run(opts).compose(tap).toArray(); + for (const line of result) { + process.stdout.write(typeof line === 'string' ? line : String(line)); + } + `; + writeFileSync(tmpFile, code); + return new Promise((resolve, reject) => { + const child = spawn(process.execPath, [tmpFile], { stdio: ['ignore', 'pipe', 'pipe'] }); + let stdout = ''; + let stderr = ''; + child.stdout.on('data', (data) => { stdout += data; }); + child.stderr.on('data', (data) => { stderr += data; }); + child.on('close', (code) => { + unlinkSync(tmpFile); + if (code !== 0) { + return reject(new Error(`Test failed with exit code ${code}\n${stderr}`)); + } + resolve(stdout); + }); + }); +} const testFixtures = fixtures.path('test-runner'); @@ -650,6 +698,30 @@ describe('require(\'node:test\').run', { concurrency: true }, () => { }); }); +describe("with isolation set to 'none'", () => { + it('should skip tests not matching testNamePatterns - string', async () => { + const result = await runTestInSubprocess( + join(testFixtures, 'default-behavior/test/skip_by_name.cjs'), + { isolation: 'none', testNamePatterns: ['executed'] } + ); + + assert.match(result, /ok 1 - this should be executed/); + assert.match(result, /1\.\.1/); + assert.match(result, /# tests 1/); + }); + + it('should skip tests matching testSkipPatterns', async () => { + const result = await runTestInSubprocess( + join(testFixtures, 'default-behavior/test/skip_by_name.cjs'), + { isolation: 'none', testSkipPatterns: ['skipped'] } + ); + + assert.match(result, /ok 1 - this should be executed/); + assert.match(result, /1\.\.1/); + assert.match(result, /# tests 1/); + }); +}); + describe('forceExit', () => { it('throws for non-boolean values', () => { [Symbol(), {}, 0, 1, '1', Promise.resolve([])].forEach((forceExit) => { @@ -668,7 +740,6 @@ describe('forceExit', () => { }); }); - // exitHandler doesn't run until after the tests / after hooks finish. process.on('exit', () => { assert.strictEqual(process.listeners('uncaughtException').length, 0);