Skip to content

Commit 26087fb

Browse files
committed
test_runner: expand directory arguments to find test files
When a directory is passed as an argument to node --test, the test runner treats it as a file pattern. Node.js internal Glob matches the directory itself, causing MODULE_NOT_FOUND when the runner tries to import the directory as a module. This restores the pre-v21 behavior by detecting directory arguments and expanding them to search for default test file patterns within them. Fixes: #64555
1 parent 012ecf5 commit 26087fb

2 files changed

Lines changed: 40 additions & 0 deletions

File tree

lib/internal/test_runner/runner.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const {
1818
ObjectAssign,
1919
PromisePrototypeThen,
2020
PromiseWithResolvers,
21+
RegExpPrototypeSymbolReplace,
2122
SafeMap,
2223
SafePromiseAll,
2324
SafePromiseAllReturnVoid,
@@ -34,6 +35,7 @@ const {
3435
} = primordials;
3536

3637
const { spawn } = require('child_process');
38+
const { statSync } = require('fs');
3739
const { finished } = require('internal/streams/end-of-stream');
3840
const { availableParallelism } = require('os');
3941
const { resolve, sep, isAbsolute } = require('path');
@@ -153,6 +155,23 @@ function createTestFileList(patterns, cwd) {
153155
const hasUserSuppliedPattern = patterns != null;
154156
if (!patterns || patterns.length === 0) {
155157
patterns = [kDefaultPattern];
158+
} else {
159+
patterns = ArrayPrototypeMap(patterns, (pattern) => {
160+
// A directory argument is expanded to search for the default test files
161+
// within it, matching the behavior from before glob patterns were
162+
// supported. Glob patterns always use forward slashes, so a trailing
163+
// path separator (of either kind) is stripped before appending.
164+
const resolved = isAbsolute(pattern) ? pattern : resolve(cwd, pattern);
165+
try {
166+
if (statSync(resolved).isDirectory()) {
167+
return `${RegExpPrototypeSymbolReplace(/[\/]+$/, pattern, '')}/${kDefaultPattern}`;
168+
}
169+
} catch {
170+
// Not a directory or not accessible; leave the pattern as-is so the
171+
// existing "Could not find" handling still applies.
172+
}
173+
return pattern;
174+
});
156175
}
157176
const glob = new Glob(patterns, {
158177
__proto__: null,

test/parallel/test-runner-cli.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,27 @@ for (const isolation of ['none', 'process']) {
6262
assert.doesNotMatch(stdout, /ok 4 - this should pass/);
6363
}
6464

65+
{
66+
// A directory argument is searched for the default test files within it,
67+
// both bare and with a trailing separator.
68+
// Refs: https://github.com/nodejs/node/issues/64555
69+
for (const dir of ['matching-patterns', 'matching-patterns/']) {
70+
const args = ['--test', '--test-reporter=tap',
71+
'--no-experimental-strip-types',
72+
`--test-isolation=${isolation}`, dir];
73+
const child = spawnSync(process.execPath, args, { cwd: testFixtures });
74+
75+
assert.strictEqual(child.status, 0);
76+
assert.strictEqual(child.signal, null);
77+
assert.strictEqual(child.stderr.toString(), '');
78+
const stdout = child.stdout.toString();
79+
80+
assert.match(stdout, /ok 1 - this should pass/);
81+
assert.match(stdout, /ok 2 - this should pass/);
82+
assert.match(stdout, /ok 3 - this should pass/);
83+
}
84+
}
85+
6586
{
6687
// Should match files with "-test.(c|m)(t|j)s" suffix when typescript support is enabled
6788
const args = ['--test', '--test-reporter=tap', '--no-warnings',

0 commit comments

Comments
 (0)