|
| 1 | +import type { File, Task } from '@vitest/runner' |
1 | 2 | import type { Writable } from 'node:stream' |
2 | 3 | import type { Vitest } from './core' |
| 4 | +import type { FilterObject } from './watch-filter' |
3 | 5 | import readline from 'node:readline' |
4 | | -import { getTests } from '@vitest/runner/utils' |
| 6 | +import { isTestCase } from '@vitest/runner/utils' |
5 | 7 | import { relative, resolve } from 'pathe' |
6 | 8 | import prompt from 'prompts' |
7 | 9 | import c from 'tinyrainbow' |
@@ -38,6 +40,38 @@ ${keys |
38 | 40 | ) |
39 | 41 | } |
40 | 42 |
|
| 43 | +function* traverseFilteredTestNames(parentName: string, filter: RegExp, t: Task): Generator<FilterObject> { |
| 44 | + if (isTestCase(t)) { |
| 45 | + if (t.name.match(filter)) { |
| 46 | + const displayName = `${parentName} > ${t.name}` |
| 47 | + yield { key: t.name, toString: () => displayName } |
| 48 | + } |
| 49 | + } |
| 50 | + else { |
| 51 | + parentName = parentName.length ? `${parentName} > ${t.name}` : t.name |
| 52 | + for (const task of t.tasks) { |
| 53 | + yield* traverseFilteredTestNames(parentName, filter, task) |
| 54 | + } |
| 55 | + } |
| 56 | +} |
| 57 | + |
| 58 | +function* getFilteredTestNames(pattern: string, suite: File[]): Generator<FilterObject> { |
| 59 | + try { |
| 60 | + const reg = new RegExp(pattern) |
| 61 | + // TODO: we cannot run tests per workspace yet: filtering files |
| 62 | + const files = new Set<string>() |
| 63 | + for (const file of suite) { |
| 64 | + if (!files.has(file.name)) { |
| 65 | + files.add(file.name) |
| 66 | + yield* traverseFilteredTestNames('', reg, file) |
| 67 | + } |
| 68 | + } |
| 69 | + } |
| 70 | + catch { |
| 71 | + // `new RegExp` may throw error when input is invalid regexp |
| 72 | + } |
| 73 | +} |
| 74 | + |
41 | 75 | export function registerConsoleShortcuts( |
42 | 76 | ctx: Vitest, |
43 | 77 | stdin: NodeJS.ReadStream | undefined = process.stdin, |
@@ -134,24 +168,13 @@ export function registerConsoleShortcuts( |
134 | 168 |
|
135 | 169 | async function inputNamePattern() { |
136 | 170 | off() |
137 | | - const watchFilter = new WatchFilter( |
| 171 | + const watchFilter = new WatchFilter<'object'>( |
138 | 172 | 'Input test name pattern (RegExp)', |
139 | 173 | stdin, |
140 | 174 | stdout, |
141 | 175 | ) |
142 | 176 | const filter = await watchFilter.filter((str: string) => { |
143 | | - const files = ctx.state.getFiles() |
144 | | - const tests = getTests(files) |
145 | | - try { |
146 | | - const reg = new RegExp(str) |
147 | | - return tests |
148 | | - .map(test => test.name) |
149 | | - .filter(testName => testName.match(reg)) |
150 | | - } |
151 | | - catch { |
152 | | - // `new RegExp` may throw error when input is invalid regexp |
153 | | - return [] |
154 | | - } |
| 177 | + return [...getFilteredTestNames(str, ctx.state.getFiles())] |
155 | 178 | }) |
156 | 179 |
|
157 | 180 | on() |
|
0 commit comments