diff --git a/docs/.vitepress/components.d.ts b/docs/.vitepress/components.d.ts index b509b57215f9..5863edbb5838 100644 --- a/docs/.vitepress/components.d.ts +++ b/docs/.vitepress/components.d.ts @@ -7,6 +7,7 @@ export {} /* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { + AsideViteConf: typeof import('./components/AsideViteConf.vue')['default'] Contributors: typeof import('./components/Contributors.vue')['default'] CourseLink: typeof import('./components/CourseLink.vue')['default'] FeaturesList: typeof import('./components/FeaturesList.vue')['default'] diff --git a/docs/.vitepress/components/AsideViteConf.vue b/docs/.vitepress/components/AsideViteConf.vue new file mode 100644 index 000000000000..a2c1f544679d --- /dev/null +++ b/docs/.vitepress/components/AsideViteConf.vue @@ -0,0 +1,73 @@ + + + diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index db96f5703341..e1a5110ecee1 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -9,6 +9,7 @@ import TwoslashFloatingVue from '@shikijs/vitepress-twoslash/client' import { enhanceAppWithTabs } from 'vitepress-plugin-tabs/client' import HomePage from '../components/HomePage.vue' import Version from '../components/Version.vue' +import AsideViteConf from '../components/AsideViteConf.vue' import '@shikijs/vitepress-twoslash/style.css' if (inBrowser) { @@ -20,6 +21,7 @@ export default { Layout() { return h(DefaultTheme.Layout, null, { 'home-features-after': () => h(HomePage), + 'aside-ads-before': () => h(AsideViteConf), }) }, enhanceApp({ app }) { diff --git a/docs/guide/cli.md b/docs/guide/cli.md index 9041fc80e828..ba46c7b1270f 100644 --- a/docs/guide/cli.md +++ b/docs/guide/cli.md @@ -86,6 +86,17 @@ vitest list filename.spec.ts -t="some-test" --json=./file.json If `--json` flag doesn't receive a value, it will output the JSON into stdout. +You also can pass down `--filesOnly` flag to print the test files only: + +```bash +vitest list --filesOnly +``` + +```txt +tests/test1.test.ts +tests/test2.test.ts +``` + ## Options ::: tip diff --git a/docs/public/viteconf.svg b/docs/public/viteconf.svg new file mode 100644 index 000000000000..2ca20c23f890 --- /dev/null +++ b/docs/public/viteconf.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/vite-node/package.json b/packages/vite-node/package.json index c3c5b79767cb..7edcb07c6601 100644 --- a/packages/vite-node/package.json +++ b/packages/vite-node/package.json @@ -85,11 +85,11 @@ "cac": "^6.7.14", "debug": "^4.3.6", "pathe": "^1.1.2", - "tinyrainbow": "^1.2.0", "vite": "^5.0.0" }, "devDependencies": { "@jridgewell/trace-mapping": "^0.3.25", - "@types/debug": "^4.1.12" + "@types/debug": "^4.1.12", + "tinyrainbow": "^1.2.0" } } diff --git a/packages/vitest/LICENSE.md b/packages/vitest/LICENSE.md index d54def96b44e..9f826da7d2d2 100644 --- a/packages/vitest/LICENSE.md +++ b/packages/vitest/LICENSE.md @@ -1235,6 +1235,35 @@ Repository: sindresorhus/p-locate --------------------------------------- +## package-manager-detector +License: MIT +By: Anthony Fu +Repository: git+https://github.com/antfu-collective/package-manager-detector.git + +> MIT License +> +> Copyright (c) 2020-PRESENT Anthony Fu +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all +> copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +> SOFTWARE. + +--------------------------------------- + ## picomatch License: MIT By: Jon Schlinkert diff --git a/packages/vitest/src/node/cli/cac.ts b/packages/vitest/src/node/cli/cac.ts index e2fcc55c2030..4a025df52766 100644 --- a/packages/vitest/src/node/cli/cac.ts +++ b/packages/vitest/src/node/cli/cac.ts @@ -300,24 +300,30 @@ async function collect(mode: VitestRunMode, cliFilters: string[], options: CliOp catch {} try { - const { prepareVitest, processCollected } = await import('./cli-api') + const { prepareVitest, processCollected, outputFileList } = await import('./cli-api') const ctx = await prepareVitest(mode, { ...normalizeCliOptions(options), watch: false, run: true, }) + if (!options.filesOnly) { + const { tests, errors } = await ctx.collect(cliFilters.map(normalize)) + + if (errors.length) { + console.error('\nThere were unhandled errors during test collection') + errors.forEach(e => console.error(e)) + console.error('\n\n') + await ctx.close() + return + } - const { tests, errors } = await ctx.collect(cliFilters.map(normalize)) - - if (errors.length) { - console.error('\nThere were unhandled errors during test collection') - errors.forEach(e => console.error(e)) - console.error('\n\n') - await ctx.close() - return + processCollected(ctx, tests, options) + } + else { + const files = await ctx.listFiles(cliFilters.map(normalize)) + outputFileList(files, options) } - processCollected(ctx, tests, options) await ctx.close() } catch (e) { diff --git a/packages/vitest/src/node/cli/cli-api.ts b/packages/vitest/src/node/cli/cli-api.ts index c9e3576fabe2..486bf385d047 100644 --- a/packages/vitest/src/node/cli/cli-api.ts +++ b/packages/vitest/src/node/cli/cli-api.ts @@ -1,7 +1,7 @@ /* eslint-disable no-console */ import { mkdirSync, writeFileSync } from 'node:fs' -import { dirname, resolve } from 'pathe' +import { dirname, relative, resolve } from 'pathe' import type { UserConfig as ViteUserConfig } from 'vite' import type { File, Suite, Task } from '@vitest/runner' import { CoverageProviderMap } from '../../integrations/coverage' @@ -12,6 +12,7 @@ import type { Vitest, VitestOptions } from '../core' import { FilesNotFoundError, GitNotFoundError } from '../errors' import { getNames, getTests } from '../../utils' import type { UserConfig, VitestEnvironment, VitestRunMode } from '../types/config' +import type { WorkspaceSpec } from '../pool' export interface CliOptions extends UserConfig { /** @@ -26,6 +27,10 @@ export interface CliOptions extends UserConfig { * Output collected tests as JSON or to a file */ json?: string | boolean + /** + * Output collected test files only + */ + filesOnly?: boolean } /** @@ -184,6 +189,48 @@ export function processCollected(ctx: Vitest, files: File[], options: CliOptions return formatCollectedAsString(files).forEach(test => console.log(test)) } +export function outputFileList(files: WorkspaceSpec[], options: CliOptions) { + if (typeof options.json !== 'undefined') { + return outputJsonFileList(files, options) + } + + return formatFilesAsString(files, options).map(file => console.log(file)) +} + +function outputJsonFileList(files: WorkspaceSpec[], options: CliOptions) { + if (typeof options.json === 'boolean') { + return console.log(JSON.stringify(formatFilesAsJSON(files), null, 2)) + } + if (typeof options.json === 'string') { + const jsonPath = resolve(options.root || process.cwd(), options.json) + mkdirSync(dirname(jsonPath), { recursive: true }) + writeFileSync(jsonPath, JSON.stringify(formatFilesAsJSON(files), null, 2)) + } +} + +function formatFilesAsJSON(files: WorkspaceSpec[]) { + return files.map((file) => { + const result: any = { + file: file.moduleId, + } + + if (file.project.name) { + result.projectName = file.project.name + } + return result + }) +} + +function formatFilesAsString(files: WorkspaceSpec[], options: CliOptions) { + return files.map((file) => { + let name = relative(options.root || process.cwd(), file.moduleId) + if (file.project.name) { + name = `[${file.project.name}] ${name}` + } + return name + }) +} + function processJsonOutput(files: File[], options: CliOptions) { if (typeof options.json === 'boolean') { return console.log(JSON.stringify(formatCollectedAsJSON(files), null, 2)) diff --git a/packages/vitest/src/node/cli/cli-config.ts b/packages/vitest/src/node/cli/cli-config.ts index b9bf3295262e..05b83a555a17 100644 --- a/packages/vitest/src/node/cli/cli-config.ts +++ b/packages/vitest/src/node/cli/cli-config.ts @@ -795,6 +795,7 @@ export const cliOptionsConfig: VitestCLIOptions = { outputJson: null, json: null, provide: null, + filesOnly: null, } export const benchCliOptionsConfig: Pick< @@ -813,10 +814,13 @@ export const benchCliOptionsConfig: Pick< export const collectCliOptionsConfig: Pick< VitestCLIOptions, - 'json' + 'json' | 'filesOnly' > = { json: { description: 'Print collected tests as JSON or write to a file (Default: false)', argument: '[true/path]', }, + filesOnly: { + description: 'Print only test files with out the test cases', + }, } diff --git a/packages/vitest/src/node/core.ts b/packages/vitest/src/node/core.ts index 56ccc67afe1d..53c6e5c266d9 100644 --- a/packages/vitest/src/node/core.ts +++ b/packages/vitest/src/node/core.ts @@ -374,6 +374,14 @@ export class Vitest { } } + async listFiles(filters?: string[]) { + const files = await this.filterTestsBySource( + await this.globTestFiles(filters), + ) + + return files + } + async start(filters?: string[]) { this._onClose = [] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 37643fbb1d56..f34a9091bbec 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -889,9 +889,6 @@ importers: pathe: specifier: ^1.1.2 version: 1.1.2 - tinyrainbow: - specifier: ^1.2.0 - version: 1.2.0 vite: specifier: ^5.4.0 version: 5.4.0(@types/node@20.14.15)(terser@5.22.0) @@ -902,6 +899,9 @@ importers: '@types/debug': specifier: ^4.1.12 version: 4.1.12 + tinyrainbow: + specifier: ^1.2.0 + version: 1.2.0 packages/vitest: dependencies: diff --git a/test/cli/test/list.test.ts b/test/cli/test/list.test.ts index 6fc3597cb2c2..afcaeaaafeb3 100644 --- a/test/cli/test/list.test.ts +++ b/test/cli/test/list.test.ts @@ -58,6 +58,22 @@ test('correctly outputs json', async () => { expect(exitCode).toBe(0) }) +test('correctly outputs files only json', async () => { + const { stdout, exitCode } = await runVitestCli('list', '-r=./fixtures/list', '--json', '--filesOnly') + expect(relative(stdout)).toMatchInlineSnapshot(` + "[ + { + "file": "/fixtures/list/basic.test.ts" + }, + { + "file": "/fixtures/list/math.test.ts" + } + ] + " + `) + expect(exitCode).toBe(0) +}) + test('correctly saves json', async () => { const { stdout, exitCode } = await runVitestCli('list', '-r=./fixtures/list', '--json=./list.json') onTestFinished(() => { @@ -96,6 +112,26 @@ test('correctly saves json', async () => { expect(exitCode).toBe(0) }) +test('correctly saves files only json', async () => { + const { stdout, exitCode } = await runVitestCli('list', '-r=./fixtures/list', '--json=./list.json', '--filesOnly') + onTestFinished(() => { + rmSync('./fixtures/list/list.json') + }) + const json = readFileSync('./fixtures/list/list.json', 'utf-8') + expect(stdout).toBe('') + expect(relative(json)).toMatchInlineSnapshot(` + "[ + { + "file": "/fixtures/list/basic.test.ts" + }, + { + "file": "/fixtures/list/math.test.ts" + } + ]" + `) + expect(exitCode).toBe(0) +}) + test('correctly filters by file', async () => { const { stdout, exitCode } = await runVitestCli('list', 'math.test.ts', '-r=./fixtures/list') expect(stdout).toMatchInlineSnapshot(` @@ -106,6 +142,15 @@ test('correctly filters by file', async () => { expect(exitCode).toBe(0) }) +test('correctly filters by file when using --filesOnly', async () => { + const { stdout, exitCode } = await runVitestCli('list', 'math.test.ts', '-r=./fixtures/list', '--filesOnly') + expect(stdout).toMatchInlineSnapshot(` + "math.test.ts + " + `) + expect(exitCode).toBe(0) +}) + test('correctly prints project name in basic report', async () => { const { stdout } = await runVitestCli('list', 'math.test.ts', '-r=./fixtures/list', '--config=./custom.config.ts') expect(stdout).toMatchInlineSnapshot(` @@ -115,6 +160,14 @@ test('correctly prints project name in basic report', async () => { `) }) +test('correctly prints project name in basic report when using --filesOnly', async () => { + const { stdout } = await runVitestCli('list', 'math.test.ts', '-r=./fixtures/list', '--config=./custom.config.ts', '--filesOnly') + expect(stdout).toMatchInlineSnapshot(` + "[custom] math.test.ts + " + `) +}) + test('correctly prints project name and locations in json report', async () => { const { stdout } = await runVitestCli('list', 'math.test.ts', '-r=./fixtures/list', '--json', '--config=./custom.config.ts') expect(relative(stdout)).toMatchInlineSnapshot(` @@ -142,6 +195,19 @@ test('correctly prints project name and locations in json report', async () => { `) }) +test('correctly prints project name in json report when using --filesOnly', async () => { + const { stdout } = await runVitestCli('list', 'math.test.ts', '-r=./fixtures/list', '--json', '--config=./custom.config.ts', '--filesOnly') + expect(relative(stdout)).toMatchInlineSnapshot(` + "[ + { + "file": "/fixtures/list/math.test.ts", + "projectName": "custom" + } + ] + " + `) +}) + test('correctly filters by test name', async () => { const { stdout } = await runVitestCli('list', '-t=inner', '-r=./fixtures/list') expect(stdout).toMatchInlineSnapshot(`