diff --git a/api/advanced/runner.md b/api/advanced/runner.md index b7279c69..b820da6c 100644 --- a/api/advanced/runner.md +++ b/api/advanced/runner.md @@ -145,7 +145,7 @@ export default class Runner { ::: ::: tip -快照支持和其他功能是依赖于测试运行器的。如果你想保留这些功能,可以从 `vitest/runners` 导入 `VitestTestRunner` 并将你的测试运行器继承该类。如果你想扩展基准测试功能,它还提供了 `NodeBenchmarkRunner`。 +快照支持和其他功能是依赖于测试运行器的。如果你想保留这些功能,可以从 `vitest` 导入 `TestRunner` 并将你的测试运行器继承该类。如果你想扩展基准测试功能,它还提供了 `NodeBenchmarkRunner`。 ::: ## Tasks {#tasks} diff --git a/api/browser/locators.md b/api/browser/locators.md index 1ee3c653..6929c8ce 100644 --- a/api/browser/locators.md +++ b/api/browser/locators.md @@ -1052,6 +1052,56 @@ function all(): Locator[] 在内部,此方法调用 `.elements` 并使用 [`page.elementLocator`](/api/browser/context#page) 包装每个元素。 - [更多内容请参阅 `locator.elements()`](#elements) + +### serialize + +```ts +function serialize(): SerializedLocator +``` + +Returns a JSON-serializable representation of the locator. The returned object has two fields: + +- [`selector`](#selector): the provider-specific selector string used to query the element at runtime. +- `locator`: a human-readable description of the locator (e.g. `getByRole('button')`), used for error messages and tracing. Equivalent to calling [`asLocator()`](#aslocator). + +This is primarily intended for forwarding a locator to a [browser command](/api/browser/commands), which runs in Node and cannot receive a live `Locator` instance: + +```ts +import { commands, page } from 'vitest/browser' + +await commands.myCommand(page.getByRole('button').serialize()) +``` + +::: tip +Vitest automatically serializes any `Locator` argument passed to a command, so calling `serialize()` explicitly is rarely necessary. You can also use `JSON.stringify(locator)` (it calls [`toJSON`](#tojson) internally), which produces the same result. +::: + +### toJSON + +```ts +function toJSON(): SerializedLocator +``` + +Alias of [`serialize`](#serialize). Defined so that `JSON.stringify(locator)` and structured-clone-based transports return a `SerializedLocator` object. + +### asLocator + +```ts +function asLocator(): string +``` + +Returns a human-readable description of the locator using the JavaScript locator syntax (e.g. `getByRole('button', { name: 'Submit' })`). This is the same string exposed as the `locator` field of [`serialize()`](#serialize) and is used in error messages and traces. + +```ts +import { page } from 'vitest/browser' + +const button = page.getByRole('button', { name: 'Submit' }) +button.asLocator() // "getByRole('button', { name: 'Submit' })" +``` + +::: tip +Use [`selector`](#selector) when you need the provider-specific string to forward to a [browser command](/api/browser/commands). Use `asLocator()` only for diagnostic output. The returned string is not meant to be re-used to query elements. +::: ## Properties @@ -1064,8 +1114,9 @@ function all(): Locator[] ```ts [commands.ts] import type { BrowserCommand } from 'vitest/node' +import type { SerializedLocator } from '@vitest/browser' -const test: BrowserCommand = function test(context, selector) { +const test: BrowserCommand = function test(context, { selector }) { // playwright await context.iframe.locator(selector).click() // webdriverio @@ -1079,7 +1130,7 @@ import { commands, page } from 'vitest/browser' test('works correctly', async () => { await commands.test(page.getByText('Hello').selector) // ✅ - // Vitest 会自动将其解包为字符串 + // Vitest 会自动将其解包为 SerializedLocator 对象 await commands.test(page.getByText('Hello')) // ✅ }) ``` diff --git a/api/describe.md b/api/describe.md index 6ab2652a..6b358cec 100644 --- a/api/describe.md +++ b/api/describe.md @@ -207,6 +207,19 @@ describe.concurrent('suite', () => { test.concurrent('concurrent test 3', async () => { /* ... */ }) }) ``` + +Set `concurrent` to `false` to opt out of concurrency inherited from a parent suite or [`sequence.concurrent`](/config/sequence#sequence-concurrent): + +```ts +describe.concurrent('suite', () => { + test('concurrent test', async () => { /* ... */ }) + + describe('sequential suite', { concurrent: false }, () => { + test('sequential test 1', async () => { /* ... */ }) + test('sequential test 2', async () => { /* ... */ }) + }) +}) +``` `.skip`、`.only` 和 `.todo` 适用于并发测试套件。以下所有组合都有效: @@ -230,26 +243,6 @@ describe.concurrent('suite', () => { }) ``` -## describe.sequential - -- **别名:** `suite.sequential` - -测试套件中的 `describe.sequential` 会将所有测试标记为顺序执行。该特性适用于需要在 `describe.concurrent` 并发测试套件中按顺序运行测试,或使用 `--sequence.concurrent` 命令行选项。 - -```ts -import { describe, test } from 'vitest' - -describe.concurrent('suite', () => { - test('concurrent test 1', async () => { /* ... */ }) - test('concurrent test 2', async () => { /* ... */ }) - - describe.sequential('', () => { - test('sequential test 1', async () => { /* ... */ }) - test('sequential test 2', async () => { /* ... */ }) - }) -}) -``` - ## describe.shuffle - **别名:** `suite.shuffle` diff --git a/api/hooks.md b/api/hooks.md index ac85057d..18d21bdd 100644 --- a/api/hooks.md +++ b/api/hooks.md @@ -34,8 +34,8 @@ beforeEach(async () => { ``` 此处,`beforeEach` 确保每个测试都会添加用户。 - -`beforeEach` 还可以返回一个可选的清理函数(等价于 [`afterEach`](#aftereach)): + +`beforeEach` can also return an optional cleanup function. It's similar to [`afterEach`](#aftereach). The only difference is that it's executed after all other `afterEach` hooks: ```ts import { beforeEach } from 'vitest' @@ -44,7 +44,7 @@ beforeEach(async () => { // 在每次测试运行前调用一次 await prepareSomething() - // 清理函数,在每次测试运行后调用一次 + // 清理函数,在每次测试运行后调用一次,在 afterEach 钩子之后执行 return async () => { await resetSomething() } @@ -103,7 +103,7 @@ beforeAll(async () => { 此处,`beforeAll` 确保在测试运行前完成模拟数据的初始化。 -`beforeAll` 还可以返回一个可选的清理函数(等价于 [`afterAll`](#afterall)): +`beforeAll` 还可以返回一个可选的清理函数(等价于 [`afterAll`](#afterall))。唯一区别在于该钩子会在所有其他 `afterAll` 钩子执行完毕后运行: ```ts import { beforeAll } from 'vitest' @@ -112,7 +112,7 @@ beforeAll(async () => { // 在所有测试运行之前调用一次 await startMocking() - // 清理函数,在所有测试运行之后调用一次 + // 清理函数,在所有测试运行结束后调用一次,在所有 afterAll 钩子之后执行 return async () => { await stopMocking() } diff --git a/api/test.md b/api/test.md index 31f9a2b3..4f7e5ded 100644 --- a/api/test.md +++ b/api/test.md @@ -216,14 +216,14 @@ describe( - **别名:** [`test.concurrent`](#test-concurrent) 是否与测试套件中其他并发测试并行运行。 + +Set `concurrent` to `false` to opt out of concurrency inherited from [`describe.concurrent`](/api/describe#describe-concurrent) or [`sequence.concurrent`](/config/sequence#sequence-concurrent): -### sequential - -- **类型:** `boolean` -- **默认值:** `true` -- **别名:** [`test.sequential`](#test-sequential) - -是否按顺序运行测试。如果同时指定了 `concurrent` 和 `sequential`,`concurrent` 优先生效。 +```ts +test('runs sequentially', { concurrent: false }, async () => { + // ... +}) +``` ### skip @@ -453,31 +453,6 @@ test.concurrent('test 2', async ({ expect }) => { 注意,如果测试是同步的,Vitest 仍会按顺序运行它们。 -## test.sequential - -- **别名:** `it.sequential` - -`test.sequential` 将测试标记为顺序执行。适用于在 `describe.concurrent` 或 `--sequence.concurrent` 命令选项下按顺序运行测试的场景。 -```ts -import { describe, test } from 'vitest' - -// 配置项 { sequence: { concurrent: true } } 开启时 -test('concurrent test 1', async () => { /* ... */ }) -test('concurrent test 2', async () => { /* ... */ }) - -test.sequential('sequential test 1', async () => { /* ... */ }) -test.sequential('sequential test 2', async () => { /* ... */ }) - -// 在并发测试套件内 -describe.concurrent('suite', () => { - test('concurrent test 1', async () => { /* ... */ }) - test('concurrent test 2', async () => { /* ... */ }) - - test.sequential('sequential test 1', async () => { /* ... */ }) - test.sequential('sequential test 2', async () => { /* ... */ }) -}) -``` - ## test.todo - **别名:** `it.todo` diff --git a/config/attachmentsdir.md b/config/attachmentsdir.md index 8887d331..74c3d0ac 100644 --- a/config/attachmentsdir.md +++ b/config/attachmentsdir.md @@ -6,6 +6,6 @@ outline: deep # attachmentsDir - **类型:** `string` -- **默认值:** `'.vitest-attachments'` +- **默认值:** `'.vitest/attachments'` 用于存储 [`context.annotate`](/guide/test-context#annotate) 所创建附件的目录路径(相对于项目根目录)。 diff --git a/config/browser/expect.md b/config/browser/expect.md index 94f4d4a1..cd3e2c05 100644 --- a/config/browser/expect.md +++ b/config/browser/expect.md @@ -104,6 +104,10 @@ export default defineConfig({ [`attachmentsDir`](/config/attachmentsdir) 配置项提供的值,如果未配置则使用其默认值。 +- `project: TestProject` 4.1.6 + + The [`TestProject`](/api/advanced/test-project) the test belongs to. + 例如,以下示例按浏览器分组存储截图: ```ts diff --git a/config/coverage.md b/config/coverage.md index 14ec711d..95795ff4 100644 --- a/config/coverage.md +++ b/config/coverage.md @@ -458,3 +458,14 @@ export default defineConfig({ - **命令行终端:** `--coverage.changed`, `--coverage.changed=` 仅收集自指定提交或分支以来更改的文件的代码覆盖率。设置为 `true` 时,使用已暂存和未暂存的更改。 + +## coverage.autoAttachSubprocess 5.0.0 {#coverage-autoattachsubprocess} + +- **Type:** `boolean` +- **Default:** `false` +- **Available for providers:** `'v8'` +- **CLI:** `--coverage.autoAttachSubprocess` + +Track coverage of the `node:child_process` and `node:worker_threads` spawned during test run. + +Note that this option has some performance overhead as its using [`NODE_V8_COVERAGE`](https://nodejs.org/api/cli.html#node-v8-coveragedir) internally. This triggers Node to write lots of unnecessary files on file system. diff --git a/config/reporters.md b/config/reporters.md index a9d74af6..74bded13 100644 --- a/config/reporters.md +++ b/config/reporters.md @@ -13,11 +13,11 @@ interface UserConfig { type ConfigReporter = string | Reporter | [string, object?] ``` - -- **默认值:** [`'default'`](/guide/reporters#default-reporter)(当 `process.env.GITHUB_ACTIONS === 'true'` 时为 [['default'](/guide/reporters#default-reporter), ['github-actions'](/guide/reporters#github-actions-reporter)]) -- **命令行终端:** - - `--reporter=tap` 用于单个报告器 - - `--reporter=verbose --reporter=github-actions` 用于多个报告器 + +- **Default:** [`'default'`](/guide/reporters#default-reporter). See [Default Reporters](/guide/reporters#default-reporters) for environment-specific behavior. +- **CLI:** + - `--reporter=tap` for a single reporter + - `--reporter=verbose --reporter=github-actions` for multiple reporters 此选项定义在 Vitest 测试运行期间可用的单个报告器或报告器列表。 @@ -49,14 +49,14 @@ type ConfigReporter = string | Reporter | [string, object?] ::: code-group ```js [vitest.config.js] -import { defineConfig } from 'vitest/config' +import { configDefaults, defineConfig } from 'vitest/config' export default defineConfig({ test: { reporters: [ - 'default', + ...configDefaults.reporters, // 条件报告器 - process.env.CI ? 'github-actions' : {}, + ...(process.env.CI ? ['html'] : []), // 来自 npm 包的自定义报告器 // 选项将以元组形式向下传递 [ diff --git a/guide/browser/visual-regression-testing.md b/guide/browser/visual-regression-testing.md index bd89ca4e..1df2c981 100644 --- a/guide/browser/visual-regression-testing.md +++ b/guide/browser/visual-regression-testing.md @@ -276,10 +276,10 @@ Reference screenshot: tests/__screenshots__/button.test.ts/button-chromium-darwin.png Actual screenshot: - tests/.vitest-attachments/button.test.ts/button-chromium-darwin-actual.png + tests/.vitest/attachments/button.test.ts/button-chromium-darwin-actual.png Diff image: - tests/.vitest-attachments/button.test.ts/button-chromium-darwin-diff.png + tests/.vitest/attachments/button.test.ts/button-chromium-darwin-diff.png ``` ### 如何解读差异图 {#understanding-the-diff-image} diff --git a/guide/cli-generated.md b/guide/cli-generated.md index 9c05409f..045c7d1a 100644 --- a/guide/cli-generated.md +++ b/guide/cli-generated.md @@ -298,6 +298,13 @@ Coverage reporters to use. Visit [`coverage.reporter`](/config/coverage#coverage - **配置:** [coverage.htmlDir](/config/coverage#coverage-htmldir) UI 模式和 HTML 报告器中提供的 HTML 覆盖率输出目录。 + +### coverage.autoAttachSubprocess + +- **CLI:** `--coverage.autoAttachSubprocess` +- **Config:** [coverage.autoAttachSubprocess](/config/coverage#coverage-autoattachsubprocess) + +Track coverage of the `node:child_process` and `node:worker_threads` spawned during test run. Supported only by `v8` provider. (default: false) ### mode @@ -865,7 +872,7 @@ UI 模式和 HTML 报告器中提供的 HTML 覆盖率输出目录。 - **命令行终端:** `--attachmentsDir ` - **配置:** [attachmentsDir](/config/attachmentsdir) -`context.annotate` 方法所生成附件的存储目录 (默认值: `.vitest-attachments`) +`context.annotate` 方法所生成附件的存储目录 (默认值: `.vitest/attachments`) ### run diff --git a/guide/improving-performance.md b/guide/improving-performance.md index ee44e7c2..676f893c 100644 --- a/guide/improving-performance.md +++ b/guide/improving-performance.md @@ -191,7 +191,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: blob-attachments-${{ matrix.shardIndex }} - path: .vitest-attachments/** + path: .vitest/** include-hidden-files: true retention-days: 1 @@ -222,7 +222,7 @@ jobs: - name: Download attachments from GitHub Actions Artifacts uses: actions/download-artifact@v4 with: - path: .vitest-attachments + path: .vitest pattern: blob-attachments-* merge-multiple: true diff --git a/guide/lifecycle.md b/guide/lifecycle.md index cdbfd2f9..4fa8b9ed 100644 --- a/guide/lifecycle.md +++ b/guide/lifecycle.md @@ -137,10 +137,12 @@ afterEach(() => { - `beforeEach` 钩子执行(按定义顺序,或基于 [`sequence.hooks`](/config/sequence#sequence-hooks)) - 测试函数执行 - `afterEach` 钩子执行(默认以 `sequence.hooks: 'stack'` 倒序执行) + - Cleanup functions returned from `beforeEach` hooks execute (reverse order by default with `sequence.hooks: 'stack'`) - [`onTestFinished`](/api/hooks#ontestfinished) 回调执行(始终倒序) - 如果测试失败:[`onTestFailed`](/api/hooks#ontestfailed) 回调执行 - 注意:如果设置了 `repeats` 或 `retry`,上述所有步骤会再次执行 6. **[`afterAll`](/api/hooks#afterall) 钩子:** 套件中所有测试完成后执行一次 +7. **Cleanup functions returned from `beforeAll` hooks:** Run once after all tests in the suite complete **执行流程示例:** @@ -162,6 +164,11 @@ describe('User API', () => { beforeAll(() => { // 在套件所有测试前运行一次 console.log('beforeAll') + + return function beforeAllCleanup() { + // Runs once afterAll hooks have run + console.log('beforeAllCleanup') + } }) aroundEach(async (runTest) => { @@ -174,6 +181,11 @@ describe('User API', () => { beforeEach(() => { // 每个测试用例前运行 console.log('beforeEach') + + return function beforeEachCleanup() { + // Runs after afterEach hooks have run + console.log('beforeEachCleanup') + } }) test('creates user', () => { @@ -206,13 +218,16 @@ describe('User API', () => { // beforeEach // test 1 // afterEach +// beforeEachCleanup // aroundEach after // aroundEach before // beforeEach // test 2 // afterEach +// beforeEachCleanup // aroundEach after // afterAll +// beforeAllCleanup // aroundAll after ``` diff --git a/guide/migration.md b/guide/migration.md index cba0bdd7..f60aba89 100644 --- a/guide/migration.md +++ b/guide/migration.md @@ -6,6 +6,68 @@ outline: deep # 迁移指南 {#migration-guide} [迁移至 Vitest 3.0](https://v3.vitest.dev/guide/migration) | [迁移至 Vitest 2.0](https://v2.vitest.dev/guide/migration) + +## Migrating to Vitest 5.0 {#vitest-5} + +::: warning Work in progress +Vitest 5.0 is currently in beta. This section tracks breaking changes as they are merged and may change before the stable release. +::: + +### Removed `test.sequential`, `describe.sequential`, and `sequential` Options + +Vitest 5.0 removes the deprecated `test.sequential`, `describe.sequential`, and `sequential` test options. Use `concurrent: false` when you need a test or suite to opt out of inherited or globally configured concurrency. + +```ts +test.sequential('example', async () => { /* ... */ }) // [!code --] +test('example', { concurrent: false }, async () => { /* ... */ }) // [!code ++] +``` + +```ts +describe.sequential('suite', () => { /* ... */ }) // [!code --] +describe('suite', { concurrent: false }, () => { /* ... */ }) // [!code ++] +``` + +The same replacement applies to option objects: + +```ts +test('example', { sequential: true }, async () => { /* ... */ }) // [!code --] +test('example', { concurrent: false }, async () => { /* ... */ }) // [!code ++] +``` + +### Locators in Commands are Serialized as Objects + +Locators forwarded to [browser commands](/api/browser/commands) are now serialized as a `SerializedLocator` object instead of a bare selector string. The object exposes two fields: + +- `selector`: the provider-specific selector string (the same value commands previously received). +- `locator`: a human-readable representation of the locator (e.g. `getByRole('button')`), used for error messages and tracing. + +Update any custom commands that accept a locator to destructure `selector` from the new object: + +```ts +import type { SerializedLocator } from '@vitest/browser' +import type { BrowserCommandContext } from 'vitest/node' + +export async function customClick( + context: BrowserCommandContext, + selector: string, // [!code --] + { selector }: SerializedLocator, // [!code ++] +) { + await context.page.locator(selector).click() +} +``` + +### Removed Deprecated Entrypoints + +Several entry points were marked as deprecated in Vitest 4.1. This release removes them entirely. + +- `vitest/coverage`: use `vitest/node` instead +- `vitest/reporters`: use `vitest/node` instead +- `vitest/environments`: use `vitest/runtime` instead +- `vitest/snapshot`: use `vitest/runtime` instead +- `vitest/runners`: use `TestRunner` from `vitest` instead +- `vitest/suite`: use static methods on `TestRunner` from vitest instead (for example, `TestRunner.getCurrentTest()`) +- `vitest/mocker` is removed completely, use `@vitest/mocker` package directly (this was published by accident at one point and never removed) +- `vitest/internal/module-runner` is removed ## 迁移至 Vitest 4.0 {#vitest-4} diff --git a/guide/parallelism.md b/guide/parallelism.md index fd0c745a..db7d888e 100644 --- a/guide/parallelism.md +++ b/guide/parallelism.md @@ -82,6 +82,19 @@ describe.concurrent('user API', () => { If you want *all* tests in your project to run concurrently by default, set [`sequence.concurrent`](/config/sequence#sequence-concurrent) to `true` in your config. +You can opt individual tests or suites out of inherited concurrency with `concurrent: false`: + +```ts +test('uses a shared resource', { concurrent: false }, async () => { + // ... +}) + +describe('shared resource suite', { concurrent: false }, () => { + test('step 1', async () => { /* ... */ }) + test('step 2', async () => { /* ... */ }) +}) +``` + ### Hooks with Concurrent Tests When tests run concurrently, lifecycle hooks behave differently. `beforeAll` and `afterAll` still run once for the group, but `beforeEach` and `afterEach` run for each test — potentially at the same time, since the tests themselves overlap. diff --git a/guide/reporters.md b/guide/reporters.md index a9d3a46b..0ab66a9c 100644 --- a/guide/reporters.md +++ b/guide/reporters.md @@ -5,7 +5,7 @@ outline: deep # 报告器 {#reporters} -Vitest 提供了几种内置报告器,以不同格式显示测试输出,以及使用自定义报告器的能力。你可以使用 `--reporter` 命令行选项,或者在你的 `outputFile` [配置选项](/config/reporters) 中加入 `reporters` 属性来选择不同的报告器。如果没有指定报告器,Vitest 将使用下文所述的默认报告器。 +Vitest 提供了几种内置报告器,以不同格式显示测试输出,以及使用自定义报告器的能力。你可以使用 `--reporter` 命令行选项,或者在你的 `outputFile` [配置选项](/config/reporters) 中加入 `reporters` 属性来选择不同的报告器。如果未指定报告器,Vitest 将根据运行环境 [自动选择报告器](#default-configuration)。 通过命令行使用报告器: @@ -34,6 +34,26 @@ export default defineConfig({ }, }) ``` + +## Default Configuration + +When `reporters` is not configured, Vitest uses the following reporters: + +- [`default`](#default-reporter) in normal terminal runs +- [`minimal`](#minimal-reporter) when Vitest detects an AI coding agent +- [`github-actions`](#github-actions-reporter) is added when `process.env.GITHUB_ACTIONS === 'true'` + +If you configure your own reporters, the configured list replaces the default list. To add a reporter while keeping Vitest's defaults, extend `configDefaults.reporters`: + +```ts +import { configDefaults, defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + reporters: ['json', ...configDefaults.reporters], + }, +}) +``` ## 报告器输出 {#reporter-output} @@ -67,10 +87,12 @@ npx vitest --reporter=json --reporter=default ``` ```ts +import { configDefaults, defineConfig } from 'vitest/config' + export default defineConfig({ test: { - reporters: ['json', 'default'], - outputFile: './test-output.json', + reporters: ['json', ...configDefaults.reporters], + outputFile: './test-output.json' }, }) ``` @@ -96,12 +118,8 @@ export default defineConfig({ ## 内置报告器 {#built-in-reporters} ### 默认报告器 {#default-reporter} - -默认情况下(即如果没有指定报告器),Vitest 会在底部显示运行测试的摘要及其状态。一旦测试套件通过,其状态将被报告在摘要的顶部。 - -::: tip -当 Vitest 检测到运行在 AI 智能体编程环境中时,将自动启用 [`minimal`](#minimal-reporter) 报告器以精简输出内容并优化词元 (token) 消耗。你可以通过显式配置 [`reporters`](/config/reporters) 选项来覆盖此行为。 -::: + +The `default` reporter displays summary of running tests and their status at the bottom. Once a suite passes, its status will be reported on top of the summary. 我们可以通过配置报告器来禁用摘要: @@ -338,18 +356,71 @@ AssertionError: expected 5 to be 4 // Object.is equality ``` + +The output XML contains nested `testsuites` → `testsuite` → `testcase` tags. You can customize the reporter's behaviour with the following options: + +| Option | Description | Default | +|---|---|---| +| `suiteName` | `name` attribute of `` | `"vitest tests"` | +| `suiteNameTemplate` | Template for the `name` attribute of ``. Accepts a string with placeholders or a function. | Relative file path | +| `classnameTemplate` | Template for the `classname` attribute of ``. Accepts a string with placeholders or a function. | Relative file path | +| `titleTemplate` | Template for the `name` attribute of ``. Accepts a string with placeholders or a function. | Full test title with ancestor hierarchy | +| `ancestorSeparator` | Separator used when joining ancestor describe block names in the `{classname}` placeholder and in the default test title. | `" > "` | +| `addFileAttribute` | Add a `file` attribute to each ``. | `false` | +| `includeConsoleOutput` | Include `` / `` console output. | `true` | +| `stackTrace` | Include stack traces in `` elements. | `true` | + +The following placeholders are available for `suiteNameTemplate`: +- `{title}` – name of the first top-level `describe` block; falls back to the file basename when there is no top-level `describe` +- `{filename}` – relative file path from the root (e.g. `src/foo.test.ts`) +- `{filepath}` – absolute file path +- `{basename}` – file name without directory (e.g. `foo.test.ts`) +- `{displayName}` – Vitest project name + +The following placeholders are available for `classnameTemplate` and `titleTemplate`: +- `{classname}` – ancestor `describe` block names joined by `ancestorSeparator` (e.g. `outer > inner`) +- `{title}` – leaf test title (the string passed to `it`/`test`) +- `{suitename}` – top-level `describe` block name, empty string when the test has no enclosing `describe` +- `{filename}` – relative file path from the root +- `{filepath}` – absolute file path +- `{basename}` – file name without directory +- `{displayName}` – Vitest project name -输出的 XML 包含嵌套的 `testsuites` 和 `testcase` 标签。这些也可以通过报告选项 `suiteName` 和 `classnameTemplate` 进行自定义。`classnameTemplate` 可以是一个模板字符串或者一个函数。 +::: tip +`{filename}` follows Vitest's convention and resolves to the **relative path** from the project root (e.g. `src/foo.test.ts`). This differs from jest-junit where `{filename}` is the bare file name. Use `{basename}` to get only the file name. +::: -`classnameTemplate` 选项支持的占位符有: -- filename -- filepath +```ts +export default defineConfig({ + test: { + reporters: [ + ['junit', { + suiteName: 'My Test Suite', + // Use the first top-level describe block name as the testsuite name + suiteNameTemplate: '{title}', + // classname = ancestor describe chain + classnameTemplate: '{classname}', + // name = leaf test title only (jest-junit-compatible) + titleTemplate: '{title}', + ancestorSeparator: ' > ', + }] + ] + }, +}) +``` + +Function-based templates receive all available variables and can return any string: ```ts export default defineConfig({ test: { reporters: [ - ['junit', { suiteName: 'custom suite name', classnameTemplate: 'filename:{filename} - filepath:{filepath}' }] + ['junit', { + classnameTemplate: ({ classname, filename }) => + classname ? `${filename}::${classname}` : filename, + titleTemplate: ({ suitename, title }) => + suitename ? `[${suitename}] ${title}` : title, + }] ] }, }) @@ -564,23 +635,14 @@ export default defineConfig({ ::: ### GitHub Actions 报告器 {#github-actions-reporter} - -当测试失败时输出 [工作流命令](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message) 提供注解。当未配置 `reporters` 选项且 `process.env.GITHUB_ACTIONS === 'true'`(在GitHub Actions环境中)时,此报告器会自动启用。 + +Output [workflow commands](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message) +to provide annotations for test failures. This reporter is [enabled automatically](#default-configuration) when `process.env.GITHUB_ACTIONS === 'true'` (on GitHub Actions environment). GitHub Actions GitHub Actions -如果已配置报告器,需显式添加 `github-actions` 添加到报告器列表中。 - -```ts -export default defineConfig({ - test: { - reporters: process.env.GITHUB_ACTIONS === 'true' ? ['dot', 'github-actions'] : ['dot'], - }, -}) -``` - -你可以使用 `onWritePath` 选项自定义以 [GitHub 注解命令格式](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions) 打印的文件路径。这在容器化环境(如 Docker)中运行 Vitest 时非常有用,因为在这些环境中文件路径可能与 GitHub Actions 环境中的路径不匹配。 +You can customize the file paths that are printed in [GitHub's annotation command format](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions) by using the `onWritePath` option. This is useful when running Vitest in a containerized environment, such as Docker, where the file paths may not match the paths in the GitHub Actions environment. ```ts export default defineConfig({ @@ -677,7 +739,7 @@ export default defineConfig({ Outputs a minimal report containing only failed tests and their error messages. Console logs from passing tests and the summary section are also suppressed. ::: tip Agent Reporter -This reporter is well optimized for AI coding assistants and LLM-based workflows to reduce token usage. It is automatically enabled when no `reporters` option is configured and Vitest detects it is running inside an AI coding agent. If you configure custom reporters, you can explicitly add `minimal` or `agent`: +This reporter is well optimized for AI coding assistants and LLM-based workflows to reduce token usage. It is [enabled automatically](#default-configuration) when Vitest detects it is running inside an AI coding agent. :::code-group ```bash [CLI] diff --git a/guide/snapshot.md b/guide/snapshot.md index 3d03eda5..b990e4a9 100644 --- a/guide/snapshot.md +++ b/guide/snapshot.md @@ -337,7 +337,7 @@ Custom serializers control how values are _rendered_ into snapshot strings, but A domain adapter implements four methods and is generic over two types — `Captured` (what the value actually is) and `Expected` (what the stored snapshot parses into): ```ts -import type { DomainMatchResult, DomainSnapshotAdapter } from '@vitest/snapshot' +import type { DomainMatchResult, DomainSnapshotAdapter } from 'vitest' const myAdapter: DomainSnapshotAdapter = { name: 'my-domain', @@ -413,7 +413,7 @@ expect(value).toMatchMyDomainInlineSnapshot(`key=value`) A minimal adapter that stores objects as `key=value` lines, with regex pattern and subset key match support ([full source](https://github.com/vitest-dev/vitest/blob/main/test/snapshots/test/fixtures/domain/basic.ts)): ```ts [kv-adapter.ts] -import type { DomainMatchResult, DomainSnapshotAdapter } from '@vitest/snapshot' +import type { DomainMatchResult, DomainSnapshotAdapter } from 'vitest' type KVCaptured = Record type KVExpected = Record diff --git a/guide/ui.md b/guide/ui.md index 3a3a9542..4813ac49 100644 --- a/guide/ui.md +++ b/guide/ui.md @@ -38,9 +38,9 @@ export default defineConfig({ ``` 你可以在 Vitest UI 中查看覆盖率报告:查看 [覆盖率 | UI 模式](/guide/coverage#vitest-ui) 了解更多详情。 - + ::: warning -如果你仍想在终端中实时查看测试的运行情况,请不要忘记将 `default` 报告器添加到 `reporters` 选项:`['default', 'html']`。 +If you still want to see how your tests are running in real time in the terminal, add `configDefaults.reporters` to the `reporters` option: `['html', ...configDefaults.reporters]`. ::: ::: tip diff --git a/package.json b/package.json index 5132bc6a..315994b5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "docs-cn", "type": "module", - "version": "4.1.4", + "version": "5.0.0-beta.1", "private": true, "packageManager": "pnpm@9.7.1", "scripts": {