Skip to content

Commit c793a13

Browse files
authoredFeb 20, 2024··
feat(vitest): expose parseCLI method (#5248)
1 parent 28f22b9 commit c793a13

File tree

4 files changed

+119
-8
lines changed

4 files changed

+119
-8
lines changed
 

‎docs/advanced/api.md

+10
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@ const vitest = await createVitest('test', {
4646
})
4747
```
4848
49+
## parseCLI
50+
51+
You can use this method to parse CLI arguments. It accepts a string (where arguments are split by a single space) or a strings array of CLI arguments in the same format that Vitest CLI uses. It returns a filter and `options` that you can later pass down to `createVitest` or `startVitest` methods.
52+
53+
```ts
54+
import { parseCLI } from 'vitest/node'
55+
56+
parseCLI('vitest ./files.ts --coverage --browser=chrome')
57+
```
58+
4959
## Vitest
5060
5161
Vitest instance requires the current test mode. It can be either:

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

+38-7
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ function addCommand(cli: CAC, name: string, option: CLIOption<any>) {
5454
}
5555
}
5656

57-
export function createCLI() {
57+
interface CLIOptions {
58+
allowUnknownOptions?: boolean
59+
}
60+
61+
export function createCLI(options: CLIOptions = {}) {
5862
const cli = cac('vitest')
5963

6064
cli
@@ -141,23 +145,23 @@ export function createCLI() {
141145
})
142146

143147
cli
144-
.command('run [...filters]')
148+
.command('run [...filters]', undefined, options)
145149
.action(run)
146150

147151
cli
148-
.command('related [...filters]')
152+
.command('related [...filters]', undefined, options)
149153
.action(runRelated)
150154

151155
cli
152-
.command('watch [...filters]')
156+
.command('watch [...filters]', undefined, options)
153157
.action(watch)
154158

155159
cli
156-
.command('dev [...filters]')
160+
.command('dev [...filters]', undefined, options)
157161
.action(watch)
158162

159163
cli
160-
.command('bench [...filters]')
164+
.command('bench [...filters]', undefined, options)
161165
.action(benchmark)
162166

163167
// TODO: remove in Vitest 2.0
@@ -168,12 +172,39 @@ export function createCLI() {
168172
})
169173

170174
cli
171-
.command('[...filters]')
175+
.command('[...filters]', undefined, options)
172176
.action((filters, options) => start('test', filters, options))
173177

174178
return cli
175179
}
176180

181+
export function parseCLI(argv: string | string[], config: CLIOptions = {}): {
182+
filter: string[]
183+
options: CliOptions
184+
} {
185+
const arrayArgs = typeof argv === 'string' ? argv.split(' ') : argv
186+
if (arrayArgs[0] !== 'vitest')
187+
throw new Error(`Expected "vitest" as the first argument, received "${arrayArgs[0]}"`)
188+
arrayArgs[0] = '/index.js'
189+
arrayArgs.unshift('node')
190+
let { args, options } = createCLI(config).parse(arrayArgs, {
191+
run: false,
192+
})
193+
if (arrayArgs[2] === 'watch' || arrayArgs[2] === 'dev')
194+
options.watch = true
195+
if (arrayArgs[2] === 'run')
196+
options.run = true
197+
if (arrayArgs[2] === 'related') {
198+
options.related = args
199+
options.passWithNoTests ??= true
200+
args = []
201+
}
202+
return {
203+
filter: args as string[],
204+
options,
205+
}
206+
}
207+
177208
async function runRelated(relatedFiles: string[] | string, argv: CliOptions): Promise<void> {
178209
argv.related = relatedFiles
179210
argv.passWithNoTests ??= true

‎packages/vitest/src/node/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export type { WorkspaceProject } from './workspace'
33
export { createVitest } from './create'
44
export { VitestPlugin } from './plugins'
55
export { startVitest } from './cli/cli-api'
6+
export { parseCLI } from './cli/cac'
67
export { registerConsoleShortcuts } from './stdin'
78
export type { GlobalSetupContext } from './globalSetup'
89
export type { WorkspaceSpec, ProcessPool } from './pool'

‎test/core/test/cli-test.test.ts

+70-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect, test } from 'vitest'
2-
import { createCLI } from '../../../packages/vitest/src/node/cli/cac.js'
2+
import { createCLI, parseCLI } from '../../../packages/vitest/src/node/cli/cac.js'
33

44
const vitestCli = createCLI()
55

@@ -253,3 +253,72 @@ test('browser by name', () => {
253253
expect(args).toEqual([])
254254
expect(options).toEqual({ browser: { enabled: true, name: 'firefox' } })
255255
})
256+
257+
test('public parseCLI works correctly', () => {
258+
expect(parseCLI('vitest dev')).toEqual({
259+
filter: [],
260+
options: {
261+
'watch': true,
262+
'--': [],
263+
'color': true,
264+
},
265+
})
266+
expect(parseCLI('vitest watch')).toEqual({
267+
filter: [],
268+
options: {
269+
'watch': true,
270+
'--': [],
271+
'color': true,
272+
},
273+
})
274+
expect(parseCLI('vitest run')).toEqual({
275+
filter: [],
276+
options: {
277+
'run': true,
278+
'--': [],
279+
'color': true,
280+
},
281+
})
282+
expect(parseCLI('vitest related ./some-files.js')).toEqual({
283+
filter: [],
284+
options: {
285+
'passWithNoTests': true,
286+
'related': ['./some-files.js'],
287+
'--': [],
288+
'color': true,
289+
},
290+
})
291+
292+
expect(parseCLI('vitest --coverage --browser=chrome')).toEqual({
293+
filter: [],
294+
options: {
295+
'coverage': { enabled: true },
296+
'browser': { enabled: true, name: 'chrome' },
297+
'--': [],
298+
'color': true,
299+
},
300+
})
301+
302+
expect(parseCLI('vitest ./tests.js --coverage')).toEqual({
303+
filter: ['./tests.js'],
304+
options: {
305+
'coverage': { enabled: true },
306+
'--': [],
307+
'color': true,
308+
},
309+
})
310+
311+
expect(parseCLI('vitest ./tests.js --coverage --custom-options', { allowUnknownOptions: true })).toEqual({
312+
filter: ['./tests.js'],
313+
options: {
314+
'coverage': { enabled: true },
315+
'customOptions': true,
316+
'--': [],
317+
'color': true,
318+
},
319+
})
320+
321+
expect(() => {
322+
parseCLI('node --test --coverage --browser --typecheck')
323+
}).toThrowError(`Expected "vitest" as the first argument, received "node"`)
324+
})

0 commit comments

Comments
 (0)
Please sign in to comment.