diff --git a/lib/internal/test_runner/runner.js b/lib/internal/test_runner/runner.js index dc6529c220539a..ee587e041a9d7f 100644 --- a/lib/internal/test_runner/runner.js +++ b/lib/internal/test_runner/runner.js @@ -18,6 +18,7 @@ const { ObjectAssign, PromisePrototypeThen, PromiseWithResolvers, + RegExpPrototypeSymbolReplace, SafeMap, SafePromiseAll, SafePromiseAllReturnVoid, @@ -34,6 +35,7 @@ const { } = primordials; const { spawn } = require('child_process'); +const { statSync } = require('fs'); const { finished } = require('internal/streams/end-of-stream'); const { availableParallelism } = require('os'); const { resolve, sep, isAbsolute } = require('path'); @@ -153,6 +155,23 @@ function createTestFileList(patterns, cwd) { const hasUserSuppliedPattern = patterns != null; if (!patterns || patterns.length === 0) { patterns = [kDefaultPattern]; + } else { + patterns = ArrayPrototypeMap(patterns, (pattern) => { + // A directory argument is expanded to search for the default test files + // within it, matching the behavior from before glob patterns were + // supported. Glob patterns always use forward slashes, so a trailing + // path separator (of either kind) is stripped before appending. + const resolved = isAbsolute(pattern) ? pattern : resolve(cwd, pattern); + try { + if (statSync(resolved).isDirectory()) { + return `${RegExpPrototypeSymbolReplace(/[\\/]+$/, pattern, '')}/${kDefaultPattern}`; + } + } catch { + // Not a directory or not accessible; leave the pattern as-is so the + // existing "Could not find" handling still applies. + } + return pattern; + }); } const glob = new Glob(patterns, { __proto__: null, diff --git a/test/parallel/test-runner-cli.js b/test/parallel/test-runner-cli.js index f563d67d1a9a41..0702c5c1500c29 100644 --- a/test/parallel/test-runner-cli.js +++ b/test/parallel/test-runner-cli.js @@ -62,6 +62,27 @@ for (const isolation of ['none', 'process']) { assert.doesNotMatch(stdout, /ok 4 - this should pass/); } + { + // A directory argument is searched for the default test files within it, + // both bare and with a trailing separator. + // Refs: https://github.com/nodejs/node/issues/64555 + for (const dir of ['matching-patterns', 'matching-patterns/']) { + const args = ['--test', '--test-reporter=tap', + '--no-experimental-strip-types', + `--test-isolation=${isolation}`, dir]; + const child = spawnSync(process.execPath, args, { cwd: testFixtures }); + + assert.strictEqual(child.status, 0); + assert.strictEqual(child.signal, null); + assert.strictEqual(child.stderr.toString(), ''); + const stdout = child.stdout.toString(); + + assert.match(stdout, /ok 1 - this should pass/); + assert.match(stdout, /ok 2 - this should pass/); + assert.match(stdout, /ok 3 - this should pass/); + } + } + { // Should match files with "-test.(c|m)(t|j)s" suffix when typescript support is enabled const args = ['--test', '--test-reporter=tap', '--no-warnings',