Skip to content

Commit b8b4d7c

Browse files
committed
feat(cli): list related tests
1 parent 17683f2 commit b8b4d7c

6 files changed

Lines changed: 197 additions & 2 deletions

File tree

docs/guide/cli.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ tests/test1.test.ts
121121
tests/test2.test.ts
122122
```
123123

124+
Use `--findRelatedTests` to list only the tests that import the specified source files:
125+
126+
```bash
127+
vitest list --findRelatedTests --filesOnly src/index.ts src/utils.ts
128+
```
129+
124130
Since Vitest 4.1, you may pass `--static-parse` to [parse test files](/api/advanced/vitest#parsespecifications) instead of running them to collect tests. Vitest parses test files with limited concurrency, defaulting to `os.availableParallelism()`. You can change it via the `--static-parse-concurrency` option.
125131

126132
### `vitest doctor`

packages/vitest/src/node/cli/cac.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,16 @@ export function createCLI(options: CliParseOptions = {}): CAC {
193193
addCliOptions(
194194
cli
195195
.command('list [...filters]', undefined, options)
196-
.action((filters, options) => collect(filters, options)),
196+
.action((filters, options) => {
197+
if (options.findRelatedTests) {
198+
const { findRelatedTests, ...cliOptions } = options
199+
return collect([], {
200+
...cliOptions,
201+
related: filters,
202+
})
203+
}
204+
return collect(filters, options)
205+
}),
197206
collectCliOptionsConfig,
198207
)
199208

@@ -254,6 +263,11 @@ export function parseCLI(argv: string | string[], config: CliParseOptions = {}):
254263
options.passWithNoTests ??= true
255264
args = []
256265
}
266+
if (arrayArgs[2] === 'list' && options.findRelatedTests) {
267+
options.related = args as string[]
268+
delete options.findRelatedTests
269+
args = []
270+
}
257271
return {
258272
filter: args as string[],
259273
options,

packages/vitest/src/node/cli/cli-api.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ export interface CliOptions extends UserConfig {
2828
* Output collected test files only
2929
*/
3030
filesOnly?: boolean
31+
/**
32+
* List only tests related to the provided source files.
33+
*/
34+
findRelatedTests?: boolean
3135
/**
3236
* Parse files statically instead of running them to collect tests
3337
* @experimental

packages/vitest/src/node/cli/cli-config.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ export type CLIOptions<Config extends object> = {
3030
}
3131

3232
type VitestCLIOptions = CLIOptions<CliOptions>
33+
type CollectCLIOptions = Omit<VitestCLIOptions, 'related'> & {
34+
findRelatedTests: CLIOption<boolean>
35+
}
3336

3437
const apiConfig: (port: number) => CLIOptions<ApiConfig> = (port: number) => ({
3538
port: {
@@ -986,6 +989,7 @@ export const cliOptionsConfig: VitestCLIOptions = {
986989
environmentOptions: null,
987990
unstubEnvs: null,
988991
related: null,
992+
findRelatedTests: null,
989993
restoreMocks: null,
990994
runner: null,
991995
mockReset: null,
@@ -1013,7 +1017,7 @@ export const cliOptionsConfig: VitestCLIOptions = {
10131017
taskTitleValueFormatTruncate: null,
10141018
}
10151019

1016-
export const collectCliOptionsConfig: VitestCLIOptions = {
1020+
export const collectCliOptionsConfig: CollectCLIOptions = {
10171021
...cliOptionsConfig,
10181022
json: {
10191023
description: 'Print collected tests as JSON or write to a file (Default: false)',
@@ -1029,6 +1033,9 @@ export const collectCliOptionsConfig: VitestCLIOptions = {
10291033
description: 'How many tests to process at the same time (default: os.availableParallelism())',
10301034
argument: '<limit>',
10311035
},
1036+
findRelatedTests: {
1037+
description: 'Print only tests related to the specified files',
1038+
},
10321039
changed: {
10331040
description: 'Print only tests that are affected by the changed files (default: `false`)',
10341041
argument: '[since]',

test/e2e/test/list-related.test.ts

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import { expect, test } from 'vitest'
2+
import { replaceRoot, runInlineTests, runVitestCli } from '#test-utils'
3+
4+
const structure = {
5+
'src/shared.ts': 'export const shared = true',
6+
'src/intermediate.ts': `
7+
export { shared } from './shared'
8+
`,
9+
'src/other.ts': 'export const other = true',
10+
'src/unrelated.ts': 'export const unrelated = true',
11+
'tests/direct.test.ts': `
12+
import { expect, test } from 'vitest'
13+
import { shared } from '../src/shared'
14+
15+
test('direct dependency', () => {
16+
expect(shared).toBe(true)
17+
})
18+
`,
19+
'tests/transitive.test.ts': `
20+
import { expect, test } from 'vitest'
21+
import { shared } from '../src/intermediate'
22+
23+
test('transitive dependency', () => {
24+
expect(shared).toBe(true)
25+
})
26+
`,
27+
'tests/other.test.ts': `
28+
import { expect, test } from 'vitest'
29+
import { other } from '../src/other'
30+
31+
test('other dependency', () => {
32+
expect(other).toBe(true)
33+
})
34+
`,
35+
'tests/unrelated.test.ts': `
36+
import { expect, test } from 'vitest'
37+
import { unrelated } from '../src/unrelated'
38+
39+
test('unrelated dependency', () => {
40+
expect(unrelated).toBe(true)
41+
})
42+
`,
43+
}
44+
45+
async function setupRelatedTests() {
46+
const result = await runInlineTests(structure)
47+
48+
expect(result.stderr).toBe('')
49+
expect(result.testTree()).toMatchInlineSnapshot(`
50+
{
51+
"tests/direct.test.ts": {
52+
"direct dependency": "passed",
53+
},
54+
"tests/other.test.ts": {
55+
"other dependency": "passed",
56+
},
57+
"tests/transitive.test.ts": {
58+
"transitive dependency": "passed",
59+
},
60+
"tests/unrelated.test.ts": {
61+
"unrelated dependency": "passed",
62+
},
63+
}
64+
`)
65+
66+
return result
67+
}
68+
69+
test('list --findRelatedTests includes direct and transitive dependents', async () => {
70+
const { root } = await setupRelatedTests()
71+
const { stdout, stderr, exitCode } = await runVitestCli(
72+
'list',
73+
`--root=${root}`,
74+
'--findRelatedTests',
75+
'src/shared.ts',
76+
)
77+
78+
expect(stderr).toBe('')
79+
expect(stdout).toMatchInlineSnapshot(`
80+
"tests/direct.test.ts > direct dependency
81+
tests/transitive.test.ts > transitive dependency
82+
"
83+
`)
84+
expect(exitCode).toBe(0)
85+
})
86+
87+
test('list --findRelatedTests combines multiple source files', async () => {
88+
const { root } = await setupRelatedTests()
89+
const { stdout, stderr, exitCode } = await runVitestCli(
90+
'list',
91+
`--root=${root}`,
92+
'--findRelatedTests',
93+
'src/shared.ts',
94+
'src/other.ts',
95+
)
96+
97+
expect(stderr).toBe('')
98+
expect(stdout).toMatchInlineSnapshot(`
99+
"tests/direct.test.ts > direct dependency
100+
tests/other.test.ts > other dependency
101+
tests/transitive.test.ts > transitive dependency
102+
"
103+
`)
104+
expect(exitCode).toBe(0)
105+
})
106+
107+
test('list --findRelatedTests supports files-only and JSON output', async () => {
108+
const { root } = await setupRelatedTests()
109+
const filesResult = await runVitestCli(
110+
'list',
111+
`--root=${root}`,
112+
'--findRelatedTests',
113+
'--filesOnly',
114+
'src/shared.ts',
115+
)
116+
const jsonResult = await runVitestCli(
117+
'list',
118+
`--root=${root}`,
119+
'--findRelatedTests',
120+
'src/shared.ts',
121+
'--json',
122+
)
123+
124+
expect(filesResult.stderr).toBe('')
125+
expect(filesResult.stdout).toMatchInlineSnapshot(`
126+
"tests/direct.test.ts
127+
tests/transitive.test.ts
128+
"
129+
`)
130+
expect(filesResult.exitCode).toBe(0)
131+
132+
expect(jsonResult.stderr).toBe('')
133+
expect(replaceRoot(jsonResult.stdout, root)).toMatchInlineSnapshot(`
134+
"[
135+
{
136+
"name": "direct dependency",
137+
"file": "<root>/tests/direct.test.ts"
138+
},
139+
{
140+
"name": "transitive dependency",
141+
"file": "<root>/tests/transitive.test.ts"
142+
}
143+
]
144+
"
145+
`)
146+
expect(jsonResult.exitCode).toBe(0)
147+
})

test/unit/test/cli-test.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,23 @@ test('public parseCLI works correctly', () => {
420420
'color': true,
421421
},
422422
})
423+
expect(parseCLI('vitest list --findRelatedTests ./source-a.ts ./source-b.ts')).toEqual({
424+
filter: [],
425+
options: {
426+
'related': ['./source-a.ts', './source-b.ts'],
427+
'--': [],
428+
'color': true,
429+
},
430+
})
431+
expect(parseCLI('vitest list --findRelatedTests --filesOnly ./source-a.ts ./source-b.ts')).toEqual({
432+
filter: [],
433+
options: {
434+
'related': ['./source-a.ts', './source-b.ts'],
435+
'filesOnly': true,
436+
'--': [],
437+
'color': true,
438+
},
439+
})
423440

424441
expect(parseCLI('vitest --coverage --browser=chrome')).toEqual({
425442
filter: [],

0 commit comments

Comments
 (0)