diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 0c4a7daf3..5a7f5be4f 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1,7 @@ ### 在提交PR之前,请确保您执行以下操作: -- [ ] 曾阅读过[翻译须知](https://github.com/vitest-dev/docs-cn/issues/391)。 +- [ ] 曾阅读过 [翻译须知](https://github.com/vitest-dev/docs-cn/issues/391)。 - [ ] 检查是否已经有PR以同样的方式解决问题,以避免创建重复。 - [ ] 在此PR中描述正在解决的问题,或引用它解决的问题(例如:`fixes #123`)。 diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index 64eab75a6..1a804963b 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -11,10 +11,10 @@ jobs: timeout-minutes: 10 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Use Node.js lts/* - uses: actions/setup-node@v4 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: lts/* diff --git a/.vitepress/scripts/cli-generator.ts b/.vitepress/scripts/cli-generator.ts index 15795b111..9cd40630f 100644 --- a/.vitepress/scripts/cli-generator.ts +++ b/.vitepress/scripts/cli-generator.ts @@ -87,7 +87,7 @@ const template = options.map((option) => { const cli = option.cli const [page, ...hash] = (title.startsWith('browser.') ? title.slice(8) : title).toLowerCase().split('.') const config = skipConfig.has(title) ? '' : `[${title}](${title.includes('browser.') ? '/config/browser/' : '/config/'}${page}${hash.length ? `#${[page, ...hash].join('-')}` : ''})` - // eslint-disable-next-line e18e/prefer-static-regex + return `### ${title}\n\n- **CLI:** ${cli}\n${config ? `- **Config:** ${config}\n` : ''}\n${option.description.replace(/https:\/\/vitest\.dev\//g, '/')}\n` }).join('\n') diff --git a/advanced/pool.md b/advanced/pool.md index e38ec1853..a1bf6691c 100644 --- a/advanced/pool.md +++ b/advanced/pool.md @@ -1,4 +1,4 @@ -# 自定义运行池 +# 自定义运行池 advanced {#custom-pool} ::: warning 这是一个高级且非常底层的 API。如果你只是想 [运行测试](/guide/),你可能不需要这个。它主要由库作者使用。 diff --git a/api/advanced/metadata.md b/api/advanced/metadata.md index dfba92250..1d4ef7654 100644 --- a/api/advanced/metadata.md +++ b/api/advanced/metadata.md @@ -1,4 +1,4 @@ -# 任务元数据 高级 +# 任务元数据 高级 {#task-metadata} 如果你正在开发自定义报告器或使用 Vitest Node.js API,你可能会发现将在各种上下文中执行的测试中的数据传递给报告器或自定义 Vitest 处理程序很有用。 @@ -42,7 +42,7 @@ Vitest 使用不同的方法与 Node.js 进程进行通信。 该属性也会出现在每个测试的 `json` 报告中,因此请确保数据可以序列化为 JSON。 -另外,请确保在设置[错误属性](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#error_types)之前序列化它们。 +另外,请确保在设置 [错误属性](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#error_types) 之前序列化它们。 ::: 当测试运行完成时,你还可以从 Vitest 状态获取此信息: diff --git a/api/advanced/plugin.md b/api/advanced/plugin.md index 1b447e17e..ea64d5a0a 100644 --- a/api/advanced/plugin.md +++ b/api/advanced/plugin.md @@ -14,6 +14,7 @@ outline: deep Vitest 自 3.1 版起支持 `configureVitest` [插件](https://cn.vite.dev/guide/api-plugin) hook。 ::: code-group + ```ts [only vitest] import type { Vite, VitestPluginContext } from 'vitest/node' @@ -26,6 +27,7 @@ export function plugin(): Vite.Plugin { } } ``` + ```ts [vite and vitest] /// @@ -43,14 +45,16 @@ export function plugin(): Plugin { } } ``` + ::: ::: tip TypeScript -Vitest 通过 `Vite` namespace 重新导出所有仅 Vite 类型的导入,我们可以使用它来保持版本同步。但是,如果我们正在为 Vite 和 Vitest 编写插件,则可以继续使用 `vite` 入口点的 `Plugin` 类型。只需确保我们在某处引用了 `vitest/config` ,以便正确增强 `configureVitest` 即可: +Vitest 通过 `Vite` namespace 重新导出所有仅 Vite 类型的导入,我们可以使用它来保持版本同步。但是,如果我们正在为 Vite 和 Vitest 编写插件,则可以继续使用 `vite` 入口点的 `Plugin` 类型。只需确保我们在某处引用了 `vitest/config`,以便正确增强 `configureVitest` 即可: ```ts /// ``` + ::: 与 [`reporter.onInit`](/api/advanced/reporters#oninit) 不同,此 hooks 在 Vitest 生命周期的早期运行,允许我们更改 `coverage` 和 `reporters` 等配置。更值得注意的变化是,如果我们的插件是在项目中定义而不是在全局配置中定义的,我们可以从 [工作区项目](/guide/projects) 操作全局配置。 @@ -59,7 +63,7 @@ Vitest 通过 `Vite` namespace 重新导出所有仅 Vite 类型的导入,我 ### project -该插件所属的当前[测试项目](./test-project)。 +该插件所属的当前 [测试项目](./test-project)。 ::: warning 浏览器模式 请注意,如果我们依赖浏览器功能,则 `project.browser` 字段尚未设置。请改用 [`reporter.onBrowserInit`](./reporters#onbrowserinit) 事件。 @@ -75,7 +79,7 @@ vitest.config.reporters.push([['my-reporter', {}]]) ``` ::: warning 配置已解析完成 -请注意,Vitest 已经解析了配置,因此某些类型可能与通常的用户配置不同。这也意味着某些属性将不会再次解析,例如 `setupFile` 。如果我们要添加新文件,请确保先解析它。 +请注意,Vitest 已经解析了配置,因此某些类型可能与通常的用户配置不同。这也意味着某些属性将不会再次解析,例如 `setupFile`。如果我们要添加新文件,请确保先解析它。 此时尚未创建记者,因此修改 `vitest.reporters` 将不起作用,因为它将被覆盖。如果我们需要注入自己的记者,请修改配置。 ::: diff --git a/api/advanced/reporters.md b/api/advanced/reporters.md index aabaccba1..d07553002 100644 --- a/api/advanced/reporters.md +++ b/api/advanced/reporters.md @@ -57,6 +57,7 @@ function onInit(vitest: Vitest): Awaitable 请注意,我们还可以通过 [`project`](/api/advanced/test-project) 属性从测试用例、套件和测试模块中访问 `vitest` 实例,但在此方法中存储对 `vitest` 的引用也可能有用。 ::: details 示例 + ```ts import type { Reporter, TestSpecification, Vitest } from 'vitest/node' @@ -78,6 +79,7 @@ class MyReporter implements Reporter { export default new MyReporter() ``` + ::: ## onBrowserInit {#onbrowserinit} @@ -101,6 +103,7 @@ function onTestRunStart( 如果 Vitest 没有找到任何要运行的测试文件,此事件将以空数组调用,然后 [`onTestRunEnd`](#ontestrunend) 将立即被调用。 ::: details 示例 + ```ts import type { Reporter, TestSpecification } from 'vitest/node' @@ -112,6 +115,7 @@ class MyReporter implements Reporter { export default new MyReporter() ``` + ::: ## onTestRunEnd @@ -139,6 +143,7 @@ function onTestRunEnd( 如果 Vitest 没有找到任何要运行的测试文件,此事件将以空的模块和错误数组调用,状态将取决于 [`config.passWithNoTests`](/config/passwithnotests) 的值。 ::: details 示例 + ```ts import type { Reporter, @@ -175,6 +180,7 @@ class MyReporter implements Reporter { export default new MyReporter() ``` + ::: ## onCoverage diff --git a/api/advanced/runner.md b/api/advanced/runner.md index 157e935eb..c45b43fa2 100644 --- a/api/advanced/runner.md +++ b/api/advanced/runner.md @@ -1,4 +1,4 @@ -# 运行器 API advanced +# 运行器 API advanced {#runner-api} ::: warning 注意 这是高级 API。如果你只需要 [运行测试](/guide/),你可能不需要这个。它主要被库的作者使用。 @@ -138,6 +138,7 @@ export default class Runner { } } ``` + ::: ::: warning @@ -229,7 +230,7 @@ interface Test extends TaskBase { } ``` -每个任务都可以有一个 `result` 字段。只有当在套件回调或 `beforeAll`/`afterAll` 回调中抛出错误,阻止了测试的收集时,套件才会有这个字段。测试在它们的回调被调用后总是有这个字段——`state` 和 `errors` 字段根据结果的存在与否而存在。如果在 `beforeEach` 或 `afterEach` 回调中抛出了错误,抛出的错误将出现在 `task.result.errors` 中。 +每个任务都可以有一个 `result` 字段。只有当在套件回调或 `beforeAll`/`afterAll` 回调中抛出错误,阻止了测试的收集时,套件才会有这个字段。测试在它们的回调被调用后总是有这个字段—— `state` 和 `errors` 字段根据结果的存在与否而存在。如果在 `beforeEach` 或 `afterEach` 回调中抛出了错误,抛出的错误将出现在 `task.result.errors` 中。 ```ts export interface TaskResult { diff --git a/api/advanced/test-case.md b/api/advanced/test-case.md index 356e1aab0..530a57a24 100644 --- a/api/advanced/test-case.md +++ b/api/advanced/test-case.md @@ -143,6 +143,7 @@ test('the validation works correctly', ({ task }) => { task.meta.decorated = false }) ``` + 如果测试尚未运行完毕,元数据将是一个空对象,除非它定义了静态元数据: ```ts @@ -157,7 +158,7 @@ test('the validation works correctly', { meta: { decorated: true } }) function result(): TestResult ``` -测试结果。如果测试尚未完成或刚刚开始收集,等于 `TestResultPending` : +测试结果。如果测试尚未完成或刚刚开始收集,等于 `TestResultPending`: ```ts export interface TestResultPending { diff --git a/api/advanced/test-collection.md b/api/advanced/test-collection.md index f19c83db3..8b391f00c 100644 --- a/api/advanced/test-collection.md +++ b/api/advanced/test-collection.md @@ -12,6 +12,7 @@ for (const child of module.children) { console.log(child.type, child.name) } ``` + ::: ## size diff --git a/api/advanced/test-module.md b/api/advanced/test-module.md index 0426f840b..48a0d8993 100644 --- a/api/advanced/test-module.md +++ b/api/advanced/test-module.md @@ -66,7 +66,7 @@ describe('the validation works correctly', (task) => { ``` :::tip -如果元数据是在收集过程中附加的(在 `test` 函数之外),那么它将在自定义报告器中的['onTestModuleCollectd'](./reporters#onTestModuleCollected) 挂钩中可用。 +如果元数据是在收集过程中附加的(在 `test` 函数之外),那么它将在自定义报告器中的 ['onTestModuleCollectd'](./reporters#onTestModuleCollected) 挂钩中可用。 ::: ## diagnostic @@ -126,8 +126,9 @@ interface ImportDuration { 这是 Vite 的 [`DevEnvironment`](https://cn.vite.dev/guide/api-environment),用于转换测试模块中的所有文件。 ::: details 历史 + - `v4.0.15`: 作为实验性功能添加 -::: + ::: ## toTestSpecification 4.1.0 {#totestspecification} diff --git a/api/advanced/test-project.md b/api/advanced/test-project.md index 67016bf85..81dca521f 100644 --- a/api/advanced/test-project.md +++ b/api/advanced/test-project.md @@ -13,6 +13,7 @@ title: TestProject 名称是由用户分配或由 Vitest 解析的唯一字符串。如果用户没有提供名称,Vitest 会尝试加载项目根目录中的 `package.json` 并从中获取 `name` 属性。如果没有 `package.json`,Vitest 默认使用文件夹的名称。内联项目使用数字作为名称(转换为字符串)。 ::: code-group + ```ts [node.js] import { createVitest } from 'vitest/node' @@ -24,6 +25,7 @@ vitest.projects.map(p => p.name) === [ 'custom' ] ``` + ```ts [vitest.config.js] import { defineConfig } from 'vitest/config' @@ -48,6 +50,7 @@ export default defineConfig({ }, }) ``` + ::: ::: info @@ -74,6 +77,7 @@ const config: SerializedConfig = vitest.projects[0].serializedConfig ```ts project.serializedConfig === project.serializedConfig // ❌ ``` + ::: ## globalConfig @@ -120,6 +124,7 @@ function provide( 除了 [`config.provide`](/config/provide) 字段外,还提供了一种向测试提供自定义值的方法。所有值在存储之前都通过 [`structuredClone`](https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone) 进行验证,但 `providedContext` 上的值本身不会被克隆。 ::: code-group + ```ts [node.js] import { createVitest } from 'vitest/node' @@ -128,10 +133,12 @@ const project = vitest.projects.find(p => p.name === 'custom') project.provide('key', 'value') await vitest.start() ``` + ```ts [test.spec.js] import { inject } from 'vitest' const value = inject('key') ``` + ::: 这些值可以动态提供。测试中提供的值将在下次运行时更新。 @@ -144,6 +151,7 @@ export default function setup({ provide }) { provide('wsPort', 3000) } ``` + ::: ## getProvidedContext @@ -240,7 +248,7 @@ Vitest 使用 [fast-glob](https://npmx.dev/package/fast-glob) 来查找测试文 - `test.include`、`test.exclude` 用于查找常规测试文件 - `test.includeSource`、`test.exclude` 用于查找源代码中的测试 - `test.typecheck.include`、`test.typecheck.exclude` 用于查找类型检查测试 -::: + ::: ## matchesTestGlob @@ -286,6 +294,7 @@ const dynamicExample = await project.import('./example.js') dynamicExample !== staticExample // ✅ ``` + ::: ::: info diff --git a/api/advanced/test-specification.md b/api/advanced/test-specification.md index 6ae9a1670..4beefc203 100644 --- a/api/advanced/test-specification.md +++ b/api/advanced/test-specification.md @@ -57,12 +57,14 @@ It's possible to have multiple pools in a single test project with [`typecheck.e 请注意,如果这些行中的至少一行没有测试,整个测试套件将会失败。以下是一个正确的 `testLines` 配置示例: ::: code-group + ```ts [script.js] const specification = project.createSpecification( resolve('./example.test.ts'), [3, 8, 9], ) ``` + ```ts:line-numbers{3,8,9} [example.test.js] import { test, describe } from 'vitest' @@ -75,6 +77,7 @@ describe('a group of tests', () => { // [!code error] test.skip('skipped test') }) ``` + ::: ## testNamePattern 4.1.0 {#testnamepattern} diff --git a/api/advanced/vitest.md b/api/advanced/vitest.md index 6b9f78284..37451ecc0 100644 --- a/api/advanced/vitest.md +++ b/api/advanced/vitest.md @@ -23,7 +23,7 @@ Vitest 4 新增了多个 API(它们都标记有 "4.0.0+" 徽章),并移除 - `globTestSpecs`(使用 [`globTestSpecifications`](#globtestspecifications) 代替) - `globTestFiles`(使用 [`globTestSpecifications`](#globtestspecifications) 代替) - `listFile`(使用 [`getRelevantTestSpecifications`](#getrelevanttestspecifications) 代替) -::: + ::: ## mode @@ -78,7 +78,7 @@ const testCase = vitest.state.getReportedEntity(task) // 新 API ## projects -这是一个数组,里面包含了所有 [测试项目](/api/advanced/test-project) ,这些项目是用户自己定义的。如果用户没有显式指定任何项目,那么这个数组中只会包含一个 [根项目](#getrootproject) 。 +这是一个数组,里面包含了所有 [测试项目](/api/advanced/test-project),这些项目是用户自己定义的。如果用户没有显式指定任何项目,那么这个数组中只会包含一个 [根项目](#getrootproject)。 Vitest 会保证这个数组里至少有一个项目可用。如果用户在命令行里通过 --project 参数指定了不存在的项目名称,Vitest 会在创建这个数组前就报错。 @@ -149,7 +149,7 @@ function getProvidedContext(): ProvidedContext function getProjectByName(name: string): TestProject ``` -此方法通过名称返回项目。类似于调用 `vitest.projects.find` 。 +此方法通过名称返回项目。类似于调用 `vitest.projects.find`。 ::: warning 如果项目不存在,此方法将返回根项目 - 请确保再次检查返回的项目是否是我们要找的项目。 @@ -194,7 +194,7 @@ function getRelevantTestSpecifications( - 如果我们需要获取已知测试文件的规范列表,请使用 [`getModuleSpecifications`](#getmodulespecifications) 代替。 - 如果我们需要获取所有可能的测试文件列表,请使用 [`globTestSpecifications`](#globtestspecifications)。 -::: + ::: ## mergeReports @@ -434,6 +434,7 @@ const dynamicExample = await vitest.import('./example.js') dynamicExample !== staticExample // ✅ ``` + ::: ::: info @@ -483,6 +484,7 @@ function onCancel(fn: (reason: CancelReason) => Awaitable): () => void 注册一个处理程序,当测试运行被 [`vitest.cancelCurrentRun`](#cancelcurrentrun) 取消时调用。 + Since 4.0.10, `onCancel` experimentally returns a teardown function that will remove the listener. Since 4.1.0 this behaviour is considered stable. ## onClose @@ -508,6 +510,7 @@ function onFilterWatchedSpecification( fn: (specification: TestSpecification) => boolean ): void ``` + 注册一个处理程序,当文件更改时调用。此回调应返回 `true` 或 `false`,指示是否需要重新运行测试文件。 通过此方法,我们可以挂钩到默认的观察器逻辑,以延迟或丢弃用户当前不想跟踪的测试: @@ -532,7 +535,7 @@ Vitest 可以根据 `pool` 或 `locations` 选项为同一文件创建不同的 function matchesProjectFilter(name: string): boolean ``` -检查名称是否与当前 [项目过滤器](/guide/cli#project) 匹配。如果没有项目过滤器,则始终返回 `true` 。 +检查名称是否与当前 [项目过滤器](/guide/cli#project) 匹配。如果没有项目过滤器,则始终返回 `true`。 无法通过编程方式更改 `--project` CLI 选项。 @@ -590,7 +593,7 @@ function experimental_parseSpecification( ): Promise ``` -该函数会收集文件内的所有测试,但不会执行它们。它借助 Vite 的 `ssrTransform` ,并在其之上使用 rollup 的 `parseAst` 进行静态分析,从而提取所有可识别的测试用例。 +该函数会收集文件内的所有测试,但不会执行它们。它借助 Vite 的 `ssrTransform`,并在其之上使用 rollup 的 `parseAst` 进行静态分析,从而提取所有可识别的测试用例。 ::: warning 如果 Vitest 无法解析测试的名称,它将在测试或套件中注入一个 `dynamic: true` 属性。`id` 也会带有 `-dynamic` 后缀,以避免破坏已正确收集的测试。 @@ -599,9 +602,9 @@ Vitest 总是在带有 `for` 或 `each` 修饰符的测试,或者名称是动 Vitest 无法做到让动态测试可以被过滤,但你可以使用 `escapeTestName` 函数将带有 `for` 或 `each` 修饰符的测试转换为名称模式: -若 Vitest 无法解析测试名称,它会在测试或套件中注入一个隐藏的 `dynamic: true` 属性,并在 `id` 后追加 `-dynamic` ,以免破坏已正确收集的测试。 +若 Vitest 无法解析测试名称,它会在测试或套件中注入一个隐藏的 `dynamic: true` 属性,并在 `id` 后追加 `-dynamic`,以免破坏已正确收集的测试。 -含 `for` 或 `each` 修饰符的测试,以及名称动态生成的测试(如 `hello ${property}` 或 `'hello' + ${property}` ) , Vitest 一律会注入此属性。 Vitest 仍会为其分配名称,但该名称无法用于过滤测试。 +含 `for` 或 `each` 修饰符的测试,以及名称动态生成的测试(如 `hello ${property}` 或 `'hello' + ${property}`) , Vitest 一律会注入此属性。 Vitest 仍会为其分配名称,但该名称无法用于过滤测试。 Vitest 无法让动态测试支持过滤,但你可以使用 `escapeTestName` 函数,将带 `for` 或 `each` 的测试转换成名称模式: @@ -611,12 +614,13 @@ import { escapeTestName } from 'vitest/node' // 转换为 /hello, .+?/ const escapedPattern = new RegExp(escapeTestName('hello, %s', true)) ``` + ::: ::: warning Vitest 只会收集当前文件内定义的测试,绝不会跟随导入去其他文件搜寻。 -无论是否从 `vitest` 入口点导入, Vitest 都会收集所有 `it` 、`test` 、`suite` 和 `describe` 的定义。 +无论是否从 `vitest` 入口点导入, Vitest 都会收集所有 `it`、`test`、`suite` 和 `describe` 的定义。 ::: ## experimental_parseSpecifications 4.0.0 {#parsespecifications} @@ -650,6 +654,7 @@ export function experimental_getSourceModuleDiagnostic( ``` ::: details 类型 + ```ts export interface ModuleDefinitionLocation { line: number @@ -689,6 +694,7 @@ export interface SourceModuleDiagnostic { untrackedModules: UntrackedModuleDefinitionDiagnostic[] } ``` + ::: 返回模块的诊断信息。如果未提供 [`testModule`](/api/advanced/test-module),则 `selfTime` 和 `totalTime` 将聚合上次运行的所有测试。如果模块未被转换或执行,诊断信息将为空。 @@ -697,6 +703,7 @@ export interface SourceModuleDiagnostic { [浏览器模式](/guide/browser/) 暂不支持。 ::: + ## createReport 5.0.0 {#createreport} ```ts @@ -801,6 +808,7 @@ const filenames: string[] = await report.readdir() ### Report.delete + ```ts function delete(filename: string): Promise ``` diff --git a/api/assert.md b/api/assert.md index f7ed29d7f..7754701dd 100644 --- a/api/assert.md +++ b/api/assert.md @@ -111,7 +111,7 @@ test('assert.strictEqual', () => { - **类型:** `(actual: T, expected: T, message?: string) => void` -断言 `actual` 深度等于 `expected` 。 +断言 `actual` 深度等于 `expected`。 ```ts import { assert, test } from 'vitest' @@ -125,7 +125,7 @@ test('assert.deepEqual', () => { - **类型:** `(actual: T, expected: T, message?: string) => void` -断言 `actual` 不深度等于 `expected` 。 +断言 `actual` 不深度等于 `expected`。 ```ts import { assert, test } from 'vitest' @@ -139,7 +139,7 @@ test('assert.notDeepEqual', () => { - **类型:** `(valueToCheck: number, valueToBeAbove: number, message?: string) => void` -断言 `valueToCheck` 严格大于 (>) `valueToBeAbove` 。 +断言 `valueToCheck` 严格大于 (>) `valueToBeAbove`。 ```ts import { assert, test } from 'vitest' @@ -153,7 +153,7 @@ test('assert.isAbove', () => { - **类型:** `(valueToCheck: number, valueToBeAtLeast: number, message?: string) => void` -断言 `valueToCheck` 大于等于 (>=) `valueToBeAtLeast` 。 +断言 `valueToCheck` 大于等于 (>=) `valueToBeAtLeast`。 ```ts import { assert, test } from 'vitest' @@ -168,7 +168,7 @@ test('assert.isAtLeast', () => { - **类型:** `(valueToCheck: number, valueToBeBelow: number, message?: string) => void` -断言 `valueToCheck` 严格小于 (<) `valueToBeBelow` 。 +断言 `valueToCheck` 严格小于 (<) `valueToBeBelow`。 ```ts import { assert, test } from 'vitest' @@ -182,7 +182,7 @@ test('assert.isBelow', () => { - **类型:** `(valueToCheck: number, valueToBeAtMost: number, message?: string) => void` -断言 `valueToCheck` 小于等于 (<=) `valueToBeAtMost` 。 +断言 `valueToCheck` 小于等于 (<=) `valueToBeAtMost`。 ```ts import { assert, test } from 'vitest' @@ -692,7 +692,7 @@ test('assert.instanceOf', () => { - `(haystack: WeakSet, needle: T, message?: string) => void` - `(haystack: T, needle: Partial, message?: string) => void` -断言 `haystack` 包含 `needle` 。可以用来断言数组中是否包含一个值、字符串中是否包含一个子字符串、或者对象中是否包含一组属性。 +断言 `haystack` 包含 `needle`。可以用来断言数组中是否包含一个值、字符串中是否包含一个子字符串、或者对象中是否包含一组属性。 ```ts import { assert, test } from 'vitest' @@ -716,7 +716,7 @@ test('assert.include', () => { - `(haystack: WeakSet, needle: T, message?: string) => void` - `(haystack: T, needle: Partial, message?: string) => void` -断言 `haystack` 不包含 `needle` 。可以用来断言数组中是否不包含一个值、字符串中是否不包含一个子字符串、或者对象中是否不包含一组属性。 +断言 `haystack` 不包含 `needle`。可以用来断言数组中是否不包含一个值、字符串中是否不包含一个子字符串、或者对象中是否不包含一组属性。 ```ts import { assert, test } from 'vitest' @@ -735,7 +735,7 @@ test('assert.notInclude', () => { - `(haystack: readonly T[] | ReadonlySet | ReadonlyMap, needle: T, message?: string) => void` - `(haystack: T, needle: T extends WeakSet ? never : Partial, message?: string) => void` -断言 `haystack` 包含 `needle` 。可以用来断言数组中是否包含一个值或对象中是否包含一组属性。使用深度相等。 +断言 `haystack` 包含 `needle`。可以用来断言数组中是否包含一个值或对象中是否包含一组属性。使用深度相等。 ```ts import { assert, test } from 'vitest' @@ -756,7 +756,7 @@ test('assert.deepInclude', () => { - `(haystack: readonly T[] | ReadonlySet | ReadonlyMap, needle: T, message?: string) => void` - `(haystack: T, needle: T extends WeakSet ? never : Partial, message?: string) => void` -断言 `haystack` 不包含 `needle` 。可以用来断言数组中是否不包含一个值或对象中是否不包含一组属性。使用深度相等。 +断言 `haystack` 不包含 `needle`。可以用来断言数组中是否不包含一个值或对象中是否不包含一组属性。使用深度相等。 ```ts import { assert, test } from 'vitest' @@ -774,7 +774,7 @@ test('assert.notDeepInclude', () => { - **类型:** `(haystack: any, needle: any, message?: string) => void` -断言 `haystack` 包含 `needle` 。 可以用来断言对象中是否包含一组属性。允许使用点和括号表示法来引用嵌套属性。属性名中的 ‘[]’ 和 ‘.’ 可以使用双反斜杠转义。 +断言 `haystack` 包含 `needle`。 可以用来断言对象中是否包含一组属性。允许使用点和括号表示法来引用嵌套属性。属性名中的 ‘[]’ 和 ‘.’ 可以使用双反斜杠转义。 ```ts import { assert, test } from 'vitest' @@ -789,7 +789,7 @@ test('assert.nestedInclude', () => { - **类型:** `(haystack: any, needle: any, message?: string) => void` -断言 `haystack` 不包含 `needle` 。可以用来断言对象中是否不包含一组属性。允许使用点和括号表示法来引用嵌套属性。属性名中的 ‘[]’ 和 ‘.’ 可以使用双反斜杠转义。 +断言 `haystack` 不包含 `needle`。可以用来断言对象中是否不包含一组属性。允许使用点和括号表示法来引用嵌套属性。属性名中的 ‘[]’ 和 ‘.’ 可以使用双反斜杠转义。 ```ts import { assert, test } from 'vitest' @@ -804,7 +804,7 @@ test('assert.nestedInclude', () => { - **类型:** `(haystack: any, needle: any, message?: string) => void` -断言 `haystack` 包含 `needle` 。可以用来断言对象中是否包含一组属性,同时检查深度相等性。允许使用点和括号表示法来引用嵌套属性。属性名中的 ‘[]’ 和 ‘.’ 可以使用双反斜杠转义。 +断言 `haystack` 包含 `needle`。可以用来断言对象中是否包含一组属性,同时检查深度相等性。允许使用点和括号表示法来引用嵌套属性。属性名中的 ‘[]’ 和 ‘.’ 可以使用双反斜杠转义。 ```ts import { assert, test } from 'vitest' @@ -822,7 +822,7 @@ test('assert.deepNestedInclude', () => { - **类型:** `(haystack: any, needle: any, message?: string) => void` -断言 `haystack` 不包含 `needle` 。可以用来断言对象中是否不包含一组属性,同时检查深度相等性。允许使用点和括号表示法来引用嵌套属性。属性名中的 ‘[]’ 和 ‘.’ 可以使用双反斜杠转义。 +断言 `haystack` 不包含 `needle`。可以用来断言对象中是否不包含一组属性,同时检查深度相等性。允许使用点和括号表示法来引用嵌套属性。属性名中的 ‘[]’ 和 ‘.’ 可以使用双反斜杠转义。 ```ts import { assert, test } from 'vitest' @@ -840,7 +840,7 @@ test('assert.notDeepNestedInclude', () => { - **类型:** `(haystack: any, needle: any, message?: string) => void` -断言 `haystack` 包含 `needle` 。可以用来断言对象中是否包含一组属性,同时忽略继承的属性。 +断言 `haystack` 包含 `needle`。可以用来断言对象中是否包含一组属性,同时忽略继承的属性。 ```ts import { assert, test } from 'vitest' @@ -854,7 +854,7 @@ test('assert.ownInclude', () => { - **类型:** `(haystack: any, needle: any, message?: string) => void` -断言 `haystack` 包含 `needle` 。可以用来断言对象中是否不包含一组属性,同时忽略继承的属性 +断言 `haystack` 包含 `needle`。可以用来断言对象中是否不包含一组属性,同时忽略继承的属性 ```ts import { assert, test } from 'vitest' @@ -875,7 +875,7 @@ test('assert.notOwnInclude', () => { - **类型:** `(haystack: any, needle: any, message?: string) => void` -断言 `haystack` 包含 `needle` 。可以用来断言对象中是否包含一组属性,同时忽略继承的属性并检查深度相等性。 +断言 `haystack` 包含 `needle`。可以用来断言对象中是否包含一组属性,同时忽略继承的属性并检查深度相等性。 ```ts import { assert, test } from 'vitest' @@ -889,7 +889,7 @@ test('assert.deepOwnInclude', () => { - **类型:** `(haystack: any, needle: any, message?: string) => void` -断言 `haystack` 不包含 `needle` 。可以用来断言对象中是否不包含一组属性,同时忽略继承的属性并检查深度相等性。 +断言 `haystack` 不包含 `needle`。可以用来断言对象中是否不包含一组属性,同时忽略继承的属性并检查深度相等性。 ```ts import { assert, test } from 'vitest' @@ -903,7 +903,7 @@ test('assert.notDeepOwnInclude', () => { - **类型:** `(value: string, regexp: RegExp, message?: string) => void` -断言 `value` 匹配正则表达式 `regexp` 。 +断言 `value` 匹配正则表达式 `regexp`。 ```ts import { assert, test } from 'vitest' @@ -917,7 +917,7 @@ test('assert.match', () => { - **类型:** `(value: string, regexp: RegExp, message?: string) => void` -断言 `value` 不匹配正则表达式 `regexp` 。 +断言 `value` 不匹配正则表达式 `regexp`。 ```ts import { assert, test } from 'vitest' @@ -960,7 +960,7 @@ test('assert.notProperty', () => { - **类型:** `(object: T, property: string, value: V, message?: string) => void` -断言 `object` 具有由 `property` 指定的直接或继承属性,其值为 `value` 。使用严格相等检查(===)。 +断言 `object` 具有由 `property` 指定的直接或继承属性,其值为 `value`。使用严格相等检查(===)。 ```ts import { assert, test } from 'vitest' @@ -974,7 +974,7 @@ test('assert.notPropertyVal', () => { - **类型:** `(object: T, property: string, value: V, message?: string) => void` -断言 `object` 没有由 `property` 指定的直接或继承属性,其值为 `value` 。使用严格相等检查(===)。 +断言 `object` 没有由 `property` 指定的直接或继承属性,其值为 `value`。使用严格相等检查(===)。 ```ts import { assert, test } from 'vitest' @@ -989,7 +989,7 @@ test('assert.notPropertyVal', () => { - **类型:** `(object: T, property: string, value: V, message?: string) => void` -断言 `object` 具有由 `property` 指定的直接或继承属性,其值为 `value` 。使用深度相等检查。 +断言 `object` 具有由 `property` 指定的直接或继承属性,其值为 `value`。使用深度相等检查。 ```ts import { assert, test } from 'vitest' @@ -1005,7 +1005,7 @@ test('assert.deepPropertyVal', () => { - **类型:** `(object: T, property: string, value: V, message?: string) => void` -断言 `object` 没有由 `property` 指定的直接或继承属性,其值为 `value` 。使用深度相等检查。 +断言 `object` 没有由 `property` 指定的直接或继承属性,其值为 `value`。使用深度相等检查。 ```ts import { assert, test } from 'vitest' @@ -1055,7 +1055,7 @@ test('assert.deepPropertyVal', () => { - **类型:** `(object: T, property: string, value: any, message?: string) => void` -断言 `object` 具有由 `property` 指定的属性,其值为 `value` 给出。 `property` 可以使用点和方括号表示法进行嵌套引用。使用严格相等检查 (===)。 +断言 `object` 具有由 `property` 指定的属性,其值为 `value` 给出。`property` 可以使用点和方括号表示法进行嵌套引用。使用严格相等检查 (===)。 ```ts import { assert, test } from 'vitest' @@ -1069,7 +1069,7 @@ test('assert.nestedPropertyVal', () => { - **类型:** `(object: T, property: string, value: any, message?: string) => void` -断言 `object` 没有由 `property` 指定的属性,其值为 `value` 给出。 `property` 可以使用点和方括号表示法进行嵌套引用。使用严格相等检查 (===)。 +断言 `object` 没有由 `property` 指定的属性,其值为 `value` 给出。`property` 可以使用点和方括号表示法进行嵌套引用。使用严格相等检查 (===)。 ```ts import { assert, test } from 'vitest' @@ -1084,7 +1084,7 @@ test('assert.notNestedPropertyVal', () => { - **类型:** `(object: T, property: string, value: any, message?: string) => void` -断言 `object` 具有由 `property` 指定的属性,其值为 `value` 给出。 `property` 可以使用点和方括号表示法进行嵌套引用。使用深度相等检查。 +断言 `object` 具有由 `property` 指定的属性,其值为 `value` 给出。`property` 可以使用点和方括号表示法进行嵌套引用。使用深度相等检查。 ```ts import { assert, test } from 'vitest' @@ -1099,7 +1099,7 @@ test('assert.notNestedPropertyVal', () => { - **类型:** `(object: T, property: string, value: any, message?: string) => void` -断言 `object` 没有由 `property` 指定的属性,其值为 `value` 给出。 `property` 可以使用点和方括号表示法进行嵌套引用。使用深度相等检查。 +断言 `object` 没有由 `property` 指定的属性,其值为 `value` 给出。`property` 可以使用点和方括号表示法进行嵌套引用。使用深度相等检查。 ```ts import { assert, test } from 'vitest' @@ -1132,7 +1132,7 @@ test('assert.lengthOf', () => { - **类型:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` -断言 `object` 至少拥有一个提供的 `keys` 。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 +断言 `object` 至少拥有一个提供的 `keys`。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 ```ts import { assert, test } from 'vitest' @@ -1149,7 +1149,7 @@ test('assert.hasAnyKeys', () => { - **类型:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` -断言 `object` 拥有且仅拥有所有提供的 `keys` 。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 +断言 `object` 拥有且仅拥有所有提供的 `keys`。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 ```ts import { assert, test } from 'vitest' @@ -1187,7 +1187,7 @@ test('assert.containsAllKeys', () => { - **类型:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` -断言 `object` 不拥有任何提供的 `keys` 。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 +断言 `object` 不拥有任何提供的 `keys`。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 ```ts import { assert, test } from 'vitest' @@ -1204,7 +1204,7 @@ test('assert.doesNotHaveAnyKeys', () => { - **类型:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` -断言 `object` 至少不拥有一个提供的 `keys` 。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 +断言 `object` 至少不拥有一个提供的 `keys`。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 ```ts import { assert, test } from 'vitest' @@ -1221,7 +1221,7 @@ test('assert.hasAnyKeys', () => { - **类型:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` -断言 `object` 至少拥有一个提供的 `keys` 。由于 Set 和 Map 可以拥有对象作为键,你可以使用这个断言来进行深度比较。你也可以提供一个单独的对象而不是一个 keys 数组,它的键将被用作预期的键集。 +断言 `object` 至少拥有一个提供的 `keys`。由于 Set 和 Map 可以拥有对象作为键,你可以使用这个断言来进行深度比较。你也可以提供一个单独的对象而不是一个 keys 数组,它的键将被用作预期的键集。 ```ts import { assert, test } from 'vitest' @@ -1240,7 +1240,7 @@ test('assert.hasAnyDeepKeys', () => { - **类型:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` -断言 `object` 拥有且仅拥有所有提供的 `keys` 。由于 Set 和 Map 可以拥有对象作为键,你可以使用这个断言来进行深度比较。你也可以提供一个单独的对象而不是一个 keys 数组,它的键将被用作预期的键集。 +断言 `object` 拥有且仅拥有所有提供的 `keys`。由于 Set 和 Map 可以拥有对象作为键,你可以使用这个断言来进行深度比较。你也可以提供一个单独的对象而不是一个 keys 数组,它的键将被用作预期的键集。 ```ts import { assert, test } from 'vitest' @@ -1257,7 +1257,7 @@ test('assert.hasAnyDeepKeys', () => { - **类型:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` -断言 `object` 包含所有提供的 `keys` 。由于 Set 和 Map 可以拥有对象作为键,你可以使用这个断言来进行深度比较。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 +断言 `object` 包含所有提供的 `keys`。由于 Set 和 Map 可以拥有对象作为键,你可以使用这个断言来进行深度比较。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 ```ts import { assert, test } from 'vitest' @@ -1274,7 +1274,7 @@ test('assert.containsAllDeepKeys', () => { - **类型:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` -断言 `object` 不拥有任何提供的 `keys` 。由于 Set 和 Map 可以拥有对象作为键,你可以使用这个断言来进行深度比较。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 +断言 `object` 不拥有任何提供的 `keys`。由于 Set 和 Map 可以拥有对象作为键,你可以使用这个断言来进行深度比较。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 ```ts import { assert, test } from 'vitest' @@ -1291,7 +1291,7 @@ test('assert.doesNotHaveAnyDeepKeys', () => { - **类型:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` -断言 `object` 至少不拥有一个提供的 `keys` 。由于 Set 和 Map 可以拥有对象作为键,你可以使用这个断言来进行深度比较。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 +断言 `object` 至少不拥有一个提供的 `keys`。由于 Set 和 Map 可以拥有对象作为键,你可以使用这个断言来进行深度比较。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 ```ts import { assert, test } from 'vitest' @@ -1356,7 +1356,7 @@ test('assert.doesNotThrow', () => { - **类型:** `(val1: OperatorComparable, operator: Operator, val2: OperatorComparable, message?: string) => void` -使用 `operator` 比较 `val1` 和 `val2` 。 +使用 `operator` 比较 `val1` 和 `val2`。 ```ts import { assert, test } from 'vitest' @@ -1627,7 +1627,7 @@ test('assert.oneOf', () => { - **类型:** `(modifier: Function, object: T, property: string, message?: string) => void` -断言 `函数` 用于修改 `property` 所属 `object` 。 +断言 `函数` 用于修改 `property` 所属 `object`。 ```ts import { assert, test } from 'vitest' @@ -1643,7 +1643,7 @@ test('assert.changes', () => { - **类型:** `(modifier: Function, object: T, property: string, change: number, message?: string) => void` -断言 `函数` 通过 `change` 修改 `property` 所属的 `object` 。 +断言 `函数` 通过 `change` 修改 `property` 所属的 `object`。 ```ts import { assert, test } from 'vitest' @@ -1659,7 +1659,7 @@ test('assert.changesBy', () => { - **类型:** `(modifier: Function, object: T, property: string, message?: string) => void` -断言 `函数` 不会通过 `change` 修改 `property` 或 `函数` 返回值的 `object` 。 +断言 `函数` 不会通过 `change` 修改 `property` 或 `函数` 返回值的 `object`。 ```ts import { assert, test } from 'vitest' diff --git a/api/browser/assertions.md b/api/browser/assertions.md index 477c3ea89..eafe3bc55 100644 --- a/api/browser/assertions.md +++ b/api/browser/assertions.md @@ -12,9 +12,10 @@ Vitest 默认提供了一组丰富的 DOM 断言,这些断言源自 [`@testing ```ts /// ``` + ::: -浏览器中的测试由于其异步特性,可能会不一致地失败。因此,即使条件延迟(如超时、网络请求或动画),也必须有办法保证断言成功。为此,Vitest 通过 [`expect.poll`](/api/expect#poll)和 `expect.element` API 提供了可重试的断言: +浏览器中的测试由于其异步特性,可能会不一致地失败。因此,即使条件延迟(如超时、网络请求或动画),也必须有办法保证断言成功。为此,Vitest 通过 [`expect.poll`](/api/expect#poll) 和 `expect.element` API 提供了可重试的断言: ```ts import { expect, test } from 'vitest' @@ -54,7 +55,7 @@ interface ExpectPollOptions { ``` ::: tip -`expect.element` 是 `expect.poll(() => element)`的简写,工作方式完全相同。 +`expect.element` 是 `expect.poll(() => element)` 的简写,工作方式完全相同。 `toHaveTextContent` 以及其他所有断言在常规的 `expect` 中仍然可用,但没有内置的重试机制: @@ -62,6 +63,7 @@ interface ExpectPollOptions { // 如果 .textContent 不是 `'Error!'`,则会立即失败。 expect(banner).toHaveTextContent('Error!') ``` + ::: ## toBeDisabled @@ -310,6 +312,7 @@ function toBeInViewport(options: { ratio?: number }): Promise 该方法通过 IntersectionObserver API 检测元素是否位于当前视口内。 可通过 ratio 参数指定元素在视口中的最小可见比例(取值范围为 0~1): + ```ts // 检测指定元素是否在视口中 await expect.element(page.getByText('Welcome')).toBeInViewport() @@ -433,7 +436,7 @@ function toHaveAccessibleErrorMessage(message?: string | RegExp): Promise 这允许你断言一个元素具有预期的 [可访问错误消息](https://w3c.github.io/aria/#aria-errormessage)。 -你可以传递预期的可访问错误消息的确切字符串。或者,你可以通过传递正则表达式或使用 [`expect.stringContaining`](/api/expect#expect-stringcontaining) 或 [`expect.stringMatching`](/api/expect#expect-stringmatching)来进行部分匹配。 +你可以传递预期的可访问错误消息的确切字符串。或者,你可以通过传递正则表达式或使用 [`expect.stringContaining`](/api/expect#expect-stringcontaining) 或 [`expect.stringMatching`](/api/expect#expect-stringmatching) 来进行部分匹配。 ```html ``` -这允许你断言一个元素具有预期的[可访问名称](https://w3c.github.io/accname/)。例如,它有助于断言表单元素和按钮是否被正确标记。 +这允许你断言一个元素具有预期的 [可访问名称](https://w3c.github.io/accname/)。例如,它有助于断言表单元素和按钮是否被正确标记。 你可以传递预期的可访问名称的确切字符串,或者通过传递正则表达式进行部分匹配,也可以使用 [`expect.stringContaining`](/api/expect#expect-stringcontaining) 或 [`expect.stringMatching`](/api/expect#expect-stringmatching)。 @@ -512,7 +515,7 @@ await expect.element(getByTestId('input-title')).toHaveAccessibleName() function toHaveAttribute(attribute: string, value?: unknown): Promise ``` -这允许你检查给定的元素是否具有某个属性。你还可以选择性地验证该属性是否具有特定的预期值或使用 [`expect.stringContaining`](/api/expect#expect-stringcontaining) 或 [`expect.stringMatching`](/api/expect#expect-stringmatching)进行部分匹配。 +这允许你检查给定的元素是否具有某个属性。你还可以选择性地验证该属性是否具有特定的预期值或使用 [`expect.stringContaining`](/api/expect#expect-stringcontaining) 或 [`expect.stringMatching`](/api/expect#expect-stringmatching) 进行部分匹配。 ```html @@ -940,6 +943,7 @@ await expect.element(getByTestId('link-invalid')).toHaveRole('generic') await expect.element(getByTestId('switch')).toHaveRole('switch') // ✅ await expect.element(getByTestId('switch')).toHaveRole('alert') // ❌ ``` + ::: ## toHaveSelection @@ -948,7 +952,7 @@ await expect.element(getByTestId('switch')).toHaveRole('alert') // ❌ function toHaveSelection(selection?: string): Promise ``` -这允许断言某个元素具有一个[文本选择](https://developer.mozilla.org/en-US/docs/Web/API/Selection)。 +这允许断言某个元素具有一个 [文本选择](https://developer.mozilla.org/en-US/docs/Web/API/Selection)。 这在检查元素内是否选择了文本或部分文本时非常有用。该元素可以是文本类型的输入框、`textarea`,或者是任何包含文本的其他元素,例如段落、`span`、`div` 等。 @@ -1042,7 +1046,7 @@ function toMatchScreenshot( ::: ::: tip -若截图对比因**有意变更**而失败,可在监听模式下按 `u` 键,或运行测试时加上 `-u`/`--update` 标志,以更新基准图。 +若截图对比因 **有意变更** 而失败,可在监听模式下按 `u` 键,或运行测试时加上 `-u`/`--update` 标志,以更新基准图。 ::: ```html @@ -1072,7 +1076,9 @@ await expect.element(getByTestId('button')).toMatchScreenshot('fancy-button', { }, }) ``` + + ### Options - `comparatorName: "pixelmatch" = "pixelmatch"` @@ -1111,6 +1117,7 @@ await expect.element(getByTestId('button')).toMatchScreenshot('fancy-button', { }, }) ``` + ::: - `screenshotOptions: object` @@ -1127,7 +1134,9 @@ await expect.element(getByTestId('button')).toMatchScreenshot('fancy-button', { 等待获取稳定截图的时间。 设为 `0` 可禁用超时,但如果无法确定稳定截图,进程将不会结束。 + + #### `"pixelmatch"` comparator options The `"pixelmatch"` comparator uses [`@blazediff/core`](https://blazediff.dev/docs/core) under the hood. The following options are available when using it: diff --git a/api/browser/commands.md b/api/browser/commands.md index 3f43b0794..6588c35e1 100644 --- a/api/browser/commands.md +++ b/api/browser/commands.md @@ -3,7 +3,7 @@ title: 命令 | 浏览器模式 outline: deep --- -# 命令 +# 命令 {#commands} 命令是一个函数,它调用服务器上的另一个函数并将结果传递回浏览器。Vitest 公开了几个可以在浏览器测试中使用的内置命令。 @@ -19,6 +19,7 @@ outline: deep 此 API 遵循 [`server.fs`](https://vitejs.dev/config/server-options.html#server-fs-allow) 出于安全原因的限制。 + If [`browser.api.allowWrite`](/config/browser/api) or [`api.allowWrite`](/config/api#api-allowwrite) are disabled, `writeFile` and `removeFile` functions won't do anything. ::: @@ -59,7 +60,7 @@ expect(input).toHaveValue('a') ``` ::: warning -CDP session仅适用于 `playwright` provider,并且仅在使用 `chromium` 浏览器时有效。有关详细信息,请参阅 playwright 的 [`CDPSession`](https://playwright.dev/docs/api/class-cdpsession)文档。 +CDP session 仅适用于 `playwright` provider,并且仅在使用 `chromium` 浏览器时有效。有关详细信息,请参阅 playwright 的 [`CDPSession`](https://playwright.dev/docs/api/class-cdpsession) 文档。 ::: ## 自定义命令 {#custom-commands} @@ -126,14 +127,14 @@ declare module 'vitest/browser' { 如果自定义命令具有相同的名称,则它们将覆盖内置命令。 ::: -### 自定义 `playwright` 命令 {#custom-playwright-commands} +### 自定义 `playwright` 命令 {#custom-playwright-commands} -Vitest 在命令上下文中公开了几个`playwright`特定属性。 +Vitest 在命令上下文中公开了几个 `playwright` 特定属性。 -- `page`引用包含测试 iframe 的完整页面。这是协调器 HTML,为避免出现问题,最好不要碰它。 +- `page` 引用包含测试 iframe 的完整页面。这是协调器 HTML,为避免出现问题,最好不要碰它。 - `frame` 是一个异步方法,用于解析测试器 [`Frame`](https://playwright.dev/docs/api/class-frame)。它的 API 与 `page` 类似,但不支持某些方法。如果您需要查询元素,应优先使用 `context.iframe` 代替,因为它更稳定、更快速。 - `iframe` 是一个 [`FrameLocator`](https://playwright.dev/docs/api/class-framelocator),用于查询页面上的其他元素。 -- `context` 是指唯一的[BrowserContext](https://playwright.dev/docs/api/class-browsercontext)。 +- `context` 是指唯一的 [BrowserContext](https://playwright.dev/docs/api/class-browsercontext)。 ```ts import { BrowserCommand } from 'vitest/node' diff --git a/api/browser/context.md b/api/browser/context.md index 12d649ff1..e284eb15a 100644 --- a/api/browser/context.md +++ b/api/browser/context.md @@ -64,6 +64,7 @@ export const commands: BrowserCommands 使用 [Commands API](/api/browser/commands) 如果您需要访问 Playwright 的 `page` 对象。 + ```ts export const page: { /** @@ -179,7 +180,7 @@ await frame.click() // ❌ 不可用 ::: danger IMPORTANT 目前,`frameLocator` 方法仅支持 `playwright` 提供者。 -交互方法(如 `click` 或 `fill`)在 iframe 内的元素上始终可用,但使用 `expect.element` 进行断言时要求 iframe 具有[同源策略](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy)。 +交互方法(如 `click` 或 `fill`)在 iframe 内的元素上始终可用,但使用 `expect.element` 进行断言时要求 iframe 具有 [同源策略](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy)。 ::: ## `cdp` @@ -187,7 +188,7 @@ await frame.click() // ❌ 不可用 `cdp` 导出返回当前的 Chrome DevTools 协议会话。它主要用于库作者在其基础上构建工具。 ::: warning -CDP 会话仅适用于 `playwright` provider,并且仅在使用 `chromium` 浏览器时有效。有关详细信息,请参阅 playwright 的 [`CDPSession`](https://playwright.dev/docs/api/class-cdpsession)文档。 +CDP 会话仅适用于 `playwright` provider,并且仅在使用 `chromium` 浏览器时有效。有关详细信息,请参阅 playwright 的 [`CDPSession`](https://playwright.dev/docs/api/class-cdpsession) 文档。 ::: ```ts @@ -323,22 +324,26 @@ const html = utils.prettyDOM(element, undefined, { **Common Patterns:** Filter out scripts and styles: + ```ts utils.configurePrettyDOM({ filterNode: 'script, style' }) ``` Hide specific elements with data attributes: + ```ts utils.configurePrettyDOM({ filterNode: '[data-test-hide]' }) ``` Hide nested content within an element: + ```ts // Hides all children of elements with data-test-hide-content utils.configurePrettyDOM({ filterNode: '[data-test-hide-content] *' }) ``` Combine multiple selectors: + ```ts utils.configurePrettyDOM({ filterNode: 'script, style, [data-test-hide], svg' diff --git a/api/browser/interactivity.md b/api/browser/interactivity.md index cdf212112..4f9c4edba 100644 --- a/api/browser/interactivity.md +++ b/api/browser/interactivity.md @@ -57,6 +57,7 @@ beforeEach(async () => { await userEvent.unhover(document.body) }) ``` + ::: ## userEvent.click @@ -101,6 +102,7 @@ await userEvent.keyboard('{/Shift}') ``` 使用 Playwright: + ```ts await userEvent.click(element, { modifiers: ['Shift'] }) ``` @@ -242,7 +244,7 @@ function fill( ): Promise ``` -为 `input` 、 `textarea` 或 `contenteditable` 元素设置新的内容,并且在赋值前会先清空其中已有的文本。 +为 `input`、`textarea` 或 `contenteditable` 元素设置新的内容,并且在赋值前会先清空其中已有的文本。 ```ts import { page, userEvent } from 'vitest/browser' @@ -262,7 +264,7 @@ test('update input', async () => { 该方法聚焦元素、填充元素并在填充后触发一个 `input` 事件。您可以使用空字符串来清除字段。 ::: tip -该 API 比使用 [`userEvent.type`](#userevent-type) 或 [`userEvent.keyboard`](#userevent-keyboard) 更快,但**不支持** [user-event `keyboard` syntax](https://testing-library.com/docs/user-event/keyboard) (例如,`{Shift}{selectall}`)。 +该 API 比使用 [`userEvent.type`](#userevent-type) 或 [`userEvent.keyboard`](#userevent-keyboard) 更快,但 **不支持** [user-event `keyboard` syntax](https://testing-library.com/docs/user-event/keyboard)(例如,`{Shift}{selectall}`)。 在不需要输入特殊字符或对按键事件进行细粒度控制的情况下,我们建议使用此 API 而不是 [`userEvent.type`](#userevent-type)。 ::: @@ -307,7 +309,7 @@ test('trigger keystrokes', async () => { function tab(options?: UserEventTabOptions): Promise ``` -发送一个 `Tab` 键事件。这是`userEvent.keyboard('{tab}')`的简写。 +发送一个 `Tab` 键事件。这是 `userEvent.keyboard('{tab}')` 的简写。 ```ts import { page, userEvent } from 'vitest/browser' @@ -510,7 +512,7 @@ function unhover( 其作用与 [`userEvent.hover`](#userevent-hover) 相同,但会将光标移至 `document.body` 元素。 ::: warning -默认情况下,光标位置位于 body 元素的 "某个" 可见位置(在 `playwright` provider中)或中心位置(在 `webdriverio` provider中),因此如果当前悬停的元素已经位于相同位置,本方法将不起作用。 +默认情况下,光标位置位于 body 元素的 "某个" 可见位置(在 `playwright` provider 中)或中心位置(在 `webdriverio` provider 中),因此如果当前悬停的元素已经位于相同位置,本方法将不起作用。 ::: ```ts @@ -580,7 +582,7 @@ function dragAndDrop( ): Promise ``` -将源元素拖到目标元素的顶部。不要忘记,源元素的`draggable`属性必须设置为 `true`。 +将源元素拖到目标元素的顶部。不要忘记,源元素的 `draggable` 属性必须设置为 `true`。 ```ts import { page, userEvent } from 'vitest/browser' @@ -598,7 +600,7 @@ test('drag and drop works', async () => { ``` ::: warning - `preview` provider不支持此 API。 +`preview` provider 不支持此 API。 ::: 相关链接: diff --git a/api/browser/locators.md b/api/browser/locators.md index 023a8bc1c..f741806f8 100644 --- a/api/browser/locators.md +++ b/api/browser/locators.md @@ -13,6 +13,7 @@ outline: [2, 3] 本页介绍了 API 的使用。为了更好地了解定位器及其用法,请阅读 [Playwright 的“定位器”文档](https://playwright.dev/docs/locators)。 ::: + ::: tip Difference from `testing-library` Vitest's `page.getBy*` methods return a locator object, not a DOM element. This makes locator queries composable and allows Vitest to retry interactions and assertions when needed. @@ -34,6 +35,7 @@ const deleteButton = page await deleteButton.click() await expect.element(deleteButton).toBeEnabled() ``` + ::: ## getByRole @@ -510,6 +512,7 @@ page.getByRole('button') .or(page.getByRole('link')) .click() // ❌ 匹配到多个元素 ``` + ::: ## filter @@ -563,6 +566,7 @@ page.getByRole('article') .filter({ has: page.getByRole('button', { name: 'delete row' }) }) .filter({ has: page.getByText('Vitest') }) ``` + ::: ### hasNot @@ -744,7 +748,7 @@ await page.getByRole('img', { name: 'Rose' }).unhover() function fill(text: string, options?: UserEventFillOptions): Promise ``` -为当前的 `input` 、`textarea` 或 `contenteditable` 元素赋值。 +为当前的 `input`、`textarea` 或 `contenteditable` 元素赋值。 ```ts import { page } from 'vitest/browser' @@ -930,6 +934,7 @@ It is called automatically when locator is used with `expect.element` every time ```ts await expect.element(page.getByRole('button')).toBeDisabled() ``` + ::: 考虑以下 DOM 结构: @@ -1054,7 +1059,7 @@ function all(): Locator[] 在内部,此方法调用 `.elements` 并使用 [`page.elementLocator`](/api/browser/context#page) 包装每个元素。 -- [更多内容请参阅 `locator.elements()`](#elements) +- [更多内容请参阅 `locator.elements()`](#elements) ## Properties @@ -1086,6 +1091,7 @@ test('works correctly', async () => { await commands.test(page.getByText('Hello')) // ✅ }) ``` + ::: ### length diff --git a/api/browser/react.md b/api/browser/react.md index 00aae59da..dcbaa91bb 100644 --- a/api/browser/react.md +++ b/api/browser/react.md @@ -49,6 +49,7 @@ import { render } from 'vitest-browser-react' const screen = render() // [!code --] const screen = await render() // [!code ++] ``` + ::: ### 选项 {#options} @@ -155,7 +156,9 @@ function debug( ```ts function rerender(ui: React.ReactNode): Promise ``` + + Also records a `react.rerender` trace mark in the [Trace View](/guide/browser/trace-view). It is better if you test the component that's doing the prop updating to ensure that the props are being updated correctly to avoid relying on implementation details in your tests. That said, if you'd prefer to update the props of a rendered component in your test, this function can be used to update props of the rendered component. @@ -174,7 +177,9 @@ await rerender() ```ts function unmount(): Promise ``` + + Also records a `react.unmount` trace mark in the [Trace View](/guide/browser/trace-view). This will cause the rendered component to be unmounted. This is useful for testing what happens when your component is removed from the page (like testing that you don't leave event handlers hanging around causing memory leaks). @@ -258,6 +263,7 @@ await renderHook(() => {}, { wrapper: createWrapper(Wrapper, { value: 'foo' }), }) ``` + ::: `renderHook` 返回包含以下工具方法和属性的对象: diff --git a/api/browser/svelte.md b/api/browser/svelte.md index 119146249..9d8ac40e8 100644 --- a/api/browser/svelte.md +++ b/api/browser/svelte.md @@ -41,7 +41,9 @@ export function render( renderOptions?: SetupOptions ): RenderResult & PromiseLike> ``` + + The `render` function records a `svelte.render` trace mark, visible in the [Trace View](/guide/browser/trace-view). ::: warning @@ -51,6 +53,7 @@ Synchronous usage of `render` is deprecated and will be removed in the next majo const screen = render(Component) // [!code --] const screen = await render(Component) // [!code ++] ``` + ::: ### 选项 {#options} @@ -152,7 +155,9 @@ function debug( ```ts function rerender(props: Partial>): Promise ``` + + Updates the component's props and waits for Svelte to apply the changes. Use this to test how your component responds to prop changes. Also records a `svelte.rerender` trace mark in the [Trace View](/guide/browser/trace-view). ```ts @@ -171,7 +176,9 @@ await rerender({ number: 2 }) ```ts function unmount(): Promise ``` + + Unmount and destroy the Svelte component. Also records a `svelte.unmount` trace mark in the [Trace View](/guide/browser/trace-view). This is useful for testing what happens when your component is removed from the page (like testing that you don't leave event handlers hanging around causing memory leaks). ::: warning @@ -219,6 +226,7 @@ await expect.element( 对于简单的代码片段,您可以使用包装组件和 “占位” 子元素进行测试。通过设置 `data-testid` 属性帮助测试插槽内容。 ::: code-group + ```ts [basic.test.js] import { render } from 'vitest-browser-svelte' import { expect, test } from 'vitest' @@ -234,6 +242,7 @@ test('basic snippet', async () => { await expect.element(child).toBeInTheDocument() }) ``` + ```svelte [basic-snippet.svelte]