From f0c903500b214559c6355f0679b3b8e32b106c04 Mon Sep 17 00:00:00 2001 From: Andrew Haines Date: Mon, 18 Nov 2024 11:52:57 +0000 Subject: [PATCH 1/3] feat(cli): support excluding projects with `--project=!pattern` --- docs/guide/cli-generated.md | 2 +- packages/vitest/src/node/cli/cli-config.ts | 2 +- packages/vitest/src/utils/base.ts | 17 +++++++++++++---- test/config/test/project.test.ts | 3 +++ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/guide/cli-generated.md b/docs/guide/cli-generated.md index 80e1343818e3..27d7d0160e3b 100644 --- a/docs/guide/cli-generated.md +++ b/docs/guide/cli-generated.md @@ -752,7 +752,7 @@ Path to a custom tsconfig file - **CLI:** `--project ` - **Config:** [project](/config/#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`. You can also filter projects using wildcards like `--project=packages*` +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`. You can also filter projects using wildcards like `--project=packages*`, and exclude projects with `--project=!pattern`. ### slowTestThreshold diff --git a/packages/vitest/src/node/cli/cli-config.ts b/packages/vitest/src/node/cli/cli-config.ts index 3d4f9e31961f..ac67b8ba7bf4 100644 --- a/packages/vitest/src/node/cli/cli-config.ts +++ b/packages/vitest/src/node/cli/cli-config.ts @@ -700,7 +700,7 @@ export const cliOptionsConfig: VitestCLIOptions = { }, project: { description: - '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`. You can also filter projects using wildcards like `--project=packages*`', + '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`. You can also filter projects using wildcards like `--project=packages*`, and exclude projects with `--project=!pattern`.', argument: '', array: true, }, diff --git a/packages/vitest/src/utils/base.ts b/packages/vitest/src/utils/base.ts index 25a9fa7a445d..7e02e0a20597 100644 --- a/packages/vitest/src/utils/base.ts +++ b/packages/vitest/src/utils/base.ts @@ -24,8 +24,17 @@ export function escapeRegExp(s: string) { } export function wildcardPatternToRegExp(pattern: string): RegExp { - return new RegExp( - `^${pattern.split('*').map(escapeRegExp).join('.*')}$`, - 'i', - ) + const negated = pattern.startsWith('!') + + if (negated) { + pattern = pattern.slice(1) + } + + let regexp = `${pattern.split('*').map(escapeRegExp).join('.*')}$` + + if (negated) { + regexp = `(?!${regexp})` + } + + return new RegExp(`^${regexp}`, 'i') } diff --git a/test/config/test/project.test.ts b/test/config/test/project.test.ts index 9506ec7c2ce1..a37514715cdb 100644 --- a/test/config/test/project.test.ts +++ b/test/config/test/project.test.ts @@ -7,6 +7,9 @@ test.each([ { pattern: '*j*', expected: ['project_1', 'project_2'] }, { pattern: 'project*', expected: ['project_1', 'project_2'] }, { pattern: 'space*', expected: ['space_1'] }, + { pattern: '!project_1', expected: ['project_2', 'space_1'] }, + { pattern: '!project*', expected: ['space_1'] }, + { pattern: '!project', expected: ['project_1', 'project_2', 'space_1'] }, ])('should match projects correctly: $pattern', async ({ pattern, expected }) => { const { stdout, stderr } = await runVitest({ root: 'fixtures/project', From 1232a51bfae050d9277659d8ea81ca5742dc94c0 Mon Sep 17 00:00:00 2001 From: Andrew Haines Date: Mon, 18 Nov 2024 15:36:28 +0000 Subject: [PATCH 2/3] test: fix project filtering tests --- test/config/test/project.test.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/config/test/project.test.ts b/test/config/test/project.test.ts index a37514715cdb..e300a8723867 100644 --- a/test/config/test/project.test.ts +++ b/test/config/test/project.test.ts @@ -11,14 +11,11 @@ test.each([ { pattern: '!project*', expected: ['space_1'] }, { pattern: '!project', expected: ['project_1', 'project_2', 'space_1'] }, ])('should match projects correctly: $pattern', async ({ pattern, expected }) => { - const { stdout, stderr } = await runVitest({ + const { ctx } = await runVitest({ root: 'fixtures/project', reporters: ['basic'], project: pattern, }) - expect(stderr).toBeFalsy() - expect(stdout).toBeTruthy() - - expected.forEach(name => expect(stdout).toContain(name)) + expect(ctx?.projects.map(p => p.name).sort()).toEqual(expected) }) From f1250b40c984c4bf88dfe1a03f741e5d3b6b8b38 Mon Sep 17 00:00:00 2001 From: Andrew Haines Date: Mon, 18 Nov 2024 17:37:13 +0000 Subject: [PATCH 3/3] test: verify contents of stderr and stdout --- test/config/test/project.test.ts | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/test/config/test/project.test.ts b/test/config/test/project.test.ts index e300a8723867..fd66166016d3 100644 --- a/test/config/test/project.test.ts +++ b/test/config/test/project.test.ts @@ -1,21 +1,35 @@ import { expect, test } from 'vitest' import { runVitest } from '../../test-utils' +const allProjects = ['project_1', 'project_2', 'space_1'] + test.each([ { pattern: 'project_1', expected: ['project_1'] }, - { pattern: '*', expected: ['project_1', 'project_2', 'space_1'] }, + { pattern: '*', expected: allProjects }, { pattern: '*j*', expected: ['project_1', 'project_2'] }, { pattern: 'project*', expected: ['project_1', 'project_2'] }, { pattern: 'space*', expected: ['space_1'] }, { pattern: '!project_1', expected: ['project_2', 'space_1'] }, { pattern: '!project*', expected: ['space_1'] }, - { pattern: '!project', expected: ['project_1', 'project_2', 'space_1'] }, + { pattern: '!project', expected: allProjects }, ])('should match projects correctly: $pattern', async ({ pattern, expected }) => { - const { ctx } = await runVitest({ + const { ctx, stderr, stdout } = await runVitest({ root: 'fixtures/project', reporters: ['basic'], project: pattern, }) + expect(stderr).toBeFalsy() + expect(stdout).toBeTruthy() + + for (const project of allProjects) { + if (expected.includes(project)) { + expect(stdout).toContain(project) + } + else { + expect(stdout).not.toContain(project) + } + } + expect(ctx?.projects.map(p => p.name).sort()).toEqual(expected) })