diff --git a/.vitepress/config.ts b/.vitepress/config.ts index 7ade2a08..db94f657 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -807,6 +807,11 @@ export default ({ mode }: { mode: string }) => { link: '/guide/browser/trace-view', docFooterText: '追踪查看器 | 浏览器模式', }, + { + text: 'Playwright Traces', + link: '/guide/browser/playwright-traces', + docFooterText: 'Playwright Traces | Browser Mode', + }, { text: 'ARIA Snapshots', link: '/guide/browser/aria-snapshots', diff --git a/api/advanced/vitest.md b/api/advanced/vitest.md index 1da470b6..6b9f7828 100644 --- a/api/advanced/vitest.md +++ b/api/advanced/vitest.md @@ -244,7 +244,7 @@ function start(filters?: string[]): Promise function standalone(): Promise ``` -- **别名:**: `init` +- **别名:** `init` 初始化报告器和覆盖率提供者。此方法不运行任何测试。如果提供了 `--watch` 标志,Vitest 仍将运行更改的测试,即使未调用此方法。 @@ -696,3 +696,120 @@ export interface SourceModuleDiagnostic { ::: warning [浏览器模式](/guide/browser/) 暂不支持。 ::: + +## createReport 5.0.0 {#createreport} + +```ts +function createReport(scope: string): Report +``` + +Creates a report that is limited to the given scope. `Report` follows Vitest's rules around [Storing artifacts on file system](/guide/advanced/reporters.html#storing-artifacts-on-file-system). + +`Report` provides collection of utilities for writing test results, temporary files and other artifacts on the file system. It's especially intended for third party integrations like custom reporters. + +All operations of `Report` are limited to given `scope`. A single report cannot interfere with other reports. Internally Vitest creates `.vitest` directory where each `scope` creates their own directory. This convention of `.vitest` directory reduces the amount of entries end-users need to specify in their `.gitignore`. + +```ts +import type { Report } from 'vitest/node' + +const scope = 'example-yaml-reporter' + +// Automatically creates `/.vitest/example-yaml-reporter/` +// directory if it does not exist already +const report: Report = vitest.createReport(scope) +``` + +### Report.root + +```ts +const root: string +``` + +The root directory for this scope. + +```ts +const report = vitest.createReport('my-json-reporter') + +// Is /.vitest/my-json-reporter +const root = report.root +``` + +### Report.clean + +```ts +function clean(): Promise +``` + +Clean up the report directory for this scope. + +```ts +const report = vitest.createReport('my-json-reporter') + +// Removes everything inside /.vitest/my-json-reporter/ +await report.clean() +``` + +### Report.writeFile + +```ts +function writeFile( + filename: string, + content: string | Uint8Array, + encoding?: BufferEncoding +): Promise +``` + +Write a file to the report directory for this scope. By default the file will be written with UTF-8 encoding. The filename is relative to the scope directory. + +```ts +const report = vitest.createReport('my-json-reporter') + +// Writes file to .vitest/my-json-reporter/test-report.json +await report.writeFile('test-report.json', JSON.stringify(results)) +``` + +### Report.readFile + +```ts +function readFile(filename: string, encoding?: BufferEncoding): Promise +``` + +Read a file from the report directory for this scope. + +```ts +const report = vitest.createReport('my-json-reporter') + +// Reads file from .vitest/my-json-reporter/test-report.json +const content: string = await report.readFile('test-report.json') +``` + +### Report.readdir + +```ts +function readdir(): Promise +``` + +Read contents of the report directory for this scope. + +```ts +const report = vitest.createReport('my-json-reporter') + +// Reads contents from .vitest/my-json-reporter +const filenames: string[] = await report.readdir() +``` + +### Report.delete + + +```ts +function delete(filename: string): Promise +``` + +Delete a file from the report directory for this scope. + +```ts +const report = vitest.createReport('my-json-reporter') + +// Deletes file from .vitest/my-json-reporter/test-report.json +await report.delete('test-report.json') +``` diff --git a/api/assert.md b/api/assert.md index 8f484404..f7ed29d7 100644 --- a/api/assert.md +++ b/api/assert.md @@ -36,7 +36,7 @@ test('assert.fail', () => { ## isOk - **类型:** `(value: T, message?: string) => asserts value` -- **Alias** `ok` +- **别名:** `ok` 断言给定的 `value` 是 true 。 @@ -52,7 +52,7 @@ test('assert.isOk', () => { ## isNotOk - **类型:** `(value: T, message?: string) => void` -- **Alias** `notOk` +- **别名:** `notOk` 断言给定的 `value` 是 false 。 diff --git a/api/browser/context.md b/api/browser/context.md index 7d6ecdd0..12d649ff 100644 --- a/api/browser/context.md +++ b/api/browser/context.md @@ -267,6 +267,17 @@ export const utils: { * Creates "Cannot find element" error. Useful for custom locators. */ getElementError(selector: string, container?: Element): Error + /** + * Utilities for generating and working with ARIA trees and templates. + * @experimental + */ + aria: { + generateAriaTree(rootElement: Element): AriaNode + renderAriaTree(root: AriaNode): string + renderAriaTemplate(template: AriaTemplateNode): string + parseAriaTemplate(text: string): AriaTemplateNode + matchAriaTree(root: AriaNode, template: AriaTemplateNode): { pass: boolean; resolved: string } + } } ``` @@ -337,3 +348,22 @@ utils.configurePrettyDOM({ ::: tip This feature is inspired by Testing Library's [`defaultIgnore`](https://testing-library.com/docs/dom-testing-library/api-configuration/#defaultignore) configuration. ::: + +### aria 5.0.0 {#aria} + +The `aria` namespace exposes low-level utilities used by Vitest's ARIA snapshot matchers. + +```ts +import { utils } from 'vitest/browser' + +document.body.innerHTML = ` +

Hello, World!

+ + +` +const tree = utils.aria.generateAriaTree(document.body) +const yaml = utils.aria.renderAriaNode(tree) +console.log(yaml) +// - heading "Hello, World!" [level=1] +// - button "Visible"" +``` diff --git a/api/expect.md b/api/expect.md index 4a62783e..a8d3b02b 100644 --- a/api/expect.md +++ b/api/expect.md @@ -1077,7 +1077,7 @@ test('spy function', () => { ## toHaveBeenCalledTimes -- **类型**: `(amount: number) => Awaitable` +- **类型:** `(amount: number) => Awaitable` 这个断言检查函数是否被调用了特定次数。需要将一个 spy 函数传递给 `expect`。 @@ -1102,7 +1102,7 @@ test('spy function called two times', () => { ## toHaveBeenCalledWith -- **类型**: `(...args: any[]) => Awaitable` +- **类型:** `(...args: any[]) => Awaitable` 这个断言检查函数是否至少一次被调用,并带有特定的参数。需要将一个 spy 函数传递给 `expect`。 @@ -1128,7 +1128,7 @@ test('spy function', () => { ## toHaveBeenCalledBefore -- **类型**: `(mock: MockInstance, failIfNoFirstInvocation?: boolean) => Awaitable` +- **类型:** `(mock: MockInstance, failIfNoFirstInvocation?: boolean) => Awaitable` 这个断言检查一个 `Mock` 是否在另一个 `Mock` 之前被调用。 @@ -1147,7 +1147,7 @@ test('calls mock1 before mock2', () => { ## toHaveBeenCalledAfter -- **类型**: `(mock: MockInstance, failIfNoFirstInvocation?: boolean) => Awaitable` +- **类型:** `(mock: MockInstance, failIfNoFirstInvocation?: boolean) => Awaitable` 这个断言检查一个 `Mock` 是否在另一个 `Mock` 之后被调用。 @@ -1166,7 +1166,7 @@ test('calls mock1 after mock2', () => { ## toHaveBeenCalledExactlyOnceWith -- **类型**: `(...args: any[]) => Awaitable` +- **类型:** `(...args: any[]) => Awaitable` 这个断言检查函数是否恰好被调用了一次,并且带有特定的参数。需要将一个 spy 函数传递给 `expect`。 @@ -1190,7 +1190,7 @@ test('spy function', () => { ## toHaveBeenLastCalledWith -- **类型**: `(...args: any[]) => Awaitable` +- **类型:** `(...args: any[]) => Awaitable` 这个断言检查函数在其最后一次调用时是否被传入了特定的参数。需要将一个 spy 函数传递给 `expect`。 @@ -1216,7 +1216,7 @@ test('spy function', () => { ## toHaveBeenNthCalledWith -- **类型**: `(time: number, ...args: any[]) => Awaitable` +- **类型:** `(time: number, ...args: any[]) => Awaitable` 这个断言检查函数是否在特定的次数被调用时带有特定的参数。计数从 1 开始。因此,要检查第二次调用,我们需要写成 `.toHaveBeenNthCalledWith(2, ...)`。 @@ -1243,7 +1243,7 @@ test('first call of spy function called with right params', () => { ## toHaveReturned -- **类型**: `() => Awaitable` +- **类型:** `() => Awaitable` 这个断言检查函数是否至少成功返回了一次值( i.e. ,没有抛出错误)。需要将一个 spy 函数传递给 `expect`。 @@ -1267,7 +1267,7 @@ test('spy function returned a value', () => { ## toHaveReturnedTimes -- **类型**: `(amount: number) => Awaitable` +- **类型:** `(amount: number) => Awaitable` 这个断言检查函数是否在确切的次数内成功返回了值( i.e. ,没有抛出错误)。需要将一个 spy 函数传递给 `expect`。 @@ -1286,7 +1286,7 @@ test('spy function returns a value two times', () => { ## toHaveReturnedWith -- **类型**: `(returnValue: any) => Awaitable` +- **类型:** `(returnValue: any) => Awaitable` 我们可以调用这个断言来检查函数是否至少一次成功返回了带有特定参数的值。需要将一个 spy 函数传递给 `expect`。 @@ -1304,7 +1304,7 @@ test('spy function returns a product', () => { ## toHaveLastReturnedWith -- **类型**: `(returnValue: any) => Awaitable` +- **类型:** `(returnValue: any) => Awaitable` 我们可以使用这个断言来检查函数在最后一次被调用时是否成功返回了特定的值。需要将一个 spy 函数传递给 `expect`。 @@ -1323,7 +1323,7 @@ test('spy function returns bananas on a last call', () => { ## toHaveNthReturnedWith -- **类型**: `(time: number, returnValue: any) => Awaitable` +- **类型:** `(time: number, returnValue: any) => Awaitable` 我们可以调用这个断言来检查函数是否在特定的调用中成功返回了带有特定参数的值。需要将一个 spy 函数传递给 `expect`。 @@ -1344,7 +1344,7 @@ test('spy function returns bananas on second call', () => { ## toHaveResolved -- **类型**: `() => Awaitable` +- **类型:** `() => Awaitable` 这个断言检查函数是否至少一次成功地解析了一个值( i.e. ,没有被拒绝)。需要将一个 spy 函数传递给 `expect`。 @@ -1370,7 +1370,7 @@ test('spy function resolved a value', async () => { ## toHaveResolvedTimes -- **类型**: `(amount: number) => Awaitable` +- **类型:** `(amount: number) => Awaitable` 此断言检查函数是否已成功解析值精确次数(即未 reject)。需要将 spy 函数传递给`expect`。 diff --git a/api/test.md b/api/test.md index 9fa7336f..84ca7250 100644 --- a/api/test.md +++ b/api/test.md @@ -614,9 +614,9 @@ test.each` expect(a + b).toBe(expected) }) ``` - + ::: tip -Vitest 使用 Chai 的 `format` 方法处理 `$values`。如果值被截断,可在配置文件中调大 [chaiConfig.truncateThreshold](/config/chaiconfig#chaiconfig-truncatethreshold)。 +Vitest formats interpolated title values with its display formatter. If the value is too truncated, you can increase [taskTitleValueFormatTruncate](/config/tasktitlevalueformattruncate) in your config file. ::: ## test.for diff --git a/config/allowonly.md b/config/allowonly.md index 7e932f0e..b8350884 100644 --- a/config/allowonly.md +++ b/config/allowonly.md @@ -5,8 +5,8 @@ outline: deep # allowOnly -- **类型:**: `boolean` -- **默认值:**: `!process.env.CI` +- **类型:** `boolean` +- **默认值:** `!process.env.CI` - **命令行终端:** `--allowOnly`, `--allowOnly=false` 默认情况下,Vitest 不允许在持续集成(CI)环境中运行带有 [`only`](/api/test#test-only) 标记的测试。相反,在本地开发环境中,Vitest 允许运行这些测试。 diff --git a/config/browser/trace.md b/config/browser/trace.md index 61e9efb7..fa8d14bd 100644 --- a/config/browser/trace.md +++ b/config/browser/trace.md @@ -10,6 +10,8 @@ outline: deep - **默认值:** `'off'` 捕获浏览器测试运行的追踪记录。你可以通过 [Playwright Trace Viewer](https://trace.playwright.dev/) 预览追踪文件。 + +See [Playwright Traces](/guide/browser/playwright-traces) for the full workflow. 该选项支持以下取值: diff --git a/config/browser/traceview.md b/config/browser/traceview.md new file mode 100644 index 00000000..8af97f3a --- /dev/null +++ b/config/browser/traceview.md @@ -0,0 +1,70 @@ +--- +title: browser.traceView | Config +outline: deep +--- + +# browser.traceView 5.0.0 + +- **Type:** `boolean | { enabled?: boolean; recordCanvas?: boolean; inlineImages?: boolean }` +- **CLI:** `--browser.traceView` +- **Default:** `false` + +Enable trace-view collection for browser tests. Vitest captures DOM snapshots for browser interactions and can show them in the browser UI, Vitest UI, or HTML reporter when those surfaces are enabled — no external tools required. + +```ts +export default defineConfig({ + test: { + browser: { + traceView: true, + }, + }, +}) +``` + +Use the object form to enable additional snapshot fidelity options: + +```ts +export default defineConfig({ + test: { + browser: { + traceView: { + enabled: true, + inlineImages: true, + recordCanvas: true, + }, + }, + }, +}) +``` + +| Option | Default | Description | +| --- | --- | --- | +| `enabled` | `false` | Enables Vitest trace-view artifact collection. | +| `inlineImages` | `false` | Inlines loaded `` pixels into snapshots for more portable replay, useful in the HTML reporter. | +| `recordCanvas` | `false` | Captures canvas pixels in snapshots. | + +## browser.traceView.enabled {#traceview-enabled} + +- **Type:** `boolean` +- **Default:** `false` +- **CLI:** `--browser.traceView.enabled` + +Enables Vitest trace-view artifact collection. + +## browser.traceView.inlineImages {#traceview-inlineimages} + +- **Type:** `boolean` +- **Default:** `false` +- **CLI:** `--browser.traceView.inlineImages` + +Inlines loaded `` pixels into snapshots for more portable replay, useful in the HTML reporter. + +## browser.traceView.recordCanvas {#traceview-recordcanvas} + +- **Type:** `boolean` +- **Default:** `false` +- **CLI:** `--browser.traceView.recordCanvas` + +Captures canvas pixels in snapshots. This enables a weaker replay iframe sandbox because rrweb needs scripts to redraw canvas data. + +See [Trace View](/guide/browser/trace-view) for full documentation. diff --git a/config/chaiconfig.md b/config/chaiconfig.md index f6edf81a..ded255a2 100644 --- a/config/chaiconfig.md +++ b/config/chaiconfig.md @@ -30,5 +30,3 @@ outline: deep - **默认值:** `40` 设置断言错误中实际值与期望值的长度阈值。当超过该阈值时(例如处理大型数据结构),值将被截断显示为类似 `[ Array(3) ]` 或 `{ Object (prop1, prop2) }` 的形式。若需完全禁用截断功能,请将该值设为 `0`。 - -此配置项会影响 `test.each` 标题及断言错误信息内部值的截断显示。 diff --git a/config/coverage.md b/config/coverage.md index c3c23949..42703149 100644 --- a/config/coverage.md +++ b/config/coverage.md @@ -389,6 +389,46 @@ Vitest 会将所有文件(包括匹配 glob 模式的文件)计入全局覆 - **命令行终端:** `--coverage.processingConcurrency=` 处理代码覆盖率结果时使用的并发限制。 + +## coverage.instrumenter 4.1.5 {#coverage-instrumenter} + +- **Type:** `(options: InstrumenterOptions) => CoverageInstrumenter` +- **Available for providers:** `'istanbul'` + +Factory for a custom instrumenter to use in place of the default `istanbul-lib-instrument`. Vitest calls the factory once during initialization and reuses the returned instrumenter for every file. The rest of the Istanbul pipeline (collection, merging, reporting) is unchanged. + +The factory receives an `InstrumenterOptions` object with Vitest's runtime coverage settings, and must return an object implementing the `CoverageInstrumenter` interface. Both types are exported from `vitest/node`. + + +```ts +interface InstrumenterOptions { + coverageVariable: string + coverageGlobalScope: string + coverageGlobalScopeFunc: boolean + ignoreClassMethods: string[] +} + +interface CoverageInstrumenter { + instrumentSync: (code: string, filename: string, inputSourceMap?: any) => string + lastSourceMap: () => any + lastFileCoverage: () => any +} +``` + + +```ts +import { defineConfig } from 'vitest/config' +import { createInstrumenter } from '@vitest/some-custom-instrumenter' + +export default defineConfig({ + test: { + coverage: { + provider: 'istanbul', + instrumenter: options => createInstrumenter(options), + } + } +}) +``` ## coverage.customProviderModule diff --git a/config/deps.md b/config/deps.md index d6dc33ae..6519a81e 100644 --- a/config/deps.md +++ b/config/deps.md @@ -27,7 +27,6 @@ outline: deep 此选项还会继承你的 `optimizeDeps` 配置(对于 Web 环境,Vitest 会扩展 `optimizeDeps`;对于 SSR 环境则会扩展 `ssr.optimizeDeps`)。如果在 `deps.optimizer` 中重定义 `include`/`exclude` 选项,运行测试时将会扩展你的 `optimizeDeps` 配置。若某选项同时出现在 `include` 和 `exclude` 列表中,Vitest 会自动将其从 `include` 中移除。 - ::: tip 你将无法通过编辑 `node_modules` 中的代码进行调试,因为这些代码实际位于 `cacheDir` 或 `test.cache.dir` 目录中。如需使用 `console.log` 语句进行调试,请直接修改对应文件,或通过 `deps.optimizer?.[mode].force` 选项强制重新打包。 ::: diff --git a/config/pool.md b/config/pool.md index a46a0473..98814f20 100644 --- a/config/pool.md +++ b/config/pool.md @@ -12,8 +12,8 @@ outline: deep 用于运行测试的线程池。 ## threads - -启用多线程。使用 threads 线程池时,你无法使用与进程相关的 API,如 `process.chdir()`。某些用原生语言编写的库(如`Prisma`、`bcrypt` 和 `canvas`)在多线程中运行时存在问题,容易导致段错误。在这些情况下,建议改用 `forks` 线程池。 + +Enable multi-threading. When using threads you are unable to use process related APIs such as `process.chdir()`. Some libraries written in native languages, such as `Prisma`, `bcrypt` and `canvas`, have problems when running in multiple threads and run into segfaults. In these cases it is advised to use `forks` pool instead. ## forks diff --git a/config/reporters.md b/config/reporters.md index f9f8f018..a9d74af6 100644 --- a/config/reporters.md +++ b/config/reporters.md @@ -42,7 +42,7 @@ type ConfigReporter = string | Reporter | [string, object?] - [`tap-flat`](/guide/reporters#tap-flat-reporter) - [`hanging-process`](/guide/reporters#hanging-process-reporter) - [`github-actions`](/guide/reporters#github-actions-reporter) -- [`agent`](/guide/reporters#agent-reporter) +- [`minimal`](/guide/reporters#minimal-reporter) (aliased as `agent`) - [`blob`](/guide/reporters#blob-reporter) ## 示例 {#example} diff --git a/config/tasktitlevalueformattruncate.md b/config/tasktitlevalueformattruncate.md new file mode 100644 index 00000000..69a12798 --- /dev/null +++ b/config/tasktitlevalueformattruncate.md @@ -0,0 +1,15 @@ +--- +title: taskTitleValueFormatTruncate | Config +outline: deep +--- + +# taskTitleValueFormatTruncate {#tasktitlevalueformattruncate} + +- **Type** `number` +- **Default:** `40` + +Sets the length limit for formatted values interpolated into generated task titles. + +This affects values inserted by APIs like `test.each` and `test.for`, including both `$value` and `%` placeholder formatting. + +Set it to `0` to disable truncation. diff --git a/guide/advanced/reporters.md b/guide/advanced/reporters.md index 8416d4e2..7a5e7e4c 100644 --- a/guide/advanced/reporters.md +++ b/guide/advanced/reporters.md @@ -71,6 +71,33 @@ class MyReporter implements Reporter { } } ``` + +## Storing artifacts on file system + +::: tip +Vitest provides [`vitest.createReport`](/api/advanced/vitest.html#createreport) that exposes collection of utilities for writing artifacts on file system conveniently. +::: + +If your custom reporter needs to store any artifacts on file system it should place them inside `.vitest` directory. This directory is a convention that Vitest reporters and third party integrations can use to co-locate their results in a single directory. This way users of your custom reporter do not need to add multiple exclusion in their `.gitignore`. Only the `.vitest` is needed. + +Reporters and other integrations should respect following rules around `.vitest` directory: + +- `.vitest` directory is placed in [the `root` of the project](/config/root) +- Reporter can create `.vitest` directory if it does not already exist +- Reporter should never remove `.vitest` directory +- Reporter should create their own directory inside `.vitest`, for example `.vitest/yaml-reporter/` +- Reporter can remove their own specific directory inside `.vitest`, for example `.vitest/yaml-reporter/` + +```ansi +.vitest +│ +├── yaml-reporter +│ ├── results.yaml +│ └── summary.yaml +│ +└── junit-reporter + └── report.xml +``` ## 导出报告器 {#exported-reporters} diff --git a/guide/browser/aria-snapshots.md b/guide/browser/aria-snapshots.md index 1867e963..28e423b8 100644 --- a/guide/browser/aria-snapshots.md +++ b/guide/browser/aria-snapshots.md @@ -30,6 +30,8 @@ await expect.element(page.getByRole('navigation')).toMatchAriaInlineSnapshot(` This catches accessibility regressions: missing labels, broken roles, incorrect heading levels, and more — things that DOM snapshots would miss. Even if the underlying HTML structure changes, the assertion would not fail as long as content matches semantically. +For advanced cases, you can also generate and inspect the ARIA tree through `utils.aria` from `vitest/browser`. See the [Context API](/api/browser/context#aria) for details. + ## Snapshot Workflow ARIA snapshots use the same Vitest snapshot workflow as other snapshot assertions. File snapshots, inline snapshots, `--update` / `-u`, watch mode updates, and CI snapshot behavior all work the same way. diff --git a/guide/browser/index.md b/guide/browser/index.md index e0c66780..b49bbcf0 100644 --- a/guide/browser/index.md +++ b/guide/browser/index.md @@ -323,6 +323,8 @@ npx vitest --browser.headless ::: Vitest 默认会在开发模式下自动打开浏览器界面,测试会在页面中央的 iframe 中执行。你可以通过选择界面中的预设尺寸、在测试中调用 `page.viewport` 方法,或者在 [配置文件](/config/browser/viewport) 中设置默认值来调整视口大小。 + +For an alternative debugging model that captures DOM snapshots for every test instead of showing a live iframe, see [Trace View](/guide/browser/trace-view). ## 无头模式 {#headless} diff --git a/guide/browser/playwright-traces.md b/guide/browser/playwright-traces.md new file mode 100644 index 00000000..c51a6c42 --- /dev/null +++ b/guide/browser/playwright-traces.md @@ -0,0 +1,126 @@ +# Playwright Traces + +Vitest Browser Mode supports generating Playwright's [trace files](https://playwright.dev/docs/trace-viewer#viewing-remote-traces). To enable tracing, you need to set the [`trace`](/config/browser/trace) option in the `test.browser` configuration. + +::: warning +Generating trace files is only available when using the [Playwright provider](/config/browser/playwright). +::: + +::: code-group +```ts [vitest.config.js] +import { defineConfig } from 'vitest/config' +import { playwright } from '@vitest/browser-playwright' + +export default defineConfig({ + test: { + browser: { + provider: playwright(), + trace: 'on', + }, + }, +}) +``` +```bash [CLI] +vitest --browser.trace=on +``` +::: + +By default, Vitest will generate a trace file for each test. You can also configure it to only generate traces on test failures by setting `trace` to `'on-first-retry'`, `'on-all-retries'` or `'retain-on-failure'`. The files will be saved in `__traces__` folder next to your test files. The name of the trace includes the project name, the test name, the [`repeats`](/api/test#repeats) count and [`retry`](/api/test#retry) count: + +``` +chromium-my-test-0-0.trace.zip +^^^^^^^^ project name + ^^^^^^ test name + ^ repeat count + ^ retry count +``` + +To change the output directory, you can set the `tracesDir` option in the `test.browser.trace` configuration. This way all traces will be stored in the same directory, grouped by the test file. + +```ts [vitest.config.js] +import { defineConfig } from 'vitest/config' +import { playwright } from '@vitest/browser-playwright' + +export default defineConfig({ + test: { + browser: { + provider: playwright(), + trace: { + mode: 'on', + // the path is relative to the root of the project + tracesDir: './playwright-traces', + }, + }, + }, +}) +``` + +The traces are available in reporters as [annotations](/guide/test-annotations). For example, in the HTML reporter, you can find the link to the trace file in the test details. + +## Trace markers + +You can add explicit named markers to make the trace timeline easier to read: + +```ts +import { page } from 'vitest/browser' + +document.body.innerHTML = ` + +` + +await page.getByRole('button', { name: 'Sign in' }).mark('sign in button rendered') +``` + +Both `page.mark(name)` and `locator.mark(name)` are available. + +You can also group multiple operations under one marker with `page.mark(name, callback)`: + +```ts +await page.mark('sign in flow', async () => { + await page.getByRole('textbox', { name: 'Email' }).fill('john@example.com') + await page.getByRole('textbox', { name: 'Password' }).fill('secret') + await page.getByRole('button', { name: 'Sign in' }).click() +}) +``` + +You can also wrap reusable helpers with [`vi.defineHelper()`](/api/vi#vi-defineHelper) so trace entries point to where the helper is called, not its internals: + +```ts +import { vi } from 'vitest' +import { page } from 'vitest/browser' + +const myRender = vi.defineHelper(async (content: string) => { + document.body.innerHTML = content + await page.elementLocator(document.body).mark('render helper') +}) + +test('renders content', async () => { + await myRender('') // trace points to this line +}) +``` + +## Preview + +To open the trace file, you can use the Playwright Trace Viewer. Run the following command in your terminal: + +```bash +npx playwright show-trace "path-to-trace-file" +``` + +This will start the Trace Viewer and load the specified trace file. + +Alternatively, you can open the Trace Viewer in your browser at https://trace.playwright.dev and upload the trace file there. + +Trace Viewer showing the trace timeline and rendered component +Trace Viewer showing the trace timeline and rendered component + +## Source Location + +When you open a trace, you'll notice that Vitest groups browser interactions and links them back to the exact line in your test that triggered them. This happens automatically for: + +- `expect.element(...)` assertions +- Interactive actions like `click`, `fill`, `type`, `hover`, `selectOptions`, `upload`, `dragAndDrop`, `tab`, `keyboard`, `wheel`, and screenshots + +Under the hood, Playwright still records its own low-level action events as usual. Vitest wraps them with source-location groups so you can jump straight from the trace timeline to the relevant line in your test. + +For anything not covered automatically, you can use `page.mark()` or `locator.mark()` to add your own trace groups — see [Trace markers](#trace-markers) above. diff --git a/guide/browser/trace-view.md b/guide/browser/trace-view.md index a5473aba..52aa8b5e 100644 --- a/guide/browser/trace-view.md +++ b/guide/browser/trace-view.md @@ -1,79 +1,121 @@ -# 追踪视图 {#trace-view} +# Trace View 5.0.0 + +`browser.traceView` records browser interactions as DOM snapshots and lets you replay them step by step in Vitest's built-in trace viewer. It is useful when the live browser view is not enough: you can inspect earlier tests, failed retries, screenshots, assertions, and user actions after the browser has already moved on. + +Trace view is additive to the current browser testing workflow. Enabling it does not force a single debugging mode. You can use it with the normal local browser UI, with a headless browser and Vitest UI, or with the HTML reporter in CI. + +::: tip Trace view, browser UI, and HTML reports + +The normal local browser mode opens the [browser UI](/config/browser/ui), where tests run in a visible iframe. This is useful while developing, but the iframe only shows the current browser state. When another test runs, the previous rendered state is gone. + +`browser.traceView` keeps a replayable record for each test. In local browser UI mode, the trace viewer appears alongside the existing live view so you can keep using the browser UI while also inspecting recorded steps. + +For static output, add the [HTML reporter](/guide/reporters#html-reporter). The same trace viewer can then be opened from the generated report, which is useful for run-mode and CI failures. + +::: -Vitest 浏览器模式支持生成 Playwright 的 [追踪文件](https://playwright.dev/docs/trace-viewer#viewing-remote-traces)。要启用追踪功能,需要在 `test.browser` 配置中设置 [`trace`](/config/browser/trace) 选项。 +::: details Looking for Playwright traces? + +This page now documents Vitest's built-in `browser.traceView` feature. The previous `browser.trace` guide for Playwright traces moved to [Playwright Traces](./playwright-traces). -::: warning -生成追踪文件仅在使用 [Playwright provider](/config/browser/playwright) 时可用。 ::: +## Quick Start + +Enable trace view with the [`browser.traceView`](/config/browser/traceview) option: + ::: code-group -```ts [vitest.config.js] + +```ts [vitest.config.ts] import { defineConfig } from 'vitest/config' -import { playwright } from '@vitest/browser-playwright' export default defineConfig({ test: { browser: { - provider: playwright(), - trace: 'on', + traceView: true, }, }, }) ``` + ```bash [CLI] -vitest --browser.trace=on +vitest --browser.traceView ``` + ::: -默认情况下,Vitest 会为每个测试生成一个追踪文件。你也可以通过设置 `trace` 为 `'on-first-retry'`、`'on-all-retries'` 或 `'retain-on-failure'` 来配置仅在测试失败时生成追踪。这些文件将保存在测试文件相邻的 `__traces__` 文件夹中。追踪文件的名称包括项目名称、测试名称、the [`repeats`](/api/test#repeats) 次数和 [`retry`](/api/test#retry) 次数: +When `browser.traceView` is enabled, tests with recorded traces can be opened in the trace viewer from the [browser UI](/config/browser/ui), [Vitest UI](/guide/ui), and [HTML reporter](/guide/reporters#html-reporter). The viewer has two resizable panes: -``` -chromium-my-test-0-0.trace.zip -^^^^^^^^ 项目名称 - ^^^^^^ 测试名称 - ^ 重复次数 - ^ 重试次数 -``` +- **Step list** (left) — every recorded action, assertion, mark, and lifecycle entry, with name, timing, selector, and source location. Failed actions and assertions are highlighted in red. +- **DOM snapshot** (right) — a reconstruction of the page at the selected step. The interacted element is highlighted in blue. -要更改输出目录,可以在 `test.browser.trace` 配置中设置 `tracesDir` 选项。这样所有追踪文件将按测试文件分组存储在同一目录中。 +Selecting a step also opens its source location in the Editor tab when that location is available. -```ts [vitest.config.js] -import { defineConfig } from 'vitest/config' -import { playwright } from '@vitest/browser-playwright' +Vitest UI trace viewer showing step list and DOM snapshot +Vitest UI trace viewer showing step list and DOM snapshot -export default defineConfig({ - test: { - browser: { - provider: playwright(), - trace: { - mode: 'on', - // 路径相对于项目根目录 - tracesDir: './playwright-traces', - }, - }, - }, -}) -``` +Example replay uses [Vuetify's](https://github.com/vuetifyjs/vuetify) `VDateInput` component. -追踪文件在报告器中作为 [注释](/guide/test-annotations) 形式呈现。例如,在 HTML 报告器中,你可以在测试详情页中找到追踪文件的链接。 - -## Trace markers +## Common Setups + + + +`browser.traceView` records traces. The browser mode, UI, and reporter options determine where you inspect them. + +| Goal | Configuration | Result | +| --- | --- | --- | +| Add trace replay to the normal local browser UI | `vitest --browser.traceView` | Uses the default local headed browser UI and adds trace replay for recorded tests. | +| Debug locally with a headless browser | `vitest --browser.traceView --browser.headless --ui` | The browser runs headless, while Vitest UI shows recorded trace steps and snapshots. | +| Debug locally with a visible browser window and Vitest UI | `vitest --browser.traceView --browser.headless=false --browser.ui=false --ui` | Vitest UI shows recorded trace steps and snapshots, while tests run in a separate headed browser window. | +| Generate a static report for CI or run mode | `vitest run --browser.traceView --reporter=html` | The HTML report includes the trace viewer for recorded tests. | -You can add explicit named markers to make the trace timeline easier to read: +## Relation to Playwright Traces + +`browser.traceView` and [`browser.trace`](/config/browser/trace) are independent features: + +| | `browser.traceView` | `browser.trace` | +| ---------------------- | --------------------------------------------------------- | ---------------------------------------------- | +| Provider support | All providers (playwright, webdriverio, preview) | Playwright only | +| Viewer | Browser UI / Vitest UI / HTML reporter | Playwright Trace Viewer / trace.playwright.dev | +| Format | [rrweb](https://github.com/rrweb-io/rrweb) DOM snapshots | Playwright `.trace.zip` | +| Requires external tool | No | Yes (`npx playwright show-trace`) | + +You can enable both at the same time. See [Playwright Traces](./playwright-traces) for the `browser.trace` workflow. + +## Recorded Steps + +Trace entries are recorded automatically for: + +- `expect.element(...)` assertions +- Interactive actions like `click`, `dblClick`, `tripleClick`, `fill`, `clear`, `type`, `hover`, `selectOptions`, `upload`, `dragAndDrop`, `tab`, `keyboard`, `wheel`, and screenshots +- Test runner lifecycle event (e.g. `vitest:onAfterRetryTask` is recorded after each test and retry run) + +Each entry captures the DOM state at that point, along with timing information, the selector, and the source location that triggered it. + +Element highlighting is best-effort. Some provider-specific selectors, shadow DOM selectors, or elements that are not present in the captured snapshot may not be highlighted. + +## Custom Trace Entries + +You can insert your own named entries with `page.mark()` and `locator.mark()`: ```ts import { page } from 'vitest/browser' -document.body.innerHTML = ` - -` +await page.mark('content rendered') -await page.getByRole('button', { name: 'Sign in' }).mark('sign in button rendered') +await page.getByRole('button', { name: 'Sign in' }).mark('sign in button') ``` -Both `page.mark(name)` and `locator.mark(name)` are available. - -You can also group multiple operations under one marker with `page.mark(name, callback)`: +You can also pass a callback to `page.mark()`. Note that grouping is not currently supported — each inner action is recorded individually, and the mark entry appears at the end: ```ts await page.mark('sign in flow', async () => { @@ -83,44 +125,64 @@ await page.mark('sign in flow', async () => { }) ``` -You can also wrap reusable helpers with [`vi.defineHelper()`](/api/vi#vi-defineHelper) so trace entries point to where the helper is called, not its internals: +Use [`vi.defineHelper()`](/api/vi#vi-defineHelper) to make entries from reusable helpers point to the call site rather than the helper's internals: ```ts import { vi } from 'vitest' import { page } from 'vitest/browser' -const myRender = vi.defineHelper(async (content: string) => { - document.body.innerHTML = content - await page.elementLocator(document.body).mark('render helper') +const renderContent = vi.defineHelper(async (html: string) => { + document.body.innerHTML = html + await page.elementLocator(document.body).mark('render') }) -test('renders content', async () => { - await myRender('') // trace points to this line +test('shows button', async () => { + await renderContent('') // trace entry points here }) ``` -## 预览 {#preview} +## Retries and Repeats + +Each attempt — retry or repeat — is recorded as a separate trace. When a test has multiple attempts, the viewer opens the most recent one by default. You can switch between attempts in the Report tab. + +## Snapshot Fidelity + +By default, trace view captures the DOM tree, attributes, form values, same-origin readable CSS, element scroll positions, viewport size, and window scroll position. Images and canvas pixels are not inlined by default. + +Stylesheets are captured through the browser's CSSOM. Readable `