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 @@
+
+
+
+
+
+ ViteConf 24 - Oct 3
+
+
+
+
+
+
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(`