From 0c89affd49054405c1b0ba3efdf673c7a17adbfb Mon Sep 17 00:00:00 2001 From: noise Date: Sun, 2 Nov 2025 08:07:38 +0800 Subject: [PATCH 01/21] docs(anchor): guide/improving-performance --- guide/improving-performance.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guide/improving-performance.md b/guide/improving-performance.md index 421c3c26..731d27ff 100644 --- a/guide/improving-performance.md +++ b/guide/improving-performance.md @@ -2,9 +2,9 @@ title: 性能优化 | 指南 --- -# 性能优化 +# 性能优化 {#improving-performance} -## 测试隔离 +## 测试隔离 {#test-isolation} 默认情况下,Vitest 在基于[pool](/config/#pool) 的隔离环境中运行每个测试文件: From 6dba6bce29a060cd1b5e795a3630cdb92ac31e6d Mon Sep 17 00:00:00 2001 From: noise Date: Sun, 2 Nov 2025 10:25:29 +0800 Subject: [PATCH 02/21] docs(anchor):/guide/migration --- guide/migration.md | 52 +++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/guide/migration.md b/guide/migration.md index df8d1dc7..2ce929a1 100644 --- a/guide/migration.md +++ b/guide/migration.md @@ -3,11 +3,11 @@ title: 迁移指南 | 指南 outline: deep --- -# 迁移指南 +# 迁移指南 {#migration-guide} ## 迁移到 Vitest 4.0 {#vitest-4} -### V8 Code Coverage Major Changes +### V8 Code Coverage Major Changes {#v8-code-coverage-major-changes} Vitest 的 V8 覆盖率提供器现在使用了更精准的结果映射逻辑,从 Vitest v3 升级后,你可能会看到覆盖率报告的内容有变化。 @@ -18,7 +18,7 @@ Vitest 的 V8 覆盖率提供器现在使用了更精准的结果映射逻辑, - 已移除 `coverage.experimentalAstAwareRemapping` 选项。此功能现已默认启用,并成为唯一的映射方式。 - 现在 V8 提供器也支持 `coverage.ignoreClassMethods`。 -### 移除 `coverage.all` 和 `coverage.extensions` 选项 +### 移除 `coverage.all` 和 `coverage.extensions` 选项 {#removed-options-coverage-all-and-coverage-extensions} 在之前的版本中,Vitest 会默认把所有未覆盖的文件包含到报告中。这是因为 `coverage.all` 默认为 `true`,`coverage.include` 默认为 `**`。这样设计是因为测试工具无法准确判断用户源码所在位置。 @@ -64,7 +64,7 @@ export default defineConfig({ - [覆盖率报告中的文件包含与排除](/guide/coverage.html#including-and-excluding-files-from-coverage-report) - [性能分析 | 代码覆盖率](/guide/profiling-test-performance.html#code-coverage) 了解调试覆盖率生成的方法 -### `spyOn` and `fn` 支持构造函数 +### `spyOn` and `fn` 支持构造函数 {#spyon-and-fn-support-constructors} 在之前版本中,如果你对构造函数使用 `vi.spyOn`,会收到类似 `Constructor requires 'new'` 的错误。从 Vitest 4 开始,所有用 `new` 调用的 mock 都会正确创建实例,而不是调用 `mock.apply`。这意味着 mock 实现必须使用 `function` 或 `class` 关键字,例如: @@ -95,7 +95,7 @@ const mock = new Spy() 请注意,如果此时使用箭头函数,调用 mock 时会报 [` is not a constructor` 错误](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Not_a_constructor)。 -### Mock 的变更 +### Mock 的变更 {#changes-to-mocking} Vitest 4 除新增构造函数支持外,还重构了 mock 的创建机制,一举修复多年累积的模块模拟顽疾;尤其在类与 spy 交互时,行为更易预测、不再烧脑。 @@ -129,7 +129,7 @@ expect(AutoMockedClass.prototype.method).toHaveBeenCalledTimes(4) - 执行 `vi.fn(implementation).mockReset()` 后,`.getMockImplementation()` 现可正确返回原 mock 实现。 - `vi.fn().mock.invocationCallOrder` 现以 `1` 起始,与 Jest 保持一致。 -### 带文件名过滤器的独立模式 +### 带文件名过滤器的独立模式 {#standalone-mode-with-filename-filter} 为了提升用户体验,当 [`--standalone`](/guide/cli#standalone) 与文件名过滤器一起使用时,Vitest 现在会直接开始运行匹配到的文件。 @@ -158,7 +158,7 @@ $ pnpm run test:dev math.test.ts ``` ::: -### Replacing `vite-node` with [Module Runner](https://vite.dev/guide/api-environment-runtimes.html#modulerunner) +### Replacing `vite-node` with [Module Runner](https://vite.dev/guide/api-environment-runtimes.html#modulerunner) {#replacing-vite-node-with-module-runner} Module Runner 已取代 `vite-node`,直接内嵌于 Vite, Vitest 亦移除 SSR 封装,直接调用。主要变更如下: @@ -173,7 +173,7 @@ Vite 已提供外部化机制,但为降低破坏性,仍保留旧方案;[`s 未使用上述高级功能者,升级无感知。 -### `workspace` is Replaced with `projects` +### `workspace` is Replaced with `projects` {#workspace-is-replaced-with-projects} The `workspace` configuration option was renamed to [`projects`](/guide/projects) in Vitest 3.2. They are functionally the same, except you cannot specify another file as the source of your workspace (previously you could specify a file that would export an array of projects). Migrating to `projects` is easy, just move the code from `vitest.workspace.js` to `vitest.config.ts`: @@ -209,7 +209,7 @@ export default defineWorkspace([ // [!code --] ``` ::: -### Browser Provider Rework +### Browser Provider Rework {#browser-provider-rework} In Vitest 4.0, the browser provider now accepts an object instead of a string (`'playwright'`, `'webdriverio'`). The `preview` is no longer a default. This makes it simpler to work with custom options and doesn't require adding `/// { /* ... */ }) // [!code ++] Vitest 的 API 设计兼容 Jest,旨在使从 Jest 迁移尽可能简单。尽管如此,你仍可能遇到以下差异: -### 默认是否启用全局变量 +### 默认是否启用全局变量 {#globals-as-a-default} Jest 默认启用其 [globals API](https://jestjs.io/docs/api)。Vitest 默认不启用。你可以通过配置项 [globals](/config/#globals) 启用全局变量,或者修改代码直接从 `vitest` 模块导入所需 API。 @@ -359,7 +359,7 @@ Jest 的 [`mockReset`](https://jestjs.io/docs/mock-function-api#mockfnmockreset) Vitest 的 [`mockReset`](/api/mock#mockreset) 会将 mock 实现重置为最初的实现。也就是说,使用 `vi.fn(impl)` 创建的 mock,`mockReset` 会将实现重置为 `impl`。 -### `mock.mock` 是持久的 +### `mock.mock` 是持久的 {#mock-mock-is-persistent} Jest 调用 `.mockClear` 后会重建 mock 状态,只能以 getter 方式访问; Vitest 则保留持久引用,可直接复用。 @@ -371,7 +371,7 @@ mock.mockClear() expect(state).toBe(mock.mock) // fails in Jest ``` -### 模块 Mock +### 模块 Mock {#module-mocks} 在 Jest 中,mock 模块时工厂函数返回值即为默认导出。在 Vitest 中,工厂函数需返回包含所有导出的对象。例如,以下 Jest 代码需要改写为: @@ -384,11 +384,11 @@ vi.mock('./some-path', () => ({ // [!code ++] 更多细节请参考 [`vi.mock` API](/api/vi#vi-mock)。 -### 自动 Mock 行为 +### 自动 Mock 行为 {#auto-mocking-behaviour} 与 Jest 不同,Vitest 仅在调用 `vi.mock()` 时加载 `/__mocks__` 中的模块。如果你需要像 Jest 一样在每个测试中自动 mock,可以在 [`setupFiles`](/config/#setupfiles) 中调用 mock。 -### 导入被 Mock 包的原始模块 +### 导入被 Mock 包的原始模块 {#importing-the-original-of-a-mocked-package} 如果只部分 mock 一个包,之前可能用 Jest 的 `requireActual`,Vitest 中应使用 `vi.importActual`: @@ -397,7 +397,7 @@ const { cloneDeep } = jest.requireActual('lodash/cloneDeep') // [!code --] const { cloneDeep } = await vi.importActual('lodash/cloneDeep') // [!code ++] ``` -### 扩展 Mock 到外部库 +### 扩展 Mock 到外部库 {#extends-mocking-to-external-libraries} Jest 默认会扩展 mock 到使用相同模块的外部库。Vitest 需要显式告知要 mock 的第三方库,使其成为源码的一部分,方法是使用 [server.deps.inline](https://vitest.dev/config/#server-deps-inline): @@ -414,21 +414,21 @@ Vitest 的测试名使用 `>` 符号连接,方便区分测试与套件,而 J + `${describeTitle} > ${testTitle}` ``` -### 环境变量 +### 环境变量 {#envs} 与 Jest 类似,Vitest 会将未设置时的 `NODE_ENV` 设为 `test`。Vitest 还有对应 `JEST_WORKER_ID` 的 `VITEST_POOL_ID`(小于等于 `maxThreads`),如果依赖此值,需重命名。Vitest 还暴露 `VITEST_WORKER_ID`,表示唯一的运行中 worker ID,受 `maxThreads` 不影响,随 worker 创建递增。 -### 替换属性 +### 替换属性 {#replace-property} 如果想修改对象,Jest 使用 [replaceProperty API](https://jestjs.io/docs/jest-object#jestreplacepropertyobject-propertykey-value),Vitest 可使用 [`vi.stubEnv`](/api/#vi-stubenv) 或 [`vi.spyOn`](/api/vi#vi-spyon) 达成相同效果。 -### Done 回调 +### Done 回调 {#done-callback} Vitest 不支持回调式测试声明。你可以改写为使用 `async`/`await` 函数,或使用 Promise 来模拟回调风格。 -### Hooks +### Hooks {#hooks} Vitest 中 `beforeAll`/`beforeEach` 钩子可返回 [清理函数](/api/#setup-and-teardown)。因此,如果钩子返回非 `undefined` 或 `null`,可能需改写: @@ -449,7 +449,7 @@ export default defineConfig({ }) ``` -### 类型 +### 类型 {#types} Vitest 没有 Jest 的 `jest` 命名空间,需直接从 `vitest` 导入类型: @@ -459,11 +459,11 @@ import type { Mock } from 'vitest' // [!code ++] let fn: Mock<(name: string) => number> // [!code ++] ``` -### 定时器 +### 定时器 {#timers} Vitest 不支持 Jest 的遗留定时器。 -### 超时 +### 超时 {#timeout} 如果使用了 `jest.setTimeout`,需迁移为 `vi.setConfig`: @@ -472,7 +472,7 @@ jest.setTimeout(5_000) // [!code --] vi.setConfig({ testTimeout: 5_000 }) // [!code ++] ``` -### Vue 快照 +### Vue 快照 {#vue-snapshots} 这不是 Jest 特有的功能,但如果你之前在 vue-cli 预设中使用 Jest,你需要安装 [`jest-serializer-vue`](https://github.com/eddyerburgh/jest-serializer-vue) 包,并在 [`snapshotSerializers`](/config/#snapshotserializers) 中指定它: From e89ae0a8caefbe0d58daa34b6be60a11e4d3898f Mon Sep 17 00:00:00 2001 From: noise Date: Sun, 2 Nov 2025 21:32:32 +0800 Subject: [PATCH 03/21] docs(anchor):guide/migration --- guide/migration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guide/migration.md b/guide/migration.md index 2ce929a1..8a18f428 100644 --- a/guide/migration.md +++ b/guide/migration.md @@ -366,7 +366,7 @@ Jest 调用 `.mockClear` 后会重建 mock 状态,只能以 getter 方式访 ```ts const mock = vi.fn() const state = mock.mock -mock.mockClear() +mock.mockClear()$$ expect(state).toBe(mock.mock) // fails in Jest ``` From 968a72ee30bd607e633a6b1e8c108ccb22e9ae56 Mon Sep 17 00:00:00 2001 From: noise Date: Mon, 3 Nov 2025 10:41:53 +0800 Subject: [PATCH 04/21] typo --- guide/migration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guide/migration.md b/guide/migration.md index 8a18f428..2ce929a1 100644 --- a/guide/migration.md +++ b/guide/migration.md @@ -366,7 +366,7 @@ Jest 调用 `.mockClear` 后会重建 mock 状态,只能以 getter 方式访 ```ts const mock = vi.fn() const state = mock.mock -mock.mockClear()$$ +mock.mockClear() expect(state).toBe(mock.mock) // fails in Jest ``` From 55648f3adc05f165411a9bda20f0628abe9b8b02 Mon Sep 17 00:00:00 2001 From: noise Date: Mon, 3 Nov 2025 10:55:45 +0800 Subject: [PATCH 05/21] docs(anchor): /config --- config/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/index.md b/config/index.md index 44a09b8b..91a9682c 100644 --- a/config/index.md +++ b/config/index.md @@ -2,7 +2,7 @@ outline: deep --- -# 配置索引 +# 配置索引 {#configuring-vitest} 如果我们正在使用 Vite 并且拥有一个 `vite.config` 文件,Vitest 会读取它来匹配我们的 Vite 应用的插件和设置。如果我们想要为测试配置不同的设置,或者我们的并不特别依赖于 Vite,我们我们可以选择: From e9a631462d5bebf8a7989e29a93cb2b2b4e42d0e Mon Sep 17 00:00:00 2001 From: noise Date: Mon, 3 Nov 2025 14:45:17 +0800 Subject: [PATCH 06/21] docs(anchor): /mocking --- api/index.md | 2 +- guide/coverage.md | 2 +- guide/mocking.md | 18 +++++++++--------- guide/snapshot.md | 8 ++++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/api/index.md b/api/index.md index 1dac3df3..9627709b 100644 --- a/api/index.md +++ b/api/index.md @@ -2,7 +2,7 @@ outline: deep --- -# Test API 索引 +# Test API 索引 {#test-api-reference} 下面的类型签名中使用了以下类型: diff --git a/guide/coverage.md b/guide/coverage.md index 7c232e54..ab33fa45 100644 --- a/guide/coverage.md +++ b/guide/coverage.md @@ -357,7 +357,7 @@ if (condition) { if (condition) { ``` -### 示例 +### 示例 {#examples} ::: code-group diff --git a/guide/mocking.md b/guide/mocking.md index 67c89312..395d5c2d 100644 --- a/guide/mocking.md +++ b/guide/mocking.md @@ -3,7 +3,7 @@ title: 模拟对象 | 指南 outline: false --- -# 模拟对象 +# 模拟对象 {#mocking} 在编写测试时,迟早会需要创建一个内部或外部服务的 "fake" 版本。这通常被称为**mocking**。Vitest 通过其 `vi` 辅助工具提供了实用函数来帮助您。我们可以从 `vitest` 中导入它,或者如果启用了 [`global` 配置](/config/#globals),也可以全局访问它。 @@ -26,7 +26,7 @@ Vitest has a comprehensive list of guides regarding mocking: 为了更简单快捷地开始使用模拟,你可以查看下面的备忘单。 -## 备忘单 +## 备忘单 {#cheat-sheet} I want to… @@ -44,7 +44,7 @@ vi.spyOn(exports, 'getter', 'get').mockReturnValue('mocked') 此方法在浏览器模式中无法使用。如需替代方案,请查看 [限制部分](/guide/browser/#spying-on-module-exports)。 ::: -### 对模块中导出的函数进行 mock。 +### 对模块中导出的函数进行 mock。{#mock-an-exported-function} ```ts import * as exports from 'some-path' @@ -52,7 +52,7 @@ vi.spyOn(exports, 'getter', 'get') vi.spyOn(exports, 'setter', 'set') ``` -### 模拟模块导出函数 +### 模拟模块导出函数 {#mock-an-exported-class-implementation} 1. `vi.mock` 的示例: @@ -115,7 +115,7 @@ vi.spyOn(mod, 'SomeClass').mockImplementation(class FakeClass { vi.spyOn 的示例无法在浏览器模式中正常使用。如需替代方案,请查看 [限制部分](/guide/browser/#spying-on-module-exports)。 ::: -### 监听一个函数是否返回了一个对象 +### 监听一个函数是否返回了一个对象 {#spy-on-an-object-returned-from-a-function} 1. 使用 cache 的示例: @@ -155,7 +155,7 @@ const obj = useObject() expect(obj.method).toHaveBeenCalled() ``` -### 模拟部分 module +### 模拟部分 module {#mock-part-of-a-module} ```ts import { mocked, original } from './some-path.js' @@ -175,7 +175,7 @@ mocked() // 是一个 spy 函数 别忘了,这只是 [mocks _external_ access](#mocking-pitfalls)。在本例中,如果 `original` 在内部调用 `mocked`,它将始终调用模块中定义的函数,而不是 mock 工厂中的函数。 ::: -### 模拟当前日期 +### 模拟当前日期 {#mock-the-current-date} 要模拟 `Date` 的时间,你可以使用 `vi.setSystemTime` 辅助函数。 该值将**不会**在不同的测试之间自动重置。 @@ -190,7 +190,7 @@ expect(now.valueOf()).toBe(mockDate.valueOf()) vi.useRealTimers() ``` -### 模拟全局变量 +### 模拟全局变量 {#mock-a-global-variable} 你可以通过为 `globalThis` 赋值或使用 [`vi.stubGlobal`](/api/vi#vi-stubglobal) 助手来设置全局变量。 使用 `vi.stubGlobal` 时,**不会**在不同的测试之间自动重置,除非你启用 [`unstubGlobals`](/config/#unstubglobals) 配置选项或调用 [`vi.unstubAllGlobals`](/api/vi#vi-unstuballglobals)。 @@ -199,7 +199,7 @@ vi.stubGlobal('__VERSION__', '1.0.0') expect(__VERSION__).toBe('1.0.0') ``` -### 模拟 `import.meta.env` +### 模拟 `import.meta.env` {#mock-import-meta-env} 1. 要更改环境变量,你只需为其分配一个新值即可。 该值将**不会**在不同的测试之间自动重置。 diff --git a/guide/snapshot.md b/guide/snapshot.md index 3647e0d0..eee9cfff 100644 --- a/guide/snapshot.md +++ b/guide/snapshot.md @@ -186,7 +186,7 @@ Pretty foo: Object { Vitest 提供了与 [Jest](https://jestjs.io/docs/snapshot-testing) 几乎兼容的快照功能,除少数例外: -#### 1. 快照文件中的注释标头不同 +#### 1. 快照文件中的注释标头不同 {#_1-comment-header-in-the-snapshot-file-is-different} ```diff - // Jest Snapshot v1, https://goo.gl/fbAQLP @@ -195,7 +195,7 @@ Vitest 提供了与 [Jest](https://jestjs.io/docs/snapshot-testing) 几乎兼容 这实际上不会影响功能,但在从 Jest 迁移时可能会影响提交差异。 -#### 2. `printBasicPrototype` 默认为 `false` +#### 2. `printBasicPrototype` 默认为 `false` {#_2-printbasicprototype-is-default-to-false} Jest 和 Vitest 的快照都是由 [`pretty-format`](https://github.com/facebook/jest/blob/main/packages/pretty-format) 支持的。在 Vitest 中,我们将 `printBasicPrototype` 的默认值设置为 `false` 以提供更清晰的快照输出,在 Jest 版本 < 29.0.0 中默认为 `true`。 @@ -243,7 +243,7 @@ export default defineConfig({ }) ``` -#### 3. 使用 V 形 `>` 而非冒号 `:` 作为自定义消息的分隔符 +#### 3. 使用 V 形 `>` 而非冒号 `:` 作为自定义消息的分隔符 {#_3-chevron-is-used-as-a-separator-instead-of-colon-for-custom-messages} 当创建快照文件期间传递自定义消息时,Vitest 使用 V 形 `>` 作为分隔符而不是冒号 `:` 以提高自定义消息可读性。 @@ -269,7 +269,7 @@ exports[`toThrowErrorMatchingSnapshot: hint 1`] = `"error"`; exports[`toThrowErrorMatchingSnapshot > hint 1`] = `[Error: error]`; ``` -#### 4. `toThrowErrorMatchingSnapshot` 和 `toThrowErrorMatchingInlineSnapshot` 的默认 `Error` 快照不同 +#### 4. `toThrowErrorMatchingSnapshot` 和 `toThrowErrorMatchingInlineSnapshot` 的默认 `Error` 快照不同 {#_4-default-error-snapshot-is-different-for-tothrowerrormatchingsnapshot-and-tothrowerrormatchinginlinesnapshot} ```js import { expect, test } from 'vitest' From 7f347656d51bbd1f959b505e41590773d69f7402 Mon Sep 17 00:00:00 2001 From: noise Date: Mon, 3 Nov 2025 15:02:15 +0800 Subject: [PATCH 07/21] docs(anchor): guide/browser/index --- guide/browser/index.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/guide/browser/index.md b/guide/browser/index.md index b34ae51e..109639a7 100644 --- a/guide/browser/index.md +++ b/guide/browser/index.md @@ -14,7 +14,7 @@ outline: deep Vitest UI Vitest UI -## 安装 +## 安装 {#installation} 为方便设置,可使用 `vitest init browser` 命令安装所需的依赖项并创建浏览器配置。 @@ -33,7 +33,7 @@ bunx vitest init browser ``` ::: -### 手动安装 +### 手动安装 {#manual-installation} 我们也可以手动安装软件包。默认情况下,浏览器模式不需要任何额外的 E2E provider 就能在本地运行测试,因为它会复用你现有的浏览器。 @@ -93,7 +93,7 @@ bun add -D vitest @vitest/browser webdriverio ``` ::: -## 配置 +## 配置 {#configuration} 想要在 Vitest 中启用浏览器模式,只需在配置文件中将 `browser.enabled` 设置为 true。下面是一个使用 browser 配置的示例: @@ -575,15 +575,15 @@ test('renders a message', async () => { ``` ::: -## 限制 +## 限制 {#limitations} -### 线程阻塞对话框 +### 线程阻塞对话框 {#thread-blocking-dialogs} 使用 Vitest 浏览器时,需要注意的是像 `alert` 或 `confirm` 这样的线程阻塞对话框不能在本地使用。这是因为它们阻塞了网页,这意味着 Vitest 无法继续与该页面通信,导致执行挂起。 在这类情况下,Vitest 会为相关 API 提供带有默认返回值的内置 mock,从而避免用户不小心使用同步弹窗等 Web API 时导致程序卡死。不过,仍然强烈建议用户自行对这些 Web API 进行 mock,以获得更稳定、可控的测试体验。更多内容可参考 [模拟](/guide/mocking) 章节。 -### 对模块的导出内容进行监听(Spy)。 +### 对模块的导出内容进行监听(Spy)。 {#spying-on-module-exports} 在浏览器模式下,Vitest 依赖浏览器自身对 ESM 模块的原生支持来加载模块。此时,模块的命名空间对象是不可修改的,这与 Node.js 测试中 Vitest 能够对模块执行打补丁不同。因此,你不能对通过 import 导入的对象使用 `vi.spyOn` : From f73589e8278164ac616ebe02398f6872768f4305 Mon Sep 17 00:00:00 2001 From: noise Date: Mon, 3 Nov 2025 15:13:40 +0800 Subject: [PATCH 08/21] docs(anchor): guide/comparisons --- guide/comparisons.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guide/comparisons.md b/guide/comparisons.md index 4e00ce9b..5ad17741 100644 --- a/guide/comparisons.md +++ b/guide/comparisons.md @@ -2,7 +2,7 @@ title: 与其他测试框架对比 | 指南 --- -# 与其他测试框架对比 +# 与其他测试框架对比 {#comparisons-with-other-test-runners} ## Jest From ddc5269f0ca9dc90067e3550c4828f95854c1768 Mon Sep 17 00:00:00 2001 From: noise Date: Mon, 3 Nov 2025 15:21:15 +0800 Subject: [PATCH 09/21] docs(anchor): /guide/browser/why --- guide/browser/why.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/guide/browser/why.md b/guide/browser/why.md index b7870a61..c633ee5b 100644 --- a/guide/browser/why.md +++ b/guide/browser/why.md @@ -3,30 +3,30 @@ title: Why Browser Mode | Browser Mode outline: deep --- -# 什么是浏览器模式? +# 什么是浏览器模式? {#why-browser-mode} -## 动机 +## 动机 {#motivation} 我们开发了 Vitest 浏览器模式功能,以帮助改进测试工作流程并实现更准确、可靠的测试结果。这个实验性的测试 API 增加了在本地浏览器环境中运行测试的功能。在本节中,我们将探讨这个功能背后的动机以及它对测试的好处。 -### 不同的测试方式 +### 不同的测试方式 {#different-ways-of-testing} 有不同的方法来测试 JavaScript 代码。一些测试框架在 Node.js 中模拟浏览器环境,而其他框架则在真实浏览器中运行测试。在这种情况下,[jsdom](https://www.npmjs.com/package/jsdom) 是一个模拟浏览器环境的规范实现,可以与 Jest 或 Vitest 等测试运行器一起使用,而其他测试工具,如 [WebdriverIO](https://webdriver.io/) 或 [Cypress](https://www.cypress.io/) 则允许开发者在真实浏览器中测试他们的应用,或者在 [Playwright](https://playwright.dev/) 的情况下提供一个浏览器引擎。 -### 模拟警告 +### 模拟警告 {#the-simulation-caveat} 在模拟环境(如 jsdom 或 happy-dom)中测试 JavaScript 程序简化了测试设置并提供了易于使用的 API,使它们适用于许多项目并增加了对测试结果的信心。然而,需要牢记的是,这些工具仅模拟浏览器环境而不是实际浏览器,这可能导致模拟环境和真实环境之间存在一些差异。因此,测试结果可能会出现误报或漏报。 为了在测试中获得最高的水平,测试在真实浏览器环境中进行非常重要。这就是为什么我们开发了 Vitest 的浏览器模式功能,允许开发者在浏览器中本地运行测试,并获得更准确、可靠的测试结果。通过浏览器级别的测试,开发者可以更加自信地确保他们的应用在真实场景中能够按照预期工作。 -## 缺点 +## 缺点 {#drawbacks} 使用 Vitest 浏览器时,重要的是要考虑以下缺点: -### 早期开发 +### 早期开发 {#early-development} Vitest 的浏览器模式功能仍处于早期开发阶段。因此,它可能尚未完全优化,可能存在一些尚未解决的错误或问题。建议用户使用独立的浏览器端测试运行程序(如 WebdriverIO、Cypress 或 Playwright)来增强 Vitest 浏览器体验。 -### 更长的初始化时间 +### 更长的初始化时间 {#longer-initialization} Vitest 浏览器在初始化过程中需要启动提供程序和浏览器,这可能需要一些时间。与其他测试模式相比,这可能导致更长的初始化时间。 From d077752d115dce79710e4d1b4da639291261f216 Mon Sep 17 00:00:00 2001 From: noise Date: Mon, 3 Nov 2025 15:25:41 +0800 Subject: [PATCH 10/21] docs(anchor): /guide/browser/config --- guide/browser/config.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guide/browser/config.md b/guide/browser/config.md index bd3cbfdd..6908e527 100644 --- a/guide/browser/config.md +++ b/guide/browser/config.md @@ -1,4 +1,4 @@ -# 浏览器配置参考 +# 浏览器配置参考 {#browser-config-reference} 我们可以通过更新 [配置文件](/config/) 中的 `test.browser` 字段来更改浏览器配置。一个简单的配置文件示例如下: From 628f8afe157b6fad993b20c974c009b0107fafe0 Mon Sep 17 00:00:00 2001 From: noise Date: Mon, 3 Nov 2025 15:27:49 +0800 Subject: [PATCH 11/21] docs(anchor): guide/browser/playwright --- guide/browser/playwright.md | 2 +- guide/browser/webdriverio.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/guide/browser/playwright.md b/guide/browser/playwright.md index 5b455903..170f91df 100644 --- a/guide/browser/playwright.md +++ b/guide/browser/playwright.md @@ -1,4 +1,4 @@ -# 配置 Playwright +# 配置 Playwright {#configuring-playwright} 要使用 playwright 运行测试,你需要安装 [`@vitest/browser-playwright`](https://www.npmjs.com/package/@vitest/browser-playwright) npm 包,并在配置中的 `test.browser.provider` 属性中指定其 `playwright` 导出: diff --git a/guide/browser/webdriverio.md b/guide/browser/webdriverio.md index 9db09303..25784f6a 100644 --- a/guide/browser/webdriverio.md +++ b/guide/browser/webdriverio.md @@ -1,4 +1,4 @@ -# 配置 WebdriverIO +# 配置 WebdriverIO {#configuring-webdriverio} ::: info Playwright 与 WebdriverIO 如果我们的项目尚未使用 WebdriverIO,我们建议从 [Playwright](/guide/browser/playwright) 开始,因为它更易于配置且 API 更灵活。 From 2b72280f9c6aea34ae2edfd34ab1e7f982be6dfb Mon Sep 17 00:00:00 2001 From: noise Date: Mon, 3 Nov 2025 15:37:08 +0800 Subject: [PATCH 12/21] docs(anchor): guide/browser/api --- guide/browser/assertion-api.md | 2 +- guide/browser/commands.md | 6 +++--- guide/browser/context.md | 2 +- guide/browser/locators.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/guide/browser/assertion-api.md b/guide/browser/assertion-api.md index e78ec8ac..c2eb7ad0 100644 --- a/guide/browser/assertion-api.md +++ b/guide/browser/assertion-api.md @@ -1001,7 +1001,7 @@ await expect.element(queryByTestId('prev')).not.toHaveSelection() await expect.element(queryByTestId('next')).toHaveSelection('ne') ``` -## toMatchScreenshot 实验性 +## toMatchScreenshot 实验性 {#tomatchscreenshot} ```ts function toMatchScreenshot( diff --git a/guide/browser/commands.md b/guide/browser/commands.md index 6548ae54..e0a248e3 100644 --- a/guide/browser/commands.md +++ b/guide/browser/commands.md @@ -7,9 +7,9 @@ outline: deep 命令是一个函数,它调用服务器上的另一个函数并将结果传递回浏览器。Vitest 公开了几个可以在浏览器测试中使用的内置命令。 -## 内置命令 +## 内置命令 {#built-in-commands} -### 文件处理 +### 文件处理 {#files-handling} 在浏览器测试中,可借助 `readFile`、`writeFile` 与 `removeFile` 三个 API 完成文件操作。自 Vitest 3.2 起,所有路径均以 [project](/guide/projects) 根目录为基准解析(根目录默认为 `process.cwd()`,可手动重写);旧版本则以当前测试文件所在目录为基准。 @@ -122,7 +122,7 @@ declare module 'vitest/browser' { 如果自定义命令具有相同的名称,则它们将覆盖内置命令。 ::: -### 自定义命令 `playwright` +### 自定义命令 `playwright` {#custom-playwright-commands} Vitest 在命令上下文中公开了几个`playwright`特定属性。 diff --git a/guide/browser/context.md b/guide/browser/context.md index 53ac7b4b..2e5f6c07 100644 --- a/guide/browser/context.md +++ b/guide/browser/context.md @@ -2,7 +2,7 @@ title: Context API | Browser Mode --- -# 上下文 +# 上下文 {#context-api} Vitest 通过 `vitest/browser` 入口点公开上下文模块。从 2.0 开始,它公开了一小部分实用程序,这些实用程序可能在测试中对你有用。 diff --git a/guide/browser/locators.md b/guide/browser/locators.md index 46e8f73b..3640effa 100644 --- a/guide/browser/locators.md +++ b/guide/browser/locators.md @@ -3,7 +3,7 @@ title: Locators | Browser Mode outline: [2, 3] --- -# 定位 +# 定位 {#locators} 定位器是表示一个或多个元素的方式。每个定位器都由一个称为选择器的字符串定义。Vitest 通过提供方便的方法在后台生成这些选择器,从而抽象了选择器。 From 775e6bceb172d9907cd631a76bc7b6d937174689 Mon Sep 17 00:00:00 2001 From: noise Date: Mon, 3 Nov 2025 15:46:57 +0800 Subject: [PATCH 13/21] docs(anchor): guide/browser/component-testing --- guide/browser/component-testing.md | 74 +++++++++++++++--------------- guide/browser/multiple-setups.md | 8 ++-- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/guide/browser/component-testing.md b/guide/browser/component-testing.md index 1b4eca2c..08452109 100644 --- a/guide/browser/component-testing.md +++ b/guide/browser/component-testing.md @@ -3,13 +3,13 @@ title: Component Testing | Guide outline: deep --- -# 组件测试 +# 组件测试 {#component-testing} 组件测试是一种专注于独立测试单个UI组件的测试策略。与测试整个用户流程的端到端测试不同,组件测试验证每个组件单独工作是否正确,使它们运行更快且更容易调试。 Vitest为多个框架提供全面的组件测试支持,包括Vue、React、Svelte、Lit、Preact、Qwik、Solid、Marko等。本指南涵盖了使用Vitest有效测试组件的特定模式、工具和最佳实践。 -## 为什么进行组件测试? +## 为什么进行组件测试?{#why-component-testing} 组件测试位于单元测试和端到端测试之间,提供了几个优势: @@ -18,11 +18,11 @@ Vitest为多个框架提供全面的组件测试支持,包括Vue、React、Sve - **更好的调试** - 更容易定位特定组件中的问题 - **全面的覆盖** - 更容易测试边缘情况和错误状态 -## 组件测试的浏览器模式 +## 组件测试的浏览器模式 {#browser-mode-for-component-testing} Vitest中的组件测试使用**浏览器模式**在真实浏览器环境中运行测试,支持使用Playwright、WebdriverIO或预览模式。这提供了最准确的测试环境,因为你的组件在真实浏览器中运行,具有实际的DOM实现、CSS渲染和浏览器API。 -### 为什么使用浏览器模式? +### 为什么使用浏览器模式?{#why-browser-mode} 浏览器模式是组件测试的推荐方法,因为它提供了最准确的测试环境。与DOM模拟库不同,浏览器模式能够捕获影响用户的真实世界问题。 @@ -35,13 +35,13 @@ Vitest中的组件测试使用**浏览器模式**在真实浏览器环境中运 ::: -### 本指南的目的 +### 本指南的目的 {#purpose-of-this-guide} 本指南专门介绍使用Vitest功能的**组件测试模式和最佳实践**。虽然许多示例使用浏览器模式(因为这是推荐的方法),但这里的重点是组件特定的测试策略,而不是浏览器配置细节。 有关详细的浏览器设置、配置选项和高级浏览器功能,请参阅[浏览器模式文档](/guide/browser/)。 -## 什么是好的组件测试 +## 什么是好的组件测试 {#what-makes-a-good-component-test} 好的组件测试关注**行为和用户体验**,而不是实现细节: @@ -50,7 +50,7 @@ Vitest中的组件测试使用**浏览器模式**在真实浏览器环境中运 - **测试边缘情况** - 错误状态、加载状态、空状态 - **避免测试内部实现** - 状态变量、私有方法、CSS类 -### 组件测试层次结构 +### 组件测试层次结构 {#component-testing-hierarchy} ``` 1. Critical User Paths → Always test these @@ -60,9 +60,9 @@ Vitest中的组件测试使用**浏览器模式**在真实浏览器环境中运 5. Performance → Large datasets, animations ``` -## 组件测试策略 +## 组件测试策略 {#component-testing-strategies} -### 隔离策略 +### 隔离策略 {#isolation-strategy} 通过模拟依赖项来隔离测试组件: @@ -90,7 +90,7 @@ test('UserProfile handles loading and data states', async () => { }) ``` -### 集成策略 +### 集成策略 {#integration-strategy} 测试组件协作和数据流: @@ -121,17 +121,17 @@ test('ProductList filters and displays products correctly', async () => { }) ``` -## Testing Library 集成 +## Testing Library 集成 {#testing-library-integration} 虽然Vitest为流行的框架提供了官方包([`vitest-browser-vue`](https://www.npmjs.com/package/vitest-browser-vue)、[`vitest-browser-react`](https://www.npmjs.com/package/vitest-browser-react)、[`vitest-browser-svelte`](https://www.npmjs.com/package/vitest-browser-svelte)),但你也可以为尚未得到官方支持的框架集成[Testing Library](https://testing-library.com/)。 -### 何时使用 Testing Library +### 何时使用 Testing Library {#when-to-use-testing-library} - 你的框架还没有官方的Vitest浏览器包 - 你正在迁移使用Testing Library的现有测试 - 你更喜欢Testing Library的API来处理特定的测试场景 -### 集成模式 +### 集成模式 {#integration-pattern} 关键是使用 `page.elementLocator()` 来桥接 Testing Library 的 DOM 输出与 Vitest 的浏览器模式 API: @@ -162,7 +162,7 @@ test('Solid component handles user interaction', async () => { }) ``` -### 可用的 Testing Library 包 +### 可用的 Testing Library 包 {#available-testing-library-packages} 与Vitest配合使用效果良好的流行Testing Library包: @@ -175,12 +175,12 @@ test('Solid component handles user interaction', async () => { 如果你的框架后来获得了官方的Vitest支持,你可以通过替换Testing Library的 `render` 函数来逐步迁移,同时保持大部分测试逻辑不变。 ::: -## 最佳实践 +## 最佳实践 {#best-practices} -### 1. 在CI/CD中使用浏览器模式 +### 1. 在CI/CD中使用浏览器模式 {#_1-use-browser-mode-for-ci-cd} 确保测试在真实浏览器环境中运行以获得最准确的测试结果。浏览器模式提供准确的CSS渲染、真实的浏览器API和正确的事件处理。 -### 2. 测试用户交互 +### 2. 测试用户交互 {#_2-test-user-interactions} 使用Vitest的[交互API](/guide/browser/interactivity-api)模拟真实用户行为。使用`page.getByRole()`和`userEvent`方法,如我们的[高级测试模式](#advanced-testing-patterns)所示: ```tsx @@ -192,7 +192,7 @@ await page.getByLabelText(/email/i).fill('user@example.com') // component.setState({ email: 'user@example.com' }) ``` -### 3. 测试可访问性 +### 3. 测试可访问性 {#_3-test-accessibility} 通过测试键盘导航、焦点管理和ARIA属性,确保组件对所有用户都能正常工作。请查看我们的[测试可访问性](#testing-accessibility)示例了解实用模式: ```tsx @@ -204,7 +204,7 @@ await expect.element(document.activeElement).toHaveFocus() await expect.element(modal).toHaveAttribute('aria-modal', 'true') ``` -### 4. 模拟外部依赖 +### 4. 模拟外部依赖 {#_4-mock-external-dependencies} 通过模拟API和外部服务,将测试重点放在组件逻辑上。这使得测试更快、更可靠。请查看我们的[隔离策略](#isolation-strategy)获取示例: ```tsx @@ -218,7 +218,7 @@ vi.mock(import('../components/UserCard'), () => ({ })) ``` -### 5. 使用有意义的测试描述 +### 5. 使用有意义的测试描述 {#_5-use-meaningful-test-descriptions} 编写测试描述时,应解释预期行为,而不是实现细节: ```tsx @@ -231,9 +231,9 @@ test('calls validateEmail function') test('sets isSubmitting state to true') ``` -## 高级测试模式 +## 高级测试模式 {#advanced-testing-patterns} -### 测试组件状态管理 +### 测试组件状态管理 {#testing-component-state-management} ```tsx // Testing stateful components and state transitions @@ -256,7 +256,7 @@ test('ShoppingCart manages items correctly', async () => { }) ``` -### 测试带有数据获取的异步组件 +### 测试带有数据获取的异步组件 {#testing-async-components-with-data-fetching} ```tsx // Option 1: Recommended - Use MSW (Mock Service Worker) for API mocking @@ -298,7 +298,7 @@ test('UserProfile handles loading, success, and error states', async () => { }) ``` -### 测试组件通信 +### 测试组件通信 {#testing-component-communication} ```tsx // Test parent-child component interaction @@ -326,7 +326,7 @@ test('parent and child components communicate correctly', async () => { }) ``` -### 测试带验证的复杂表单 +### 测试带验证的复杂表单 {#testing-complex-forms-with-validation} ```tsx test('ContactForm handles complex validation scenarios', async () => { @@ -373,7 +373,7 @@ test('ContactForm handles complex validation scenarios', async () => { }) ``` -### 测试错误边界 +### 测试错误边界 {#testing-error-boundaries} ```tsx // Test how components handle and recover from errors @@ -406,7 +406,7 @@ test('ErrorBoundary catches and displays errors gracefully', async () => { }) ``` -### 测试可访问性 +### 测试可访问性 {#testing-accessibility} ```tsx test('Modal component is accessible', async () => { @@ -443,9 +443,9 @@ test('Modal component is accessible', async () => { }) ``` -## 调试组件测试 +## 调试组件测试 {#debugging-component-tests} -### 1. 使用浏览器开发者工具 +### 1. 使用浏览器开发者工具 {#_1-use-browser-dev-tools} 浏览器模式在真实浏览器中运行测试,让你可以使用完整的开发者工具。当测试失败时,你可以: @@ -457,7 +457,7 @@ test('Modal component is accessible', async () => { 对于有头模式调试,可以在浏览器配置中临时添加`headless: false`。 -### 2. 添加调试语句 +### 2. 添加调试语句 {#_2-add-debug-statements} 使用策略性日志记录来理解测试失败: @@ -476,7 +476,7 @@ test('debug form validation', async () => { }) ``` -### 3. 检查渲染输出 +### 3. 检查渲染输出 {#_3-inspect-rendered-output} 当组件未按预期渲染时,请系统性地进行调查: @@ -498,7 +498,7 @@ if (button.length === 0) { } ``` -### 4. 验证选择器 +### 4. 验证选择器 {#_4-verify-selectors} 选择器问题是测试失败的常见原因。请系统性地调试它们: @@ -534,7 +534,7 @@ test('debug element queries', async () => { }) ``` -### 5. 调试异步问题 +### 5. 调试异步问题 {#_5-debugging-async-issues} 组件测试经常涉及时机问题: @@ -547,9 +547,9 @@ test('debug async component behavior', async () => { }) ``` -## 从其他测试框架迁移 +## 从其他测试框架迁移 {#migration-from-other-testing-frameworks} -### 从 Jest + Testing Library 迁移 +### 从 Jest + Testing Library 迁移 {#from-jest-testing-library} 大多数 Jest + Testing Library 测试只需少量更改即可工作: @@ -561,13 +561,13 @@ import { render, screen } from '@testing-library/react' // [!code --] import { render } from 'vitest-browser-react' // [!code ++] ``` -### 主要差异 +### 主要差异 {#key-differences} - 使用 `await expect.element()` 而不是 `expect()` 进行 DOM 断言 - 使用 `vitest/browser` 进行用户交互而不是 `@testing-library/user-event` - 浏览器模式提供真实的浏览器环境以进行准确的测试 -## 了解更多 +## 了解更多 {#learn-more} - [浏览器模式文档](/guide/browser/) - [断言API](/guide/browser/assertion-api) diff --git a/guide/browser/multiple-setups.md b/guide/browser/multiple-setups.md index 8f45c80f..95d070a2 100644 --- a/guide/browser/multiple-setups.md +++ b/guide/browser/multiple-setups.md @@ -1,10 +1,10 @@ -# 多种设置 +# 多种设置 {#multiple-setups} 你可以使用 [`browser.instances`](/guide/browser/config#browser-instances) 选项来指定多个不同的浏览器设置。 与 [测试项目](/guide/projects) 相比,使用 `browser.instances` 的最大好处在于缓存效率更高。所有项目会共享同一个 Vite 服务器,因此文件转换和 [依赖的预打包](https://vite.dev/guide/dep-pre-bundling.html) 只需进行一次即可。 -## 多个浏览器 +## 多个浏览器 {#several-browsers} 你可以使用 `browser.instances` 字段来为不同的浏览器指定选项。例如,如果你想在不同的浏览器中运行相同的测试,最小配置将如下所示: @@ -28,7 +28,7 @@ export default defineConfig({ }) ``` -## 不同的设置 +## 不同的设置 {#different-setups} 你还可以独立于浏览器指定不同的配置选项(尽管,实例也可以有 `browser` 字段): @@ -80,7 +80,7 @@ test('ratio works', () => { 请注意,如果你使用相同的浏览器名称,则需要定义自定义的 `name` 值,因为否则 Vitest 会将 `browser` 作为项目名称。 ::: -## 过滤 +## 过滤 {#filtering} 你可以使用 [`--project` 标志](/guide/cli#project) 来过滤要运行的项目。如果未手动分配项目名称,Vitest 会自动将浏览器名称作为项目名称。如果根配置已经有一个名称,Vitest 会将它们合并:`custom` -> `custom (browser)`。 From 05d4109cc0d7e29ef9dcfe8ffece624ea2f879a1 Mon Sep 17 00:00:00 2001 From: noise Date: Mon, 3 Nov 2025 16:00:54 +0800 Subject: [PATCH 14/21] docs(anchor): guide/browser/visual-regression-testing --- guide/browser/visual-regression-testing.md | 52 +++++++++++----------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/guide/browser/visual-regression-testing.md b/guide/browser/visual-regression-testing.md index 3b2f1c34..4037932c 100644 --- a/guide/browser/visual-regression-testing.md +++ b/guide/browser/visual-regression-testing.md @@ -3,13 +3,13 @@ title: 可视化回归测试 outline: [2, 3] --- -# 可视化回归测试 +# 可视化回归测试 {#visual-regression-testing} Vitest 原生支持可视化回归测试。它会自动截取 UI 组件或页面的截图,并与基准图像对比,以捕捉那些非预期的视觉变化。 与只验证功能逻辑的功能测试不同,可视化测试能发现样式异常、布局偏移和渲染错误——这些问题如果没有细致的人工检查,往往会被忽略。 -## 为什么需要可视化回归测试? +## 为什么需要可视化回归测试? {#why-visual-regression-testing} 视觉 bug 不会报错,但它们的外观已经改变。这正是可视化测试的意义所在: @@ -20,7 +20,7 @@ Vitest 原生支持可视化回归测试。它会自动截取 UI 组件或页面 可视化回归测试是 UI 的安全网,确保这些变化在进入生产环境之前就被自动发现并处理。 -## 快速入门 +## 快速入门 {#getting-started} ::: warning 浏览器渲染差异 可视化回归测试对运行环境非常敏感,不同机器生成的截图可能存在差异,常见原因包括: @@ -50,7 +50,7 @@ test('hero section looks correct', async () => { }) ``` -### 创建基准截图 +### 创建基准截图 {#creating-references} 首次运行可视化测试时, Vitest 会生成一张基准( baseline )截图,并提示如下错误信息使测试失败: @@ -70,7 +70,7 @@ Reference screenshot: **请务必提交到版本库**。 ::: -### 截图组织方式 +### 截图组织方式 {#screenshot-organization} Vitest 默认将截图按以下结构保存: @@ -91,7 +91,7 @@ Vitest 默认将截图按以下结构保存: 这种命名方式可避免不同环境生成的截图互相覆盖。 -### 更新基准截图 +### 更新基准截图 {#updating-references} 当你有意修改 UI 时,需要更新基准截图: @@ -101,9 +101,9 @@ $ vitest --update 提交前务必核对更新后的截图,确保改动符合预期。 -## 配置可视化测试 +## 配置可视化测试 {#configuring-visual-tests} -### 全局配置 +### 全局配置 {#global-configuration} 可在 [Vitest 配置文件](/guide/browser/config#browser-expect-tomatchscreenshot) 中设定可视化回归测试的默认规则: @@ -129,7 +129,7 @@ export default defineConfig({ }) ``` -### 单测试配置 +### 单测试配置 {#per-test-configuration} 若某个测试需要不同的比较标准,可在调用时覆盖全局设置: @@ -143,9 +143,9 @@ await expect(element).toMatchScreenshot('button-hover', { }) ``` -## Best Practices +## 最佳实践 {#best-practices} -### 聚焦测试目标元素 +### 聚焦测试目标元素 {#test-specific-elements} 除非确实需要测试整个页面,否则应优先只对目标组件截图,这能显著减少因页面其他部分变化而造成的误报。 @@ -157,7 +157,7 @@ await expect(page).toMatchScreenshot() await expect(page.getByTestId('product-card')).toMatchScreenshot() ``` -### 处理动态内容 +### 处理动态内容 {#handle-dynamic-content} 测试中,如果页面包含诸如时间戳、用户信息或随机值等动态内容,往往会导致结果不一致而造成测试失败。 解决方法有两种:一是模拟这些动态数据的生成源; @@ -173,7 +173,7 @@ await expect(page.getByTestId('profile')).toMatchScreenshot({ }) ``` -### 禁用所有动画 +### 禁用所有动画 {#disable-animations} 动画效果往往会导致测试结果出现波动。为避免这种情况, 可以在测试执行过程中注入一段自定义的 CSS 样式代码,用于禁用所有动画,从而提升测试的稳定性。 @@ -192,7 +192,7 @@ await expect(page.getByTestId('profile')).toMatchScreenshot({ 具体而言,`screenshotOptions` 配置中的 `animations` 选项会默认设为 `"disabled"`,从而确保截图与测试结果的稳定一致。 ::: -### 设置合理的阈值 +### 设置合理的阈值 {#set-appropriate-thresholds} 在视觉回归测试中,阈值调整是一项需要权衡的工作——它取决于页面内容、测试环境、 应用所能容忍的差异范围,且可能因具体测试而有所不同。 @@ -203,7 +203,7 @@ Vitest 并未为像素差异设定默认阈值,这需要由用户根据实际 当 `allowedMismatchedPixelRatio` 与 `allowedMismatchedPixels` 同时设置时, Vitest 会优先采用二者中限制更严格的那一个,以确保测试结果的准确性与一致性。 -### 保持统一的视口大小 +### 保持统一的视口大小 {#set-consistent-viewport-sizes} 浏览器实例的默认窗口尺寸可能存在差异,这会影响视觉回归测试的稳定性。为避免由于尺寸不一致而产生的截图偏差, 建议在测试脚本或浏览器实例配置中显式指定一个固定的视口大小,从而确保测试结果的可重复性与一致性。 @@ -232,13 +232,13 @@ export default defineConfig({ }) ``` -### 使用 Git LFS 管理基准截图 +### 使用 Git LFS 管理基准截图 {#use-git-lfs} 对于规模较大的视觉回归测试套件,建议将基准截图文件存储在 [Git LFS](https://github.com/git-lfs/git-lfs?tab=readme-ov-file) 中。 这样既能避免仓库体积膨胀,又能高效管理和传输这些大尺寸文件,提升团队协作效率。 -## 调试视觉测试失败 +## 调试视觉测试失败 {#debugging-failed-tests} 当视觉回归测试未能通过时, Vitest 会生成三张关键截图,帮助你分析问题所在: @@ -264,7 +264,7 @@ Diff image: tests/.vitest-attachments/button.test.ts/button-chromium-darwin-diff.png ``` -### 如何解读差异图 +### 如何解读差异图 {#understanding-the-diff-image} - **红色像素**:表示参考截图与实际截图之间存在显著差异的区域 - **黄色像素**:由抗锯齿处理带来的细微差异(仅在未忽略抗锯齿时可见) @@ -275,9 +275,9 @@ Diff image: 若只是文字边缘零星出现少量红点,可能只是渲染细节差异,此时适当提高阈值即可解决。 ::: -## 常见问题与解决方案 +## 常见问题与解决方案 {#common-issues-and-solutions} -### 字体渲染引发的误报 +### 字体渲染引发的误报 {#false-positives-from-font-rendering} 由于不同操作系统在字体可用性与渲染方式上差异明显,视觉回归测试中可能会出现“误报”现象。为降低这种风险,可以考虑以下做法: @@ -304,7 +304,7 @@ Diff image: - 使用云端服务或容器化测试环境,确保字体渲染效果在各次测试中保持一致,从而减少系统差异带来的影响; -### 测试不稳定或截图尺寸不一致 +### 测试不稳定或截图尺寸不一致 {#flaky-tests-or-different-screenshot-sizes} 如果测试结果出现随机通过或失败,或者在不同运行中生成的截图尺寸不一致,可以采取以下措施: @@ -315,7 +315,7 @@ Diff image: - 对体积较大的截图适当延长测试的超时时间; - 使用云端服务或容器化环境,确保字体渲染、浏览器配置等保持一致。 -## 团队版视觉回归测试方案 +## 团队版视觉回归测试方案 {#visual-regression-testing-for-teams} 视觉回归测试对环境的稳定性要求极高,而本地开发机并不适合担当这一角色。 @@ -338,7 +338,7 @@ Diff image: 要点在于,将视觉回归测试与常规测试分离运行。 否则,你可能会因截图差异引发的失败日志而浪费数小时进行排查。 -#### 测试组织建议 +#### 测试组织建议 {#organizing-your-tests} 首先,应将视觉回归测试与其他测试隔离管理。 建议单独建立一个 `visual` 文件夹(或根据项目结构选择更合适的目录名称)来存放这些测试用例,以便维护与执行。 @@ -362,7 +362,7 @@ Diff image: - `vitest --project visual` ::: -#### 持续集成( CI )环境配置 +#### 持续集成( CI )环境配置 {#ci-setup} 在 CI 环境中运行视觉回归测试时,需要确保浏览器已正确安装。至于如何安装,则取决于你所使用的 CI 服务提供商及其运行环境。 @@ -402,7 +402,7 @@ Diff image: run: npm run test:visual ``` -#### 更新工作流程 +#### 更新工作流程 {#the-update-workflow} 关键点来了——切勿在每一次 Pull Request 中都自动更新截图, *(那只会带来混乱)*。更稳妥的方式,是建立一个手动触发的工作流程, @@ -676,7 +676,7 @@ env: :::: -### 该选哪一个? +### 该选哪一个? {#so-which-one} 两种方案都可行,关键在于团队最在意的痛点是什么。 From 30bfee98eaf8cae9b8bdf0909e6f7611e502e1bf Mon Sep 17 00:00:00 2001 From: noise Date: Mon, 3 Nov 2025 16:05:33 +0800 Subject: [PATCH 15/21] docs(anchor): move file `trace-viewer` to `trace-view` --- guide/browser/{trace-viewer.md => trace-view.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename guide/browser/{trace-viewer.md => trace-view.md} (99%) diff --git a/guide/browser/trace-viewer.md b/guide/browser/trace-view.md similarity index 99% rename from guide/browser/trace-viewer.md rename to guide/browser/trace-view.md index 2d17ef52..d78db55d 100644 --- a/guide/browser/trace-viewer.md +++ b/guide/browser/trace-view.md @@ -1,4 +1,4 @@ -# Trace Viewer +# Trace View 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`](/guide/browser/config#browser-trace) option in the `test.browser` configuration. From 54f92ccc890fb6dd29541be21e8fd3489042c839 Mon Sep 17 00:00:00 2001 From: noise Date: Mon, 3 Nov 2025 16:26:14 +0800 Subject: [PATCH 16/21] docs(anchor): advanced --- advanced/api/index.md | 2 +- advanced/api/reporters.md | 40 ++++++++++++++++++--------------------- advanced/api/vitest.md | 4 ++-- advanced/metadata.md | 2 +- advanced/reporters.md | 14 +++++++------- advanced/runner.md | 6 +++--- 6 files changed, 32 insertions(+), 36 deletions(-) diff --git a/advanced/api/index.md b/advanced/api/index.md index 804c5b72..7ebdc029 100644 --- a/advanced/api/index.md +++ b/advanced/api/index.md @@ -2,7 +2,7 @@ title: Advanced API --- -# 快速起步 +# 快速起步 {#getting-started} ::: warning 本指南列出了通过 Node.js 脚本运行测试的高级 API。如果你只是想[运行测试](/guide/),你可能不需要这些内容。这些 API 主要用于库作者。 diff --git a/advanced/api/reporters.md b/advanced/api/reporters.md index f1839a3a..a00639ae 100644 --- a/advanced/api/reporters.md +++ b/advanced/api/reporters.md @@ -1,4 +1,4 @@ -# 报告器 「Reporters」 +# 报告器 {#reporters} ::: warning 这是一个高级 API。如果我们只想配置内置的报告器,请阅读 ["Reporters"](/guide/reporters) 指南。 @@ -6,27 +6,23 @@ Vitest 拥有自己的测试运行生命周期。这些生命周期通过报告器的方法来表示: -- [`onInit`](#oninit) -- [`onTestRunStart`](#ontestrunstart) - - [`onTestModuleQueued`](#ontestmodulequeued) - - [`onTestModuleCollected`](#ontestmodulecollected) - - [`onTestModuleStart`](#ontestmodulestart) - - [`onTestSuiteReady`](#ontestsuiteready) - - [`onHookStart(beforeAll)`](#onhookstart) - - [`onHookEnd(beforeAll)`](#onhookend) - - [`onTestCaseReady`](#ontestcaseready) - - [`onTestAnnotate`](#ontestannotate) 3.2.0 - - [`onHookStart(beforeEach)`](#onhookstart) - - [`onHookEnd(beforeEach)`](#onhookend) - - [`onHookStart(afterEach)`](#onhookstart) - - [`onHookEnd(afterEach)`](#onhookend) - - [`onTestCaseResult`](#ontestcaseresult) - - [`onHookStart(afterAll)`](#onhookstart) - - [`onHookEnd(afterAll)`](#onhookend) - - [`onTestSuiteResult`](#ontestsuiteresult) - - [`onTestModuleEnd`](#ontestmoduleend) - - [`onCoverage`](#oncoverage) -- [`onTestRunEnd`](#ontestrunend) +- [报告器 {#reporters}](#报告器-reporters) + - [onInit](#oninit) + - [onBrowserInit 实验性 {#onbrowserinit}](#onbrowserinit-实验性-onbrowserinit) + - [onTestRunStart](#ontestrunstart) + - [onTestRunEnd](#ontestrunend) + - [onCoverage](#oncoverage) + - [onTestModuleQueued](#ontestmodulequeued) + - [onTestModuleCollected](#ontestmodulecollected) + - [onTestModuleStart](#ontestmodulestart) + - [onTestModuleEnd](#ontestmoduleend) + - [onHookStart](#onhookstart) + - [onHookEnd](#onhookend) + - [onTestSuiteReady](#ontestsuiteready) + - [onTestSuiteResult](#ontestsuiteresult) + - [onTestCaseReady](#ontestcaseready) + - [onTestCaseResult](#ontestcaseresult) + - [onTestAnnotate 3.2.0 {#ontestannotate}](#ontestannotate-320-ontestannotate) 除非被跳过,否则单个模块中的测试和 reporters 将按顺序报告。所有跳过的测试将在 reporters /模块的末尾报告。 diff --git a/advanced/api/vitest.md b/advanced/api/vitest.md index f3a5420e..a31c50da 100644 --- a/advanced/api/vitest.md +++ b/advanced/api/vitest.md @@ -31,7 +31,7 @@ Vitest 4 新增了多个 API(它们都标记有 "4.0.0+" 徽章),并移除 测试模式只会调用 `test` 或 `it` 中的函数,并在遇到 `bench` 时抛出错误。此模式使用配置中的 `include` 和 `exclude` 选项来查找测试文件。 -### benchmark 实验性 +### benchmark 实验性 {#benchmark-experimental} 基准测试模式调用 `bench` 函数,并在遇到 `test` 或 `it` 时抛出错误。此模式使用配置中的 `benchmark.include` 和 `benchmark.exclude` 选项来查找基准测试文件。 @@ -47,7 +47,7 @@ Vitest 4 新增了多个 API(它们都标记有 "4.0.0+" 徽章),并移除 这是全局的 [`ViteDevServer`](https://vite.dev/guide/api-javascript#vitedevserver)。 -## state 实验性 +## state 实验性 {#state-experimental} ::: warning 公共 `state` 是一个实验性 API(除了 `vitest.state.getReportedEntity`)。破坏性更改可能不遵循 SemVer,请在使用时固定 Vitest 的版本。 diff --git a/advanced/metadata.md b/advanced/metadata.md index 0a262043..d0b1a7e1 100644 --- a/advanced/metadata.md +++ b/advanced/metadata.md @@ -1,4 +1,4 @@ -# 任务元数据 +# 任务元数据 {#task-metadata} ::: warning Vitest 导出了实验性私有 API。重大更改可能不遵循 semver,使用时请固定 Vitest 的版本。 diff --git a/advanced/reporters.md b/advanced/reporters.md index 4049b3a4..0f376eef 100644 --- a/advanced/reporters.md +++ b/advanced/reporters.md @@ -1,4 +1,4 @@ -# 扩展默认报告器 (Extending Reporters) +# 扩展默认报告器 {#extending-reporters} ::: warning 这是一个高级 API。如果我们只是想配置内置报告器,请阅读 [Reporters](/guide/reporters) 指南。 @@ -6,7 +6,7 @@ 我们可以从 `vitest/reporters` 导入报告器并扩展它们来创建自定义报告器。 -## 扩展内置报告器 (Extending Built-in Reporters) +## 扩展内置报告器 {#extending-built-in-reporters} 一般来说,我们不需要从头开始创建报告器。`vitest` 附带了几个可以扩展的默认报告程序。 @@ -58,7 +58,7 @@ export default defineConfig({ }) ``` -## 报告的任务(Reported Tasks) +## 报告的任务 {#reported-tasks} 建议使用 Reported Tasks API,而不是使用报告器接收到的任务。 @@ -79,11 +79,11 @@ class MyReporter implements Reporter { } ``` -## 导出报告器 (Exported Reporters) +## 导出报告器 {#exported-reporters} `vitest` 附带了一些[内置报告器](/guide/reporters),我们可以开箱即用。 -### 内置报告器: +### 内置报告器: {#built-in-reporters} 1. `DefaultReporter` 2. `DotReporter` @@ -95,10 +95,10 @@ class MyReporter implements Reporter { 8. `HangingProcessReporter` 9. `TreeReporter` -### 基础抽象报告器: +### 基础抽象报告器: {#base-abstract-reporters} 1. `BaseReporter` -### 接口报告器: +### 接口报告器: {#interface-reporters} 1. `Reporter` diff --git a/advanced/runner.md b/advanced/runner.md index 11830594..fea4315c 100644 --- a/advanced/runner.md +++ b/advanced/runner.md @@ -1,4 +1,4 @@ -# 运行器 API +# 运行器 API {#runner-api} ::: warning 注意 这是高级 API。如果你只需要[运行测试](/guide/),你可能不需要这个。它主要被库的作者使用。 @@ -150,7 +150,7 @@ export default class Runner { 快照支持和其他功能是依赖于测试运行器的。如果你想保留这些功能,可以从 `vitest/runners` 导入 `VitestTestRunner` 并将你的测试运行器继承该类。如果你想扩展基准测试功能,它还提供了 `NodeBenchmarkRunner`。 ::: -## 你的任务函数 +## Tasks {#tasks} ::: warning “Runner Tasks API” 是实验性的,主要应在测试运行时使用。Vitest 还暴露了 [“Reported Tasks API”](/advanced/api/test-module),在主线程中工作时(例如在报告器内部)应优先使用。 @@ -275,7 +275,7 @@ export interface TaskResult { } ``` -## 你的任务函数 +## 你的任务函数 {#your-task-function} Vitest 提供了 `createTaskCollector` 工具来创建您自己的 `test` 方法。它的行为与测试相同,但在收集期间会调用自定义方法。 From 0b0baceaf7cd77430bbca362d9b018fda4ba3026 Mon Sep 17 00:00:00 2001 From: noise Date: Mon, 3 Nov 2025 16:28:01 +0800 Subject: [PATCH 17/21] docs(anchor): blog --- blog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blog.md b/blog.md index 687238ba..8eb1cc17 100644 --- a/blog.md +++ b/blog.md @@ -8,6 +8,6 @@ outline: false import BlogIndex from './.vitepress/components/BlogIndex.vue' -# Vitest 博客最新消息 +# Vitest 博客最新消息 {#latest-from-the-vitest-blog} From a7bd955f9629870edbe7edcab31bb4e4573129c6 Mon Sep 17 00:00:00 2001 From: noise Date: Mon, 3 Nov 2025 16:43:47 +0800 Subject: [PATCH 18/21] fix(anchor): trace-view --- .vitepress/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vitepress/config.ts b/.vitepress/config.ts index de8c30a4..01d17675 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -369,7 +369,7 @@ export default ({ mode }: { mode: string }) => { }, { text: '跟踪查看器', - link: '/guide/browser/trace-viewer', + link: '/guide/browser/trace-view', docFooterText: 'Trace Viewer | Browser Mode', }, ], From e19f5d6299f954e63a2330c48546487f27e68502 Mon Sep 17 00:00:00 2001 From: noise Date: Mon, 3 Nov 2025 16:57:45 +0800 Subject: [PATCH 19/21] fix(anchor): fix api/reporters.md --- advanced/api/reporters.md | 40 ++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/advanced/api/reporters.md b/advanced/api/reporters.md index a00639ae..7b034443 100644 --- a/advanced/api/reporters.md +++ b/advanced/api/reporters.md @@ -6,25 +6,27 @@ Vitest 拥有自己的测试运行生命周期。这些生命周期通过报告器的方法来表示: -- [报告器 {#reporters}](#报告器-reporters) - - [onInit](#oninit) - - [onBrowserInit 实验性 {#onbrowserinit}](#onbrowserinit-实验性-onbrowserinit) - - [onTestRunStart](#ontestrunstart) - - [onTestRunEnd](#ontestrunend) - - [onCoverage](#oncoverage) - - [onTestModuleQueued](#ontestmodulequeued) - - [onTestModuleCollected](#ontestmodulecollected) - - [onTestModuleStart](#ontestmodulestart) - - [onTestModuleEnd](#ontestmoduleend) - - [onHookStart](#onhookstart) - - [onHookEnd](#onhookend) - - [onTestSuiteReady](#ontestsuiteready) - - [onTestSuiteResult](#ontestsuiteresult) - - [onTestCaseReady](#ontestcaseready) - - [onTestCaseResult](#ontestcaseresult) - - [onTestAnnotate 3.2.0 {#ontestannotate}](#ontestannotate-320-ontestannotate) - -除非被跳过,否则单个模块中的测试和 reporters 将按顺序报告。所有跳过的测试将在 reporters /模块的末尾报告。 +- [`onInit`](#oninit) +- [`onTestRunStart`](#ontestrunstart) + - [`onTestModuleQueued`](#ontestmodulequeued) + - [`onTestModuleCollected`](#ontestmodulecollected) + - [`onTestModuleStart`](#ontestmodulestart) + - [`onTestSuiteReady`](#ontestsuiteready) + - [`onHookStart(beforeAll)`](#onhookstart) + - [`onHookEnd(beforeAll)`](#onhookend) + - [`onTestCaseReady`](#ontestcaseready) + - [`onTestAnnotate`](#ontestannotate) 3.2.0 + - [`onHookStart(beforeEach)`](#onhookstart) + - [`onHookEnd(beforeEach)`](#onhookend) + - [`onHookStart(afterEach)`](#onhookstart) + - [`onHookEnd(afterEach)`](#onhookend) + - [`onTestCaseResult`](#ontestcaseresult) + - [`onHookStart(afterAll)`](#onhookstart) + - [`onHookEnd(afterAll)`](#onhookend) + - [`onTestSuiteResult`](#ontestsuiteresult) + - [`onTestModuleEnd`](#ontestmoduleend) + - [`onCoverage`](#oncoverage) +- [`onTestRunEnd`](#ontestrunend) 请注意,由于测试模块可以并行运行,Vitest 将并行报告它们。 From 767c6f7feaf2dded2b76dc232f391680289a4c4b Mon Sep 17 00:00:00 2001 From: noise Date: Mon, 3 Nov 2025 17:02:04 +0800 Subject: [PATCH 20/21] fix(anchor): fix api/reporters.md --- advanced/api/reporters.md | 1 + 1 file changed, 1 insertion(+) diff --git a/advanced/api/reporters.md b/advanced/api/reporters.md index 7b034443..7a439e27 100644 --- a/advanced/api/reporters.md +++ b/advanced/api/reporters.md @@ -28,6 +28,7 @@ Vitest 拥有自己的测试运行生命周期。这些生命周期通过报告 - [`onCoverage`](#oncoverage) - [`onTestRunEnd`](#ontestrunend) +除非被跳过,否则单个模块中的测试和 reporters 将按顺序报告。所有跳过的测试将在 reporters /模块的末尾报告。 请注意,由于测试模块可以并行运行,Vitest 将并行报告它们。 本指南列出了所有支持的报告器方法。不过,别忘了,与其创建自己的 报告器 ,我们可以[扩展现有的报告器](/advanced/reporters): From dbf4f37f6e53907b353535b12820d012a9ccdee7 Mon Sep 17 00:00:00 2001 From: noise Date: Mon, 3 Nov 2025 17:03:01 +0800 Subject: [PATCH 21/21] fix(anchor): fix api/reporters.md --- advanced/api/reporters.md | 1 + 1 file changed, 1 insertion(+) diff --git a/advanced/api/reporters.md b/advanced/api/reporters.md index 7a439e27..0cb05919 100644 --- a/advanced/api/reporters.md +++ b/advanced/api/reporters.md @@ -29,6 +29,7 @@ Vitest 拥有自己的测试运行生命周期。这些生命周期通过报告 - [`onTestRunEnd`](#ontestrunend) 除非被跳过,否则单个模块中的测试和 reporters 将按顺序报告。所有跳过的测试将在 reporters /模块的末尾报告。 + 请注意,由于测试模块可以并行运行,Vitest 将并行报告它们。 本指南列出了所有支持的报告器方法。不过,别忘了,与其创建自己的 报告器 ,我们可以[扩展现有的报告器](/advanced/reporters):