Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/guide/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ tests/test1.test.ts
tests/test2.test.ts
```

Use `--findRelatedTests` to list only the tests that import the specified source files:

```bash
vitest list --findRelatedTests --filesOnly src/index.ts src/utils.ts
```

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.

### `vitest doctor`
Expand Down
16 changes: 15 additions & 1 deletion packages/vitest/src/node/cli/cac.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,16 @@ export function createCLI(options: CliParseOptions = {}): CAC {
addCliOptions(
cli
.command('list [...filters]', undefined, options)
.action((filters, options) => collect(filters, options)),
.action((filters, options) => {
if (options.findRelatedTests) {
const { findRelatedTests, ...cliOptions } = options
return collect([], {
...cliOptions,
related: filters,
})
}
return collect(filters, options)
}),
collectCliOptionsConfig,
)

Expand Down Expand Up @@ -254,6 +263,11 @@ export function parseCLI(argv: string | string[], config: CliParseOptions = {}):
options.passWithNoTests ??= true
args = []
}
if (arrayArgs[2] === 'list' && options.findRelatedTests) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Half the changes in this PR would be removed if you just named it related. Why do we need to follow jest naming?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to go this way, it was just a strong inspiration
Working on it

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You were right. Renaming it to related removed the separate Jest-inspired option and its API/config plumbing. The remaining handling is needed because, for list, --related is a boolean flag while its positional arguments must be converted into the existing related: string[] runtime option, rather than treated as test filters

What do you think about this? Do you recommend I follow a different approach?

options.related = args as string[]
delete options.findRelatedTests
args = []
}
return {
filter: args as string[],
options,
Expand Down
4 changes: 4 additions & 0 deletions packages/vitest/src/node/cli/cli-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export interface CliOptions extends UserConfig {
* Output collected test files only
*/
filesOnly?: boolean
/**
* List only tests related to the provided source files.
*/
findRelatedTests?: boolean
/**
* Parse files statically instead of running them to collect tests
* @experimental
Expand Down
4 changes: 4 additions & 0 deletions packages/vitest/src/node/cli/cli-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,7 @@ export const cliOptionsConfig: VitestCLIOptions = {
environmentOptions: null,
unstubEnvs: null,
related: null,
findRelatedTests: null,
restoreMocks: null,
runner: null,
mockReset: null,
Expand Down Expand Up @@ -1029,6 +1030,9 @@ export const collectCliOptionsConfig: VitestCLIOptions = {
description: 'How many tests to process at the same time (default: os.availableParallelism())',
argument: '<limit>',
},
findRelatedTests: {
description: 'Print only tests related to the specified files',
},
changed: {
description: 'Print only tests that are affected by the changed files (default: `false`)',
argument: '[since]',
Expand Down
147 changes: 147 additions & 0 deletions test/e2e/test/list-related.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { expect, test } from 'vitest'
import { replaceRoot, runInlineTests, runVitestCli } from '#test-utils'

const structure = {
'src/shared.ts': 'export const shared = true',
'src/intermediate.ts': `
export { shared } from './shared'
`,
'src/other.ts': 'export const other = true',
'src/unrelated.ts': 'export const unrelated = true',
'tests/direct.test.ts': `
import { expect, test } from 'vitest'
import { shared } from '../src/shared'

test('direct dependency', () => {
expect(shared).toBe(true)
})
`,
'tests/transitive.test.ts': `
import { expect, test } from 'vitest'
import { shared } from '../src/intermediate'

test('transitive dependency', () => {
expect(shared).toBe(true)
})
`,
'tests/other.test.ts': `
import { expect, test } from 'vitest'
import { other } from '../src/other'

test('other dependency', () => {
expect(other).toBe(true)
})
`,
'tests/unrelated.test.ts': `
import { expect, test } from 'vitest'
import { unrelated } from '../src/unrelated'

test('unrelated dependency', () => {
expect(unrelated).toBe(true)
})
`,
}

async function setupRelatedTests() {
const result = await runInlineTests(structure)

expect(result.stderr).toBe('')
expect(result.testTree()).toMatchInlineSnapshot(`
{
"tests/direct.test.ts": {
"direct dependency": "passed",
},
"tests/other.test.ts": {
"other dependency": "passed",
},
"tests/transitive.test.ts": {
"transitive dependency": "passed",
},
"tests/unrelated.test.ts": {
"unrelated dependency": "passed",
},
}
`)

return result
}

test('list --findRelatedTests includes direct and transitive dependents', async () => {
const { root } = await setupRelatedTests()
const { stdout, stderr, exitCode } = await runVitestCli(
'list',
`--root=${root}`,
'--findRelatedTests',
'src/shared.ts',
)

expect(stderr).toBe('')
expect(stdout).toMatchInlineSnapshot(`
"tests/direct.test.ts > direct dependency
tests/transitive.test.ts > transitive dependency
"
`)
expect(exitCode).toBe(0)
})

test('list --findRelatedTests combines multiple source files', async () => {
const { root } = await setupRelatedTests()
const { stdout, stderr, exitCode } = await runVitestCli(
'list',
`--root=${root}`,
'--findRelatedTests',
'src/shared.ts',
'src/other.ts',
)

expect(stderr).toBe('')
expect(stdout).toMatchInlineSnapshot(`
"tests/direct.test.ts > direct dependency
tests/other.test.ts > other dependency
tests/transitive.test.ts > transitive dependency
"
`)
expect(exitCode).toBe(0)
})

test('list --findRelatedTests supports files-only and JSON output', async () => {
const { root } = await setupRelatedTests()
const filesResult = await runVitestCli(
'list',
`--root=${root}`,
'--findRelatedTests',
'--filesOnly',
'src/shared.ts',
)
const jsonResult = await runVitestCli(
'list',
`--root=${root}`,
'--findRelatedTests',
'src/shared.ts',
'--json',
)

expect(filesResult.stderr).toBe('')
expect(filesResult.stdout).toMatchInlineSnapshot(`
"tests/direct.test.ts
tests/transitive.test.ts
"
`)
expect(filesResult.exitCode).toBe(0)

expect(jsonResult.stderr).toBe('')
expect(replaceRoot(jsonResult.stdout, root)).toMatchInlineSnapshot(`
"[
{
"name": "direct dependency",
"file": "<root>/tests/direct.test.ts"
},
{
"name": "transitive dependency",
"file": "<root>/tests/transitive.test.ts"
}
]
"
`)
expect(jsonResult.exitCode).toBe(0)
})
17 changes: 17 additions & 0 deletions test/unit/test/cli-test.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,23 @@ test('public parseCLI works correctly', () => {
'color': true,
},
})
expect(parseCLI('vitest list --findRelatedTests ./source-a.ts ./source-b.ts')).toEqual({
filter: [],
options: {
'related': ['./source-a.ts', './source-b.ts'],
'--': [],
'color': true,
},
})
expect(parseCLI('vitest list --findRelatedTests --filesOnly ./source-a.ts ./source-b.ts')).toEqual({
filter: [],
options: {
'related': ['./source-a.ts', './source-b.ts'],
'filesOnly': true,
'--': [],
'color': true,
},
})

expect(parseCLI('vitest --coverage --browser=chrome')).toEqual({
filter: [],
Expand Down
Loading