diff --git a/lib/internal/test_runner/runner.js b/lib/internal/test_runner/runner.js index 7f50cc8521bcda..7176c4a1fcd6fc 100644 --- a/lib/internal/test_runner/runner.js +++ b/lib/internal/test_runner/runner.js @@ -34,6 +34,7 @@ const { } = primordials; const { spawn } = require('child_process'); +const { existsSync } = require('fs'); const { finished } = require('internal/streams/end-of-stream'); const { availableParallelism } = require('os'); const { resolve, sep, isAbsolute } = require('path'); @@ -161,9 +162,20 @@ function createTestFileList(patterns, cwd) { }); const results = glob.globSync(); - if (hasUserSuppliedPattern && results.length === 0 && ArrayPrototypeEvery(glob.matchers, (m) => !m.hasMagic())) { - console.error(`Could not find '${ArrayPrototypeJoin(patterns, ', ')}'`); - process.exit(kGenericUserError); + if (hasUserSuppliedPattern) { + if (results.length === 0 && ArrayPrototypeEvery(glob.matchers, (m) => !m.hasMagic())) { + console.error(`Could not find '${ArrayPrototypeJoin(patterns, ', ')}'`); + process.exit(kGenericUserError); + } + + const missing = ArrayPrototypeFilter(patterns, (pattern, i) => { + return !glob.matchers[i].hasMagic() && !existsSync(resolve(cwd, pattern)); + }); + + if (missing.length > 0) { + console.error(`Could not find '${ArrayPrototypeJoin(missing, ', ')}'`); + process.exit(kGenericUserError); + } } return ArrayPrototypeSort(results); diff --git a/test/parallel/test-runner-cli.js b/test/parallel/test-runner-cli.js index f563d67d1a9a41..1a5f25a3495209 100644 --- a/test/parallel/test-runner-cli.js +++ b/test/parallel/test-runner-cli.js @@ -23,6 +23,42 @@ for (const isolation of ['none', 'process']) { assert.match(child.stderr.toString(), /^Could not find/); } + { + // A file that is not found should error even when other patterns match. + const args = [ + '--test', + `--test-isolation=${isolation}`, + 'a-random-file-that-does-not-exist.js', + join(testFixtures, 'default-behavior/test/random.cjs'), + ]; + const child = spawnSync(process.execPath, args); + + assert.strictEqual(child.status, 1); + assert.strictEqual(child.signal, null); + assert.strictEqual(child.stdout.toString(), ''); + assert.match(child.stderr.toString(), + /^Could not find 'a-random-file-that-does-not-exist\.js'/); + } + + { + // Options after positional arguments are treated as patterns and should + // error instead of being silently dropped. + const args = [ + '--test', + `--test-isolation=${isolation}`, + join(testFixtures, 'default-behavior/test/random.cjs'), + '--test-reporter', + 'tap', + ]; + const child = spawnSync(process.execPath, args); + + assert.strictEqual(child.status, 1); + assert.strictEqual(child.signal, null); + assert.strictEqual(child.stdout.toString(), ''); + assert.match(child.stderr.toString(), + /^Could not find '--test-reporter, tap'/); + } + { // Default behavior. node_modules is ignored. Files that don't match the // pattern are ignored except in test/ directories.