Skip to content

Commit

Permalink
feat: add --project option to limit what projects are running (#4561)
Browse files Browse the repository at this point in the history
Co-authored-by: Anjorin Damilare <damilareanjorin1@gmail.com>
Co-authored-by: Ari Perkkiö <ari.perkkio@gmail.com>
  • Loading branch information
3 people committed Nov 24, 2023
1 parent edb0d28 commit 58ef51a
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 17 deletions.
1 change: 1 addition & 0 deletions docs/guide/cli.md
Expand Up @@ -100,6 +100,7 @@ Run only [benchmark](https://vitest.dev/guide/features.html#benchmarking-experim
| `--typecheck [options]` | Custom options for typecheck pool. If passed without options, enables typechecking |
| `--typecheck.enabled` | Enable typechecking alongside tests (default: `false`) |
| `--typecheck.only` | Run only typecheck tests. This automatically enables typecheck (default: `false`) |
| `--project` | The name of the project to run if you are using Vitest workspace feature. This can be repeated for multiple projects: `--project=1 --project=2` |
| `-h, --help` | Display available CLI options |

::: tip
Expand Down
24 changes: 12 additions & 12 deletions docs/guide/index.md
Expand Up @@ -19,18 +19,18 @@ You can try Vitest online on [StackBlitz](https://vitest.new). It runs Vitest di
<CourseLink href="https://vueschool.io/lessons/how-to-install-vitest?friend=vueuse">Learn how to install by Video</CourseLink>

::: code-group
```bash [npm]
npm install -D vitest
```
```bash [yarn]
yarn add -D vitest
```
```bash [pnpm]
pnpm add -D vitest
```
```bash [bun]
bun add -D vitest
```
```bash [npm]
npm install -D vitest
```
```bash [yarn]
yarn add -D vitest
```
```bash [pnpm]
pnpm add -D vitest
```
```bash [bun]
bun add -D vitest
```
:::

:::tip
Expand Down
43 changes: 43 additions & 0 deletions docs/guide/workspace.md
Expand Up @@ -99,6 +99,49 @@ export default defineProject({
```
:::

## Running tests

To run tests inside the workspace, define a script in your root `package.json`:

```json
{
"scripts": {
"test": "vitest"
}
}
```

Now tests can be run using your package manager:

::: code-group
```bash [npm]
npm run test
```
```bash [yarn]
yarn test
```
```bash [pnpm]
pnpm run test
```
```bash [bun]
bun test
```
:::

If you need to run tests only inside a single project, use the `--project` CLI option:

```bash
npm run test --project e2e
```

::: tip
CLI option `--project` can be used multiple times to filter out several projects:

```bash
npm run test --project e2e --project unit
```
:::

## Configuration

None of the configuration options are inherited from the root-level config file. You can create a shared config file and merge it with the project config yourself:
Expand Down
1 change: 1 addition & 0 deletions packages/vitest/src/node/cli.ts
Expand Up @@ -55,6 +55,7 @@ cli
.option('--typecheck [options]', 'Custom options for typecheck pool')
.option('--typecheck.enabled', 'Enable typechecking alongside tests (default: false)')
.option('--typecheck.only', 'Run only typecheck tests. This automatically enables typecheck (default: false)')
.option('--project <name>', 'The name of the project to run if you are using Vitest workspace feature. This can be repeated for multiple projects: --project=1 --project=2')
.help()

cli
Expand Down
19 changes: 18 additions & 1 deletion packages/vitest/src/node/core.ts
Expand Up @@ -56,6 +56,7 @@ export class Vitest {

private coreWorkspaceProject!: WorkspaceProject

private resolvedProjects: WorkspaceProject[] = []
public projects: WorkspaceProject[] = []
private projectsTestFiles = new Map<string, Set<WorkspaceProject>>()

Expand Down Expand Up @@ -151,7 +152,12 @@ export class Vitest {

await Promise.all(this._onSetServer.map(fn => fn()))

this.projects = await this.resolveWorkspace(cliOptions)
const projects = await this.resolveWorkspace(cliOptions)
this.projects = projects
this.resolvedProjects = projects
const filteredProjects = toArray(resolved.project)
if (filteredProjects.length)
this.projects = this.projects.filter(p => filteredProjects.includes(p.getName()))
if (!this.coreWorkspaceProject)
this.coreWorkspaceProject = WorkspaceProject.createBasicProject(this)

Expand Down Expand Up @@ -512,6 +518,17 @@ export class Vitest {
await this.report('onWatcherStart', this.state.getFiles(files))
}

async changeProjectName(pattern: string) {
if (pattern === '')
delete this.configOverride.project
else
this.configOverride.project = pattern

this.projects = this.resolvedProjects.filter(p => p.getName() === pattern)
const files = (await this.globTestFiles()).map(([, file]) => file)
await this.rerunFiles(files, 'change project filter')
}

async changeNamePattern(pattern: string, files: string[] = this.state.getFilepaths(), trigger?: string) {
// Empty test name pattern should reset filename pattern as well
if (pattern === '')
Expand Down
4 changes: 4 additions & 0 deletions packages/vitest/src/node/logger.ts
Expand Up @@ -3,6 +3,7 @@ import c from 'picocolors'
import { version } from '../../../../package.json'
import type { ErrorWithDiff } from '../types'
import type { TypeCheckError } from '../typecheck/typechecker'
import { toArray } from '../utils'
import { divider } from './reporters/renderers/utils'
import { RandomSequencer } from './sequencers/RandomSequencer'
import type { Vitest } from './core'
Expand Down Expand Up @@ -95,6 +96,9 @@ export class Logger {
const comma = c.dim(', ')
if (filters?.length)
this.console.error(c.dim('filter: ') + c.yellow(filters.join(comma)))
const projectsFilter = toArray(config.project)
if (projectsFilter.length)
this.console.error(c.dim('projects: ') + c.yellow(projectsFilter.join(comma)))
this.ctx.projects.forEach((project) => {
const config = project.config
const name = project.getName()
Expand Down
9 changes: 5 additions & 4 deletions packages/vitest/src/node/reporters/base.ts
@@ -1,7 +1,7 @@
import { performance } from 'node:perf_hooks'
import c from 'picocolors'
import type { ErrorWithDiff, File, Reporter, Task, TaskResultPack, UserConsoleLog } from '../../types'
import { getFullName, getSafeTimers, getSuites, getTests, hasFailed, hasFailedSnapshot, isCI, isNode, relativePath } from '../../utils'
import { getFullName, getSafeTimers, getSuites, getTests, hasFailed, hasFailedSnapshot, isCI, isNode, relativePath, toArray } from '../../utils'
import type { Vitest } from '../../node'
import { F_RIGHT } from '../../utils/figures'
import { UNKNOWN_TEST_ID } from '../../runtime/console'
Expand Down Expand Up @@ -169,16 +169,17 @@ export abstract class BaseReporter implements Reporter {
const TRIGGER = trigger ? c.dim(` ${this.relative(trigger)}`) : ''
const FILENAME_PATTERN = this.ctx.filenamePattern ? `${BADGE_PADDING} ${c.dim('Filename pattern: ')}${c.blue(this.ctx.filenamePattern)}\n` : ''
const TESTNAME_PATTERN = this.ctx.configOverride.testNamePattern ? `${BADGE_PADDING} ${c.dim('Test name pattern: ')}${c.blue(String(this.ctx.configOverride.testNamePattern))}\n` : ''
const PROJECT_FILTER = this.ctx.configOverride.project ? `${BADGE_PADDING} ${c.dim('Project name: ')}${c.blue(toArray(this.ctx.configOverride.project).join(', '))}\n` : ''

if (files.length > 1) {
if (files.length > 1 || !files.length) {
// we need to figure out how to handle rerun all from stdin
this.ctx.logger.clearFullScreen(`\n${BADGE}${TRIGGER}\n${FILENAME_PATTERN}${TESTNAME_PATTERN}`)
this.ctx.logger.clearFullScreen(`\n${BADGE}${TRIGGER}\n${PROJECT_FILTER}${FILENAME_PATTERN}${TESTNAME_PATTERN}`)
this._lastRunCount = 0
}
else if (files.length === 1) {
const rerun = this._filesInWatchMode.get(files[0]) ?? 1
this._lastRunCount = rerun
this.ctx.logger.clearFullScreen(`\n${BADGE}${TRIGGER} ${c.blue(`x${rerun}`)}\n${FILENAME_PATTERN}${TESTNAME_PATTERN}`)
this.ctx.logger.clearFullScreen(`\n${BADGE}${TRIGGER} ${c.blue(`x${rerun}`)}\n${PROJECT_FILTER}${FILENAME_PATTERN}${TESTNAME_PATTERN}`)
}

this._timeStart = new Date()
Expand Down
17 changes: 17 additions & 0 deletions packages/vitest/src/node/stdin.ts
Expand Up @@ -2,6 +2,7 @@ import readline from 'node:readline'
import c from 'picocolors'
import prompt from 'prompts'
import { isWindows, stdout } from '../utils'
import { toArray } from '../utils/base'
import type { Vitest } from './core'

const keys = [
Expand All @@ -11,6 +12,7 @@ const keys = [
['u', 'update snapshot'],
['p', 'filter by a filename'],
['t', 'filter by a test name regex pattern'],
['w', 'filter by a project name'],
['q', 'quit'],
]
const cancelKeys = ['space', 'c', 'h', ...keys.map(key => key[0]).flat()]
Expand Down Expand Up @@ -76,6 +78,9 @@ export function registerConsoleShortcuts(ctx: Vitest) {
// rerun only failed tests
if (name === 'f')
return ctx.rerunFailed()
// change project filter
if (name === 'w')
return inputProjectName()
// change testNamePattern
if (name === 't')
return inputNamePattern()
Expand All @@ -100,6 +105,18 @@ export function registerConsoleShortcuts(ctx: Vitest) {
await ctx.changeNamePattern(filter.trim(), undefined, 'change pattern')
}

async function inputProjectName() {
off()
const { filter = '' }: { filter: string } = await prompt([{
name: 'filter',
type: 'text',
message: 'Input a single project name',
initial: toArray(ctx.configOverride.project)[0] || '',
}])
on()
await ctx.changeProjectName(filter.trim())
}

async function inputFilePattern() {
off()
const { filter = '' }: { filter: string } = await prompt([{
Expand Down
5 changes: 5 additions & 0 deletions packages/vitest/src/types/config.ts
Expand Up @@ -716,6 +716,11 @@ export interface UserConfig extends InlineConfig {
* @example --shard=2/3
*/
shard?: string

/**
* Name of the project or projects to run.
*/
project?: string | string[]
}

export interface ResolvedConfig extends Omit<Required<UserConfig>, 'config' | 'filters' | 'browser' | 'coverage' | 'testNamePattern' | 'related' | 'api' | 'reporters' | 'resolveSnapshotPath' | 'benchmark' | 'shard' | 'cache' | 'sequence' | 'typecheck' | 'runner' | 'poolOptions' | 'pool'> {
Expand Down

0 comments on commit 58ef51a

Please sign in to comment.