Skip to content

Commit 00dc6d9

Browse files
authored
test_runner: improve --test-name-pattern to allow matching single test
Try to match a test by name prefixed with all its ancestors to ensure uniqueness of the name Fixes: #46728 PR-URL: #51577 Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Chemi Atlow <chemi@atlow.co.il> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
1 parent 6aa1d98 commit 00dc6d9

File tree

4 files changed

+95
-8
lines changed

4 files changed

+95
-8
lines changed

doc/api/test.md

+17
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,23 @@ allows regular expression flags to be used. In the previous example, starting
267267
Node.js with `--test-name-pattern="/test [4-5]/i"` would match `Test 4` and
268268
`Test 5` because the pattern is case-insensitive.
269269

270+
To match a single test with a pattern, you can prefix it with all its ancestor
271+
test names separated by space, to ensure it is unique.
272+
For example, given the following test file:
273+
274+
```js
275+
describe('test 1', (t) => {
276+
it('some test');
277+
});
278+
279+
describe('test 2', (t) => {
280+
it('some test');
281+
});
282+
```
283+
284+
Starting Node.js with `--test-name-pattern="test 1 some test"` would match
285+
only `some test` in `test 1`.
286+
270287
Test name patterns do not change the set of files that the test runner executes.
271288

272289
## Extraneous asynchronous activity

lib/internal/test_runner/test.js

+24-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const {
1414
PromisePrototypeThen,
1515
PromiseResolve,
1616
SafePromisePrototypeFinally,
17+
StringPrototypeTrim,
1718
ReflectApply,
1819
RegExpPrototypeExec,
1920
SafeMap,
@@ -362,8 +363,29 @@ class Test extends AsyncResource {
362363
}
363364

364365
matchesTestNamePatterns() {
365-
return ArrayPrototypeSome(testNamePatterns, (re) => RegExpPrototypeExec(re, this.name) !== null) ||
366-
this.parent?.matchesTestNamePatterns();
366+
const matchesByNameOrParent = ArrayPrototypeSome(testNamePatterns, (re) =>
367+
RegExpPrototypeExec(re, this.name) !== null,
368+
) ||
369+
this.parent?.matchesTestNamePatterns();
370+
371+
if (matchesByNameOrParent) return true;
372+
373+
const testNameWithAncestors = StringPrototypeTrim(this.getTestNameWithAncestors());
374+
if (!testNameWithAncestors) return false;
375+
376+
return ArrayPrototypeSome(testNamePatterns, (re) => RegExpPrototypeExec(re, testNameWithAncestors) !== null);
377+
}
378+
379+
/**
380+
* Returns a name of the test prefixed by name of all its ancestors in ascending order, separated by a space
381+
* Ex."grandparent parent test"
382+
*
383+
* It's needed to match a single test with non-unique name by pattern
384+
*/
385+
getTestNameWithAncestors() {
386+
if (!this.parent) return '';
387+
388+
return `${this.parent.getTestNameWithAncestors()} ${this.name}`;
367389
}
368390

369391
hasConcurrency() {

test/fixtures/test-runner/output/name_pattern.js

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Flags: --test-name-pattern=enabled --test-name-pattern=yes --test-name-pattern=/pattern/i
1+
// Flags: --test-name-pattern=enabled --test-name-pattern=yes --test-name-pattern=/pattern/i --test-name-pattern=/^DescribeForMatchWithAncestors\sNestedDescribeForMatchWithAncestors\sNestedTest$/
22
'use strict';
33
const common = require('../../../common');
44
const {
@@ -65,3 +65,15 @@ describe('no', function() {
6565
it('yes', () => {});
6666
});
6767
});
68+
69+
describe('DescribeForMatchWithAncestors', () => {
70+
it('NestedTest', () => common.mustNotCall());
71+
72+
describe('NestedDescribeForMatchWithAncestors', () => {
73+
it('NestedTest', common.mustCall());
74+
});
75+
})
76+
77+
describe('DescribeForMatchWithAncestors', () => {
78+
it('NestedTest', () => common.mustNotCall());
79+
})

test/fixtures/test-runner/output/name_pattern.snapshot

+41-5
Original file line numberDiff line numberDiff line change
@@ -171,12 +171,48 @@ ok 15 - no
171171
duration_ms: *
172172
type: 'suite'
173173
...
174-
1..15
175-
# tests 21
176-
# suites 10
177-
# pass 13
174+
# Subtest: DescribeForMatchWithAncestors
175+
# Subtest: NestedTest
176+
ok 1 - NestedTest # SKIP test name does not match pattern
177+
---
178+
duration_ms: *
179+
...
180+
# Subtest: NestedDescribeForMatchWithAncestors
181+
# Subtest: NestedTest
182+
ok 1 - NestedTest
183+
---
184+
duration_ms: *
185+
...
186+
1..1
187+
ok 2 - NestedDescribeForMatchWithAncestors
188+
---
189+
duration_ms: *
190+
type: 'suite'
191+
...
192+
1..2
193+
ok 16 - DescribeForMatchWithAncestors
194+
---
195+
duration_ms: *
196+
type: 'suite'
197+
...
198+
# Subtest: DescribeForMatchWithAncestors
199+
# Subtest: NestedTest
200+
ok 1 - NestedTest # SKIP test name does not match pattern
201+
---
202+
duration_ms: *
203+
...
204+
1..1
205+
ok 17 - DescribeForMatchWithAncestors
206+
---
207+
duration_ms: *
208+
type: 'suite'
209+
...
210+
1..17
211+
# tests 24
212+
# suites 13
213+
# pass 14
178214
# fail 0
179215
# cancelled 0
180-
# skipped 8
216+
# skipped 10
181217
# todo 0
182218
# duration_ms *

0 commit comments

Comments
 (0)