diff --git a/.vitepress/components.d.ts b/.vitepress/components.d.ts index b509b572..1d997988 100644 --- a/.vitepress/components.d.ts +++ b/.vitepress/components.d.ts @@ -1,10 +1,10 @@ /* eslint-disable */ +/* prettier-ignore */ // @ts-nocheck // Generated by unplugin-vue-components // Read more: https://github.com/vuejs/core/pull/3399 export {} -/* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { Contributors: typeof import('./components/Contributors.vue')['default'] diff --git a/.vitepress/components/FeaturesList.vue b/.vitepress/components/FeaturesList.vue index c6a4359b..fadf20b1 100644 --- a/.vitepress/components/FeaturesList.vue +++ b/.vitepress/components/FeaturesList.vue @@ -86,7 +86,6 @@ >expect-type 进行类型测试 - 支持分片 diff --git a/.vitepress/config.ts b/.vitepress/config.ts index 0fdf8e4e..1cf751b5 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -32,13 +32,13 @@ export default () => { en: { label: 'English', lang: 'en', - link: 'https://vitest.dev/', + link: 'https://v1.vitest.dev/', }, }, head: [ ['meta', { name: 'theme-color', content: '#729b1a' }], - ['link', { rel: 'icon', href: '/favicon.ico', sizes: '48x48' }], - ['link', { rel: 'icon', href: '/logo.svg', sizes: 'any', type: 'image/svg+xml' }], + ['link', { rel: 'icon', href: '/favicon.ico', sizes: 'any' }], + ['link', { rel: 'icon', href: '/logo.svg', type: 'image/svg+xml' }], ['meta', { name: 'author', content: `${teamMembers.map(c => c.name).join(', ')} and ${vitestName} contributors` }], ['meta', { name: 'keywords', content: 'vitest, vite, test, coverage, snapshot, react, vue, preact, svelte, solid, lit, marko, ruby, cypress, puppeteer, jsdom, happy-dom, test-runner, jest, typescript, esm, tinypool, tinyspy, node' }], ['meta', { property: 'og:title', content: vitestName }], @@ -69,7 +69,7 @@ export default () => { logo: '/logo.svg', editLink: { - pattern: 'https://github.com/vitest-dev/docs-cn/tree/dev/:path', + pattern: 'https://github.com/vitest-dev/docs-cn/tree/v1/:path', text: '为此页提供修改建议', }, @@ -123,7 +123,7 @@ export default () => { text: '社区指南', link: contributing, }, - ] + ], }, { items: [ @@ -135,10 +135,6 @@ export default () => { text: 'v0.x', link: 'https://v0.vitest.dev/', }, - { - text: 'v1.x', - link: 'https://v1.vitest.dev/', - }, ], }, ], @@ -173,12 +169,12 @@ export default () => { ], }, ], - '/': [ + '/guide/': [ { text: '指南', items: [ { - text: '简介', + text: '为什么是 Vitest?', link: '/guide/why', }, { @@ -222,45 +218,12 @@ export default () => { link: '/guide/testing-types', }, { - text: 'Vitest UI', + text: 'UI 模式', link: '/guide/ui', }, { text: '浏览器模式', - link: '/guide/browser/', - collapsed: false, - items: [ - { - text: 'Assertion API', - link: '/guide/browser/assertion-api', - docFooterText: 'Assertion API | Browser Mode', - }, - { - text: 'Retry-ability', - link: '/guide/browser/retry-ability', - docFooterText: 'Retry-ability | Browser Mode', - }, - { - text: 'Context', - link: '/guide/browser/context', - docFooterText: 'Context | Browser Mode', - }, - { - text: 'Interactivity API', - link: '/guide/browser/interactivity-api', - docFooterText: 'Interactivity API | Browser Mode', - }, - { - text: 'Commands', - link: '/guide/browser/commands', - docFooterText: 'Commands | Browser Mode', - }, - { - text: 'Examples', - link: '/guide/browser/examples', - docFooterText: 'Examples | Browser Mode', - }, - ], + link: '/guide/browser', }, { text: '源码内联测试', @@ -287,7 +250,7 @@ export default () => { link: '/guide/debugging', }, { - text: '与其他测试框架对比', + text: '测试框架比较', link: '/guide/comparisons', }, { @@ -304,6 +267,8 @@ export default () => { }, ], }, + ], + '/api/': [ { text: 'API', items: [ @@ -337,6 +302,8 @@ export default () => { }, ], }, + ], + '/config/': [ { text: '配置', items: [ diff --git a/advanced/api.md b/advanced/api.md index f743f3e4..12dc4e70 100644 --- a/advanced/api.md +++ b/advanced/api.md @@ -4,7 +4,7 @@ Vitest 暴露了实验性的私有 API。由于可能不遵循语义化版本规范(SemVer),因此可能会出现不兼容的更改,请在使用 Vitest 时锁定版本。 ::: -## 启动 Vitest +## startVitest 你可以使用 Vitest 的 Node API 开始运行 Vitest 测试: @@ -34,7 +34,7 @@ await vitest?.close() 或者,你可以将完整的 Vite 配置作为第四个参数传递进去,这将优先于任何其他用户定义的选项。 -## 创建 Vitest +## createVitest 你可以使用 `createVitest` 函数创建自己的 Vitest 实例. 它返回与 `startVitest` 相同的 `Vitest` 实例, 但不会启动测试,也不会验证已安装的包。 @@ -62,9 +62,8 @@ Vitest 实例需要当前的测试模式。它可以是以下之一: - 运行运行时测试时为 `test` - 运行基准测试时为 `benchmark` -- 运行类型测试时为 `typecheck` -### 模式 +### 模式 {#mode} #### test @@ -74,53 +73,6 @@ Vitest 实例需要当前的测试模式。它可以是以下之一: 基准测试模式会调用 `bench` 函数,并在遇到 `test` 或 `it` 时抛出错误。此模式使用配置中的 `benchmark.include` 和 `benchmark.exclude` 选项查找基准测试文件。 -#### typecheck - -类型检查模式不会*运行*测试。它仅分析类型并提供摘要信息。此模式使用配置中的 `typecheck.include` 和 `typecheck.exclude` 选项查找要分析的文件。 - ### start 你可以使用 `start` 方法运行测试或者基准测试。你还可以传递一个字符串数组以筛选测试文件。 - - -### `provide` - -Vitest 暴露了`provide`方法,它是`vitest.getCoreWorkspaceProject().provide`的简写。使用该方法,您可以从主线程向测试传递值。所有值在存储前都会通过 `structuredClone`进行检查,但值本身不会被克隆。 - -要在测试中接收值,需要从 `vitest` entrypont 导入 `inject` 方法: - -```ts -import { inject } from 'vitest' -const port = inject('wsPort') // 3000 -``` - -为了提高类型安全性,我们鼓励您增强 `ProvidedContext` 的类型: - -```ts -import { createVitest } from 'vitest/node' - -const vitest = await createVitest('test', { - watch: false, -}) -vitest.provide('wsPort', 3000) - -declare module 'vitest' { - export interface ProvidedContext { - wsPort: number - } -} -``` - -::: warning -从技术上讲,`provide`是`WorkspaceProject`的一个方法,因此仅限于特定的项目。不过,所有项目都继承了核心项目的值,这使得 `vitest.provide` 成为向测试传递值的通用方法。 -::: - -::: tip -在不想使用公共 API 的情况下,[全局设置文件](/config/#globalsetup) 也可以使用此方法: - -```js -export default function setup({ provide }) { - provide('wsPort', 3000) -} -``` -::: diff --git a/advanced/runner.md b/advanced/runner.md index 8340ff7f..2f04b456 100644 --- a/advanced/runner.md +++ b/advanced/runner.md @@ -31,7 +31,7 @@ export interface VitestRunner { * 这是在实际运行测试函数之前被调用的。 * 此时已经有了带有 "state" 和 "startTime" 属性的 "result" 对象。 */ - onBeforeTryTask?: (test: TaskPopulated, options: { retry: number, repeats: number }) => unknown + onBeforeTryTask?: (test: TaskPopulated, options: { retry: number; repeats: number }) => unknown /** * 这是在结果和状态都被设置之后被调用的。 */ @@ -40,7 +40,7 @@ export interface VitestRunner { * 这是在运行测试函数后立即被调用的。此时还没有新的状态。 * 如果测试函数抛出异常,将不会调用此方法。 */ - onAfterTryTask?: (test: TaskPopulated, options: { retry: number, repeats: number }) => unknown + onAfterTryTask?: (test: TaskPopulated, options: { retry: number; repeats: number }) => unknown /** * 这是在运行单个测试套件之前被调用的,此时还没有测试结果。 @@ -80,7 +80,7 @@ export interface VitestRunner { /** * 这个方法被用于 "test" 和 "custom" 处理程序。 * 你可以在 "setupFiles" 中使用 "beforeAll" 来定义自定义上下文,而不是使用 runner。 - * 更多信息请参考:https://vitest.dev/advanced/runner.html#your-task-function + * 更多信息请参考:https://v1.vitest.dev/advanced/runner.html#your-task-function */ extendTaskContext?: (context: TaskContext) => TaskContext /** diff --git a/api/assert.md b/api/assert.md index 264a2fe9..48a1131a 100644 --- a/api/assert.md +++ b/api/assert.md @@ -1,12 +1,12 @@ # assert -Vitest reexports the `assert` method from [`chai`](https://www.chaijs.com/api/assert/) for verifying invariants. +Vitest 从 [`chai`](https://www.chaijs.com/api/assert/) 重新导出了 `assert` 方法,用于验证不变量。 ## assert -- **Type:** `(expression: any, message?: string) => asserts expression` +- **类型:** `(expression: any, message?: string) => asserts expression` -Assert that the given `expression` is truthy, otherwise the assertion fails. +断言给定的 `expression` 是 true,否则断言失败。 ```ts import { assert, test } from 'vitest' @@ -18,11 +18,11 @@ test('assert', () => { ## fail -- **Type:** +- **类型:** - `(message?: string) => never` - `(actual: T, expected: T, message?: string, operator?: string) => never` -Force an assertion failure. +强制断言失败。 ```ts import { assert, test } from 'vitest' @@ -35,10 +35,10 @@ test('assert.fail', () => { ## isOk -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => asserts value` - **Alias** `ok` -Assert that the given `value` is truthy. +断言给定的 `value` 是 true 。 ```ts import { assert, test } from 'vitest' @@ -51,10 +51,10 @@ test('assert.isOk', () => { ## isNotOk -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => void` - **Alias** `notOk` -Assert that the given `value` is falsy. +断言给定的 `value` 是 false 。 ```ts import { assert, test } from 'vitest' @@ -67,9 +67,9 @@ test('assert.isNotOk', () => { ## equal -- **Type:** `(actual: T, expected: T, message?: string) => void` +- **类型:** `(actual: T, expected: T, message?: string) => void` -Asserts non-strict equality (==) of `actual` and `expected`. +断言 `actual` 和 `expected` 非严格相等 (==)。 ```ts import { assert, test } from 'vitest' @@ -81,9 +81,9 @@ test('assert.equal', () => { ## notEqual -- **Type:** `(actual: T, expected: T, message?: string) => void` +- **类型:** `(actual: T, expected: T, message?: string) => void` -Asserts non-strict inequality (!=) of `actual` and `expected`. +断言 `actual` 和 `expected` 非严格不等 (!=)。 ```ts import { assert, test } from 'vitest' @@ -95,9 +95,9 @@ test('assert.equal', () => { ## strictEqual -- **Type:** `(actual: T, expected: T, message?: string) => void` +- **类型:** `(actual: T, expected: T, message?: string) => void` -Asserts strict equality (===) of `actual` and `expected`. +断言 `actual` 和 `expected` 严格相等 (===)。 ```ts import { assert, test } from 'vitest' @@ -109,9 +109,9 @@ test('assert.strictEqual', () => { ## deepEqual -- **Type:** `(actual: T, expected: T, message?: string) => void` +- **类型:** `(actual: T, expected: T, message?: string) => void` -Asserts that `actual` is deeply equal to `expected`. +断言 `actual` 深度等于 `expected` 。 ```ts import { assert, test } from 'vitest' @@ -123,9 +123,9 @@ test('assert.deepEqual', () => { ## notDeepEqual -- **Type:** `(actual: T, expected: T, message?: string) => void` +- **类型:** `(actual: T, expected: T, message?: string) => void` -Assert that `actual` is not deeply equal to `expected`. +断言 `actual` 不深度等于 `expected` 。 ```ts import { assert, test } from 'vitest' @@ -137,9 +137,9 @@ test('assert.notDeepEqual', () => { ## isAbove -- **Type:** `(valueToCheck: number, valueToBeAbove: number, message?: string) => void` +- **类型:** `(valueToCheck: number, valueToBeAbove: number, message?: string) => void` -Assert that `valueToCheck` is strictly greater than (>) `valueToBeAbove`. +断言 `valueToCheck` 严格大于 (>) `valueToBeAbove` 。 ```ts import { assert, test } from 'vitest' @@ -151,9 +151,9 @@ test('assert.isAbove', () => { ## isAtLeast -- **Type:** `(valueToCheck: number, valueToBeAtLeast: number, message?: string) => void` +- **类型:** `(valueToCheck: number, valueToBeAtLeast: number, message?: string) => void` -Assert that `valueToCheck` is greater than or equal to (>=) `valueToBeAtLeast`. +断言 `valueToCheck` 大于等于 (>=) `valueToBeAtLeast` 。 ```ts import { assert, test } from 'vitest' @@ -166,9 +166,9 @@ test('assert.isAtLeast', () => { ## isBelow -- **Type:** `(valueToCheck: number, valueToBeBelow: number, message?: string) => void` +- **类型:** `(valueToCheck: number, valueToBeBelow: number, message?: string) => void` -Asserts `valueToCheck` is strictly less than (<) `valueToBeBelow`. +断言 `valueToCheck` 严格小于 (<) `valueToBeBelow` 。 ```ts import { assert, test } from 'vitest' @@ -180,9 +180,9 @@ test('assert.isBelow', () => { ## isAtMost -- **Type:** `(valueToCheck: number, valueToBeAtMost: number, message?: string) => void` +- **类型:** `(valueToCheck: number, valueToBeAtMost: number, message?: string) => void` -Asserts `valueToCheck` is less than or equal to (<=) `valueToBeAtMost`. +断言 `valueToCheck` 小于等于 (<=) `valueToBeAtMost` 。 ```ts import { assert, test } from 'vitest' @@ -195,9 +195,9 @@ test('assert.isAtMost', () => { ## isTrue -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => asserts value is true` -Asserts that `value` is true. +断言 `value` 是 true 。 ```ts import { assert, test } from 'vitest' @@ -211,9 +211,9 @@ test('assert.isTrue', () => { ## isNotTrue -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => asserts value is Exclude` -Asserts that `value` is not true. +断言 `value` 不是 true 。 ```ts import { assert, test } from 'vitest' @@ -227,9 +227,9 @@ test('assert.isNotTrue', () => { ## isFalse -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => asserts value is false` -Asserts that `value` is false. +断言 `value` 是 false 。 ```ts import { assert, test } from 'vitest' @@ -243,9 +243,9 @@ test('assert.isFalse', () => { ## isNotFalse -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => asserts value is Exclude` -Asserts that `value` is not false. +断言 `value` 不是 false 。 ```ts import { assert, test } from 'vitest' @@ -259,9 +259,9 @@ test('assert.isNotFalse', () => { ## isNull -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => asserts value is null` -Asserts that `value` is null. +断言 `value` 是 null 。 ```ts import { assert, test } from 'vitest' @@ -275,14 +275,14 @@ test('assert.isNull', () => { ## isNotNull -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => asserts value is Exclude` -Asserts that `value` is not null. +断言 `value` 不是 null 。 ```ts import { assert, test } from 'vitest' -const error = { message: 'error was occured' } +const error = { message: 'error was occurred' } test('assert.isNotNull', () => { assert.isNotNull(error, 'error is not null but object') @@ -291,14 +291,14 @@ test('assert.isNotNull', () => { ## isNaN -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => void` -Asserts that `value` is NaN. +断言 `value` 是 NaN 。 ```ts import { assert, test } from 'vitest' -const calculation = 1 * 'viitest' +const calculation = 1 * 'vitest' test('assert.isNaN', () => { assert.isNaN(calculation, '1 * "vitest" is NaN') @@ -307,9 +307,9 @@ test('assert.isNaN', () => { ## isNotNaN -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => void` -Asserts that `value` is not NaN. +断言 `value` 不是 NaN 。 ```ts import { assert, test } from 'vitest' @@ -323,9 +323,9 @@ test('assert.isNotNaN', () => { ## exists -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => void` -Asserts that `value` is neither null nor undefined. +断言 `value` 既不是 null 也不是 undefined 。 ```ts import { assert, test } from 'vitest' @@ -339,9 +339,9 @@ test('assert.exists', () => { ## notExists -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => void` -Asserts that `value` is either null nor undefined. +断言 `value` 是 null 或 undefined 。 ```ts import { assert, test } from 'vitest' @@ -357,9 +357,9 @@ test('assert.notExists', () => { ## isUndefined -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => void` -Asserts that `value` is undefined. +断言 `value` 是 undefined 。 ```ts import { assert, test } from 'vitest' @@ -373,9 +373,9 @@ test('assert.isUndefined', () => { ## isDefined -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => void` -Asserts that `value` is not undefined. +断言 `value` 不是 undefined 。 ```ts import { assert, test } from 'vitest' @@ -389,9 +389,9 @@ test('assert.isDefined', () => { ## isFunction -- **Type:** `(value: T, message?: string) => void` -- **Alias:** `isCallable` - Asserts that `value` is a function. +- **类型:** `(value: T, message?: string) => void` +- **别名:** `isCallable` + 断言 `value` 是一个函数。 ```ts import { assert, test } from 'vitest' @@ -407,10 +407,10 @@ test('assert.isFunction', () => { ## isNotFunction -- **Type:** `(value: T, message?: string) => void` -- **Alias:** `isNotCallable` +- **类型:** `(value: T, message?: string) => void` +- **别名:** `isNotCallable` -Asserts that `value` is not a function. +断言 `value` 不是一个函数。 ```ts import { assert, test } from 'vitest' @@ -424,9 +424,9 @@ test('assert.isNotFunction', () => { ## isObject -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => void` -Asserts that `value` is an object of type Object (as revealed by Object.prototype.toString). The assertion does not match subclassed objects. +断言 `value` 是一个类型为 Object 的对象 (由 Object.prototype.toString 确定)。 此断言不匹配子类对象。 ```ts import { assert, test } from 'vitest' @@ -440,9 +440,9 @@ test('assert.isObject', () => { ## isNotObject -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => void` -Asserts that `value` is not an object of type Object (as revealed by Object.prototype.toString). The assertion does not match subclassed objects. +断言 `value` 不是一个类型为 Object 的对象 (如 Object.prototype.toString 确定)。 该断言不匹配子类对象。 ```ts import { assert, test } from 'vitest' @@ -456,9 +456,9 @@ test('assert.isNotObject', () => { ## isArray -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => void` -Asserts that `value` is an array. +断言 `value` 是一个数组。 ```ts import { assert, test } from 'vitest' @@ -472,9 +472,9 @@ test('assert.isArray', () => { ## isNotArray -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => void` -Asserts that `value` is not an array. +断言 `value` 不是一个数组。 ```ts import { assert, test } from 'vitest' @@ -488,9 +488,9 @@ test('assert.isNotArray', () => { ## isString -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => void` -Asserts that `value` is a string. +断言 `value` 是一个字符串。 ```ts import { assert, test } from 'vitest' @@ -504,9 +504,9 @@ test('assert.isString', () => { ## isNotString -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => void` -Asserts that `value` is not a string. +断言 `value` 不是一个字符串。 ```ts import { assert, test } from 'vitest' @@ -520,9 +520,9 @@ test('assert.isNotString', () => { ## isNumber -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => void` -Asserts that `value` is a number. +断言 `value` 是一个数字。 ```ts import { assert, test } from 'vitest' @@ -536,9 +536,9 @@ test('assert.isNumber', () => { ## isNotNumber -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => void` -Asserts that `value` is not a number. +断言 `value` 不是一个数字。 ```ts import { assert, test } from 'vitest' @@ -552,9 +552,9 @@ test('assert.isNotNumber', () => { ## isFinite -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => void` -Asserts that `value` is a finite number (not NaN, Infinity). +断言 `value` 是一个有限数字。(不是 NaN, Infinity)。 ```ts import { assert, test } from 'vitest' @@ -568,9 +568,9 @@ test('assert.isFinite', () => { ## isBoolean -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => void` -Asserts that `value` is a boolean. +断言 `value` 是一个布尔值。 ```ts import { assert, test } from 'vitest' @@ -584,9 +584,9 @@ test('assert.isBoolean', () => { ## isNotBoolean -- **Type:** `(value: T, message?: string) => void` +- **类型:** `(value: T, message?: string) => void` -Asserts that `value` is not a boolean. +断言 `value` 不是一个布尔值。 ```ts import { assert, test } from 'vitest' @@ -600,9 +600,9 @@ test('assert.isBoolean', () => { ## typeOf -- **Type:** `(value: T, name: string, message?: string) => void` +- **类型:** `(value: T, name: string, message?: string) => void` -Asserts that `value`’s type is `name`, as determined by Object.prototype.toString. +断言 `value` 的类型是 `name`, 由 Object.prototype.toString 确定。 ```ts import { assert, test } from 'vitest' @@ -619,9 +619,9 @@ test('assert.typeOf', () => { ## notTypeOf -- **Type:** `(value: T, name: string, message?: string) => void` +- **类型:** `(value: T, name: string, message?: string) => void` -Asserts that `value`’s type is not `name`, as determined by Object.prototype.toString. +断言 `value` 的类型不是 `name`,由 Object.prototype.toString 确定。 ```ts import { assert, test } from 'vitest' @@ -633,16 +633,14 @@ test('assert.notTypeOf', () => { ## instanceOf -- **Type:** `(value: T, constructor: Function, message?: string) => void` +- **类型:** `(value: T, constructor: Function, message?: string) => asserts value is T` -Asserts that `value` is an instance of `constructor`. +断言 `value` 是 `constructor` 的实例。 ```ts import { assert, test } from 'vitest' -function Person(name) { - this.name = name -} +function Person(name) { this.name = name } const foo = new Person('foo') class Tea { @@ -660,16 +658,14 @@ test('assert.instanceOf', () => { ## notInstanceOf -- **Type:** `(value: T, constructor: Function, message?: string) => void` +- **类型:** `(value: T, constructor: Function, message?: string) => asserts value is Exclude` -Asserts that `value` is not an instance of `constructor`. +断言 `value` 不是 `constructor` 的实例。 ```ts import { assert, test } from 'vitest' -function Person(name) { - this.name = name -} +function Person(name) { this.name = name } const foo = new Person('foo') class Tea { @@ -686,13 +682,13 @@ test('assert.instanceOf', () => { ## include -- **Type:** +- **类型:** - `(haystack: string, needle: string, message?: string) => void` - `(haystack: readonly T[] | ReadonlySet | ReadonlyMap, needle: T, message?: string) => void` - `(haystack: WeakSet, needle: T, message?: string) => void` - `(haystack: T, needle: Partial, message?: string) => void` -Asserts that `haystack` includes `needle`. Can be used to assert the inclusion of a value in an array, a substring in a string, or a subset of properties in an object. +断言 `haystack` 包含 `needle` 。可以用来断言数组中是否包含一个值、字符串中是否包含一个子字符串、或者对象中是否包含一组属性。 ```ts import { assert, test } from 'vitest' @@ -700,23 +696,19 @@ import { assert, test } from 'vitest' test('assert.include', () => { assert.include([1, 2, 3], 2, 'array contains value') assert.include('foobar', 'foo', 'string contains substring') - assert.include( - { foo: 'bar', hello: 'universe' }, - { foo: 'bar' }, - 'object contains property' - ) + assert.include({ foo: 'bar', hello: 'universe' }, { foo: 'bar' }, 'object contains property') }) ``` ## notInclude -- **Type:** +- **类型:** - `(haystack: string, needle: string, message?: string) => void` - `(haystack: readonly T[] | ReadonlySet | ReadonlyMap, needle: T, message?: string) => void` - `(haystack: WeakSet, needle: T, message?: string) => void` - `(haystack: T, needle: Partial, message?: string) => void` -Asserts that `haystack` does not include `needle`. It can be used to assert the absence of a value in an array, a substring in a string, or a subset of properties in an object. +断言 `haystack` 不包含 `needle` 。可以用来断言数组中是否不包含一个值、字符串中是否不包含一个子字符串、或者对象中是否不包含一组属性。 ```ts import { assert, test } from 'vitest' @@ -724,22 +716,18 @@ import { assert, test } from 'vitest' test('assert.notInclude', () => { assert.notInclude([1, 2, 3], 4, 'array doesn\'t contain 4') assert.notInclude('foobar', 'baz', 'foobar doesn\'t contain baz') - assert.notInclude( - { foo: 'bar', hello: 'universe' }, - { foo: 'baz' }, - 'object doesn\'t contain property' - ) + assert.notInclude({ foo: 'bar', hello: 'universe' }, { foo: 'baz' }, 'object doesn\'t contain property') }) ``` ## deepInclude -- **Type:** +- **类型:** - `(haystack: string, needle: string, message?: string) => void` - `(haystack: readonly T[] | ReadonlySet | ReadonlyMap, needle: T, message?: string) => void` - `(haystack: T, needle: T extends WeakSet ? never : Partial, message?: string) => void` -Asserts that `haystack` includes `needle`. Can be used to assert the inclusion of a value in an array or a subset of properties in an object. Deep equality is used. +断言 `haystack` 包含 `needle` 。可以用来断言数组中是否包含一个值或对象中是否包含一组属性。使用深度相等。 ```ts import { assert, test } from 'vitest' @@ -755,12 +743,12 @@ test('assert.deepInclude', () => { ## notDeepInclude -- **Type:** +- **类型:** - `(haystack: string, needle: string, message?: string) => void` - `(haystack: readonly T[] | ReadonlySet | ReadonlyMap, needle: T, message?: string) => void` - `(haystack: T, needle: T extends WeakSet ? never : Partial, message?: string) => void` -Asserts that `haystack` does not include `needle`. It can be used to assert the absence of a value in an array or a subset of properties in an object. Deep equality is used. +断言 `haystack` 不包含 `needle` 。可以用来断言数组中是否不包含一个值或对象中是否不包含一组属性。使用深度相等。 ```ts import { assert, test } from 'vitest' @@ -776,9 +764,9 @@ test('assert.notDeepInclude', () => { ## nestedInclude -- **Type:** `(haystack: any, needle: any, message?: string) => void` +- **类型:** `(haystack: any, needle: any, message?: string) => void` -Asserts that `haystack` includes `needle`. Can be used to assert the inclusion of a subset of properties in an object. Enables the use of dot- and bracket-notation for referencing nested properties. ‘[]’ and ‘.’ in property names can be escaped using double backslashes. +断言 `haystack` 包含 `needle` 。 可以用来断言对象中是否包含一组属性。允许使用点和括号表示法来引用嵌套属性。属性名中的 ‘[]’ 和 ‘.’ 可以使用双反斜杠转义。 ```ts import { assert, test } from 'vitest' @@ -791,9 +779,9 @@ test('assert.nestedInclude', () => { ## notNestedInclude -- **Type:** `(haystack: any, needle: any, message?: string) => void` +- **类型:** `(haystack: any, needle: any, message?: string) => void` -Asserts that `haystack` does not include `needle`. Can be used to assert the inclusion of a subset of properties in an object. Enables the use of dot- and bracket-notation for referencing nested properties. ‘[]’ and ‘.’ in property names can be escaped using double backslashes. +断言 `haystack` 不包含 `needle` 。可以用来断言对象中是否不包含一组属性。允许使用点和括号表示法来引用嵌套属性。属性名中的 ‘[]’ 和 ‘.’ 可以使用双反斜杠转义。 ```ts import { assert, test } from 'vitest' @@ -806,45 +794,39 @@ test('assert.nestedInclude', () => { ## deepNestedInclude -- **Type:** `(haystack: any, needle: any, message?: string) => void` +- **类型:** `(haystack: any, needle: any, message?: string) => void` -Asserts that `haystack` includes `needle`. Can be used to assert the inclusion of a subset of properties in an object while checking for deep equality. Enables the use of dot- and bracket-notation for referencing nested properties. ‘[]’ and ‘.’ in property names can be escaped using double backslashes. +断言 `haystack` 包含 `needle` 。可以用来断言对象中是否包含一组属性,同时检查深度相等性。允许使用点和括号表示法来引用嵌套属性。属性名中的 ‘[]’ 和 ‘.’ 可以使用双反斜杠转义。 ```ts import { assert, test } from 'vitest' test('assert.deepNestedInclude', () => { assert.deepNestedInclude({ a: { b: [{ x: 1 }] } }, { 'a.b[0]': { x: 1 } }) - assert.deepNestedInclude( - { '.a': { '[b]': { x: 1 } } }, - { '\\.a.\\[b\\]': { x: 1 } } - ) + assert.deepNestedInclude({ '.a': { '[b]': { x: 1 } } }, { '\\.a.\\[b\\]': { x: 1 } }) }) ``` ## notDeepNestedInclude -- **Type:** `(haystack: any, needle: any, message?: string) => void` +- **类型:** `(haystack: any, needle: any, message?: string) => void` -Asserts that `haystack` not includes `needle`. Can be used to assert the absence of a subset of properties in an object while checking for deep equality. Enables the use of dot- and bracket-notation for referencing nested properties. ‘[]’ and ‘.’ in property names can be escaped using double backslashes. +断言 `haystack` 不包含 `needle` 。可以用来断言对象中是否不包含一组属性,同时检查深度相等性。允许使用点和括号表示法来引用嵌套属性。属性名中的 ‘[]’ 和 ‘.’ 可以使用双反斜杠转义。 ```ts import { assert, test } from 'vitest' test('assert.notDeepNestedInclude', () => { assert.notDeepNestedInclude({ a: { b: [{ x: 1 }] } }, { 'a.b[0]': { y: 1 } }) - assert.notDeepNestedInclude( - { '.a': { '[b]': { x: 1 } } }, - { '\\.a.\\[b\\]': { y: 2 } } - ) + assert.notDeepNestedInclude({ '.a': { '[b]': { x: 1 } } }, { '\\.a.\\[b\\]': { y: 2 } }) }) ``` ## ownInclude -- **Type:** `(haystack: any, needle: any, message?: string) => void` +- **类型:** `(haystack: any, needle: any, message?: string) => void` -Asserts that `haystack` includes `needle`. Can be used to assert the inclusion of a subset of properties in an object while ignoring inherited properties. +断言 `haystack` 包含 `needle` 。可以用来断言对象中是否包含一组属性,同时忽略继承的属性。 ```ts import { assert, test } from 'vitest' @@ -856,9 +838,9 @@ test('assert.ownInclude', () => { ## notOwnInclude -- **Type:** `(haystack: any, needle: any, message?: string) => void` +- **类型:** `(haystack: any, needle: any, message?: string) => void` -Asserts that `haystack` includes `needle`. Can be used to assert the absence of a subset of properties in an object while ignoring inherited properties. +断言 `haystack` 包含 `needle` 。可以用来断言对象中是否不包含一组属性,同时忽略继承的属性 ```ts import { assert, test } from 'vitest' @@ -877,9 +859,9 @@ test('assert.notOwnInclude', () => { ## deepOwnInclude -- **Type:** `(haystack: any, needle: any, message?: string) => void` +- **类型:** `(haystack: any, needle: any, message?: string) => void` -Asserts that `haystack` includes `needle`. Can be used to assert the inclusion of a subset of properties in an object while ignoring inherited properties and checking for deep equality. +断言 `haystack` 包含 `needle` 。可以用来断言对象中是否包含一组属性,同时忽略继承的属性并检查深度相等性。 ```ts import { assert, test } from 'vitest' @@ -891,9 +873,9 @@ test('assert.deepOwnInclude', () => { ## notDeepOwnInclude -- **Type:** `(haystack: any, needle: any, message?: string) => void` +- **类型:** `(haystack: any, needle: any, message?: string) => void` -Asserts that `haystack` not includes `needle`. Can be used to assert the absence of a subset of properties in an object while ignoring inherited properties and checking for deep equality. +断言 `haystack` 不包含 `needle` 。可以用来断言对象中是否不包含一组属性,同时忽略继承的属性并检查深度相等性。 ```ts import { assert, test } from 'vitest' @@ -905,9 +887,9 @@ test('assert.notDeepOwnInclude', () => { ## match -- **Type:** `(value: string, regexp: RegExp, message?: string) => void` +- **类型:** `(value: string, regexp: RegExp, message?: string) => void` -Asserts that `value` matches the regular expression `regexp`. +断言 `value` 匹配正则表达式 `regexp` 。 ```ts import { assert, test } from 'vitest' @@ -919,9 +901,9 @@ test('assert.match', () => { ## notMatch -- **Type:** `(value: string, regexp: RegExp, message?: string) => void` +- **类型:** `(value: string, regexp: RegExp, message?: string) => void` -Asserts that `value` does not matches the regular expression `regexp`. +断言 `value` 不匹配正则表达式 `regexp` 。 ```ts import { assert, test } from 'vitest' @@ -933,9 +915,9 @@ test('assert.notMatch', () => { ## property -- **Type:** `(object: T, property: string, message?: string) => void` +- **类型:** `(object: T, property: string, message?: string) => void` -Asserts that `object` has a direct or inherited property named by `property` +断言 `object` 具有由 `property` 指定的直接或继承属性。 ```ts import { assert, test } from 'vitest' @@ -948,9 +930,9 @@ test('assert.property', () => { ## notProperty -- **Type:** `(object: T, property: string, message?: string) => void` +- **类型:** `(object: T, property: string, message?: string) => void` -Asserts that `object` does not have a direct or inherited property named by `property` +断言 `object` 没有由 `property` 指定的直接或继承属性。 ```ts import { assert, test } from 'vitest' @@ -962,9 +944,9 @@ test('assert.notProperty', () => { ## propertyVal -- **Type:** `(object: T, property: string, value: V, message?: string) => void` +- **类型:** `(object: T, property: string, value: V, message?: string) => void` -Asserts that `object` has a direct or inherited property named by `property` with a value given by `value`. Uses a strict equality check (===). +断言 `object` 具有由 `property` 指定的直接或继承属性,其值为 `value` 。使用严格相等检查(===)。 ```ts import { assert, test } from 'vitest' @@ -976,9 +958,9 @@ test('assert.notPropertyVal', () => { ## notPropertyVal -- **Type:** `(object: T, property: string, value: V, message?: string) => void` +- **类型:** `(object: T, property: string, value: V, message?: string) => void` -Asserts that `object` does not have a direct or inherited property named by `property` with a value given by `value`. Uses a strict equality check (===). +断言 `object` 没有由 `property` 指定的直接或继承属性,其值为 `value` 。使用严格相等检查(===)。 ```ts import { assert, test } from 'vitest' @@ -991,9 +973,9 @@ test('assert.notPropertyVal', () => { ## deepPropertyVal -- **Type:** `(object: T, property: string, value: V, message?: string) => void` +- **类型:** `(object: T, property: string, value: V, message?: string) => void` -Asserts that `object` has a direct or inherited property named by `property` with a value given by `value`. Uses a deep equality check. +断言 `object` 具有由 `property` 指定的直接或继承属性,其值为 `value` 。使用深度相等检查。 ```ts import { assert, test } from 'vitest' @@ -1007,31 +989,25 @@ test('assert.deepPropertyVal', () => { ## notDeepPropertyVal -- **Type:** `(object: T, property: string, value: V, message?: string) => void` +- **类型:** `(object: T, property: string, value: V, message?: string) => void` -Asserts that `object` does not have a direct or inherited property named by `property` with a value given by `value`. Uses a deep equality check. +断言 `object` 没有由 `property` 指定的直接或继承属性,其值为 `value` 。使用深度相等检查。 ```ts import { assert, test } from 'vitest' test('assert.deepPropertyVal', () => { - assert.notDeepPropertyVal({ tea: { green: 'matcha' } }, 'tea', { - black: 'matcha', - }) - assert.notDeepPropertyVal({ tea: { green: 'matcha' } }, 'tea', { - green: 'oolong', - }) - assert.notDeepPropertyVal({ tea: { green: 'matcha' } }, 'coffee', { - green: 'matcha', - }) + assert.notDeepPropertyVal({ tea: { green: 'matcha' } }, 'tea', { black: 'matcha', }) + assert.notDeepPropertyVal({ tea: { green: 'matcha' } }, 'tea', { green: 'oolong', }) + assert.notDeepPropertyVal({ tea: { green: 'matcha' } }, 'coffee', { green: 'matcha', }) }) ``` ## nestedProperty -- **Type:** `(object: T, property: string, message?: string) => void` +- **类型:** `(object: T, property: string, message?: string) => void` -Asserts that `object` has a direct or inherited property named by `property`, which can be a string using dot- and bracket-notation for nested reference. +断言 `object` 具有由 `property` 指定的直接或继承属性,它可以是一个字符串,使用点和括号表示法来引用嵌套的引用。 ```ts import { assert, test } from 'vitest' @@ -1043,9 +1019,9 @@ test('assert.deepPropertyVal', () => { ## notNestedProperty -- **Type:** `(object: T, property: string, message?: string) => void` +- **类型:** `(object: T, property: string, message?: string) => void` -Asserts that `object` does not have a direct or inherited property named by `property`, which can be a string using dot- and bracket-notation for nested reference. +断言 `object` 没有由 `property` 指定的属性,它可以是一个字符串,使用点和括号表示法来引用嵌套的引用。该属性不能存在于对象上,也不能存在于其原型链中的任何地方。 ```ts import { assert, test } from 'vitest' @@ -1057,9 +1033,9 @@ test('assert.deepPropertyVal', () => { ## nestedPropertyVal -- **Type:** `(object: T, property: string, value: any, message?: string) => void` +- **类型:** `(object: T, property: string, value: any, message?: string) => void` -Asserts that `object` has a property named by `property` with value given by `value`. `property` can use dot- and bracket-notation for nested reference. Uses a strict equality check (===). +断言 `object` 具有由 `property` 指定的属性,其值为 `value` 给出。 `property` 可以使用点和方括号表示法进行嵌套引用。使用严格相等检查 (===)。 ```ts import { assert, test } from 'vitest' @@ -1071,83 +1047,55 @@ test('assert.nestedPropertyVal', () => { ## notNestedPropertyVal -- **Type:** `(object: T, property: string, value: any, message?: string) => void` +- **类型:** `(object: T, property: string, value: any, message?: string) => void` -Asserts that `object` does not have a property named by `property` with value given by `value`. `property` can use dot- and bracket-notation for nested reference. Uses a strict equality check (===). +断言 `object` 没有由 `property` 指定的属性,其值为 `value` 给出。 `property` 可以使用点和方括号表示法进行嵌套引用。使用严格相等检查 (===)。 ```ts import { assert, test } from 'vitest' test('assert.notNestedPropertyVal', () => { - assert.notNestedPropertyVal( - { tea: { green: 'matcha' } }, - 'tea.green', - 'konacha' - ) - assert.notNestedPropertyVal( - { tea: { green: 'matcha' } }, - 'coffee.green', - 'matcha' - ) + assert.notNestedPropertyVal({ tea: { green: 'matcha' } }, 'tea.green', 'konacha') + assert.notNestedPropertyVal({ tea: { green: 'matcha' } }, 'coffee.green', 'matcha') }) ``` ## deepNestedPropertyVal -- **Type:** `(object: T, property: string, value: any, message?: string) => void` +- **类型:** `(object: T, property: string, value: any, message?: string) => void` -Asserts that `object` has a property named by `property` with a value given by `value`. `property` can use dot- and bracket-notation for nested reference. Uses a deep equality check (===). +断言 `object` 具有由 `property` 指定的属性,其值为 `value` 给出。 `property` 可以使用点和方括号表示法进行嵌套引用。使用深度相等检查。 ```ts import { assert, test } from 'vitest' test('assert.notNestedPropertyVal', () => { - assert.notNestedPropertyVal( - { tea: { green: 'matcha' } }, - 'tea.green', - 'konacha' - ) - assert.notNestedPropertyVal( - { tea: { green: 'matcha' } }, - 'coffee.green', - 'matcha' - ) + assert.notNestedPropertyVal({ tea: { green: 'matcha' } }, 'tea.green', 'konacha') + assert.notNestedPropertyVal({ tea: { green: 'matcha' } }, 'coffee.green', 'matcha') }) ``` ## notDeepNestedPropertyVal -- **Type:** `(object: T, property: string, value: any, message?: string) => void` +- **类型:** `(object: T, property: string, value: any, message?: string) => void` -Asserts that `object` does not have a property named by `property` with value given by `value`. `property` can use dot- and bracket-notation for nested reference. Uses a deep equality check. +断言 `object` 没有由 `property` 指定的属性,其值为 `value` 给出。 `property` 可以使用点和方括号表示法进行嵌套引用。使用深度相等检查。 ```ts import { assert, test } from 'vitest' test('assert.notDeepNestedPropertyVal', () => { - assert.notDeepNestedPropertyVal( - { tea: { green: { matcha: 'yum' } } }, - 'tea.green', - { oolong: 'yum' } - ) - assert.notDeepNestedPropertyVal( - { tea: { green: { matcha: 'yum' } } }, - 'tea.green', - { matcha: 'yuck' } - ) - assert.notDeepNestedPropertyVal( - { tea: { green: { matcha: 'yum' } } }, - 'tea.black', - { matcha: 'yum' } - ) + assert.notDeepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.green', { oolong: 'yum' }) + assert.notDeepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.green', { matcha: 'yuck' }) + assert.notDeepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.black', { matcha: 'yum' }) }) ``` ## lengthOf -- **Type:** `(object: T, length: number, message?: string) => void` +- **类型:** `(object: T, length: number, message?: string) => void` -Asserts that `object` has a `length` or `size` with the expected value. +断言 `object` 具有预期的 `length` 或 `size` 值。 ```ts import { assert, test } from 'vitest' @@ -1156,80 +1104,49 @@ test('assert.lengthOf', () => { assert.lengthOf([1, 2, 3], 3, 'array has length of 3') assert.lengthOf('foobar', 6, 'string has length of 6') assert.lengthOf(new Set([1, 2, 3]), 3, 'set has size of 3') - assert.lengthOf( - new Map([ - ['a', 1], - ['b', 2], - ['c', 3], - ]), - 3, - 'map has size of 3' - ) + assert.lengthOf(new Map([['a', 1], ['b', 2], ['c', 3]]), 3, 'map has size of 3') }) ``` ## hasAnyKeys -- **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` +- **类型:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` -Asserts that `object` has at least one of the `keys` provided. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. +断言 `object` 至少拥有一个提供的 `keys` 。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 ```ts import { assert, test } from 'vitest' test('assert.hasAnyKeys', () => { assert.hasAnyKeys({ foo: 1, bar: 2, baz: 3 }, ['foo', 'iDontExist', 'baz']) - assert.hasAnyKeys( - { foo: 1, bar: 2, baz: 3 }, - { foo: 30, iDontExist: 99, baz: 1337 } - ) - assert.hasAnyKeys( - new Map([ - [{ foo: 1 }, 'bar'], - ['key', 'value'], - ]), - [{ foo: 1 }, 'key'] - ) - assert.hasAnyKeys(new Set([{ foo: 'bar' }, 'anotherKey']), [ - { foo: 'bar' }, - 'anotherKey', - ]) + assert.hasAnyKeys({ foo: 1, bar: 2, baz: 3 }, { foo: 30, iDontExist: 99, baz: 1337 }) + assert.hasAnyKeys(new Map([[{ foo: 1 }, 'bar'], ['key', 'value'],]), [{ foo: 1 }, 'key']) + assert.hasAnyKeys(new Set([{ foo: 'bar' }, 'anotherKey']), [{ foo: 'bar' }, 'anotherKey']) }) ``` ## hasAllKeys -- **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` +- **类型:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` -Asserts that `object` has all and only all of the `keys` provided. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. +断言 `object` 拥有且仅拥有所有提供的 `keys` 。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 ```ts import { assert, test } from 'vitest' test('assert.hasAllKeys', () => { assert.hasAllKeys({ foo: 1, bar: 2, baz: 3 }, ['foo', 'bar', 'baz']) - assert.hasAllKeys( - { foo: 1, bar: 2, baz: 3 }, - { foo: 30, bar: 99, baz: 1337 } - ) - assert.hasAllKeys( - new Map([ - [{ foo: 1 }, 'bar'], - ['key', 'value'], - ]), - [{ foo: 1 }, 'key'] - ) - assert.hasAllKeys( - new Set([{ foo: 'bar' }, 'anotherKey'], [{ foo: 'bar' }, 'anotherKey']) - ) + assert.hasAllKeys({ foo: 1, bar: 2, baz: 3 }, { foo: 30, bar: 99, baz: 1337 }) + assert.hasAllKeys(new Map([[{ foo: 1 }, 'bar'], ['key', 'value'],]), [{ foo: 1 }, 'key']) + assert.hasAllKeys(new Set([{ foo: 'bar' }, 'anotherKey'], [{ foo: 'bar' }, 'anotherKey'])) }) ``` ## containsAllKeys -- **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` +- **类型:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` -Asserts that `object` has all of the `keys` provided but may have more keys not listed. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. +断言 `object` 拥有所有提供的 `keys`,但可能还有更多未列出的键。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 ```ts import { assert, test } from 'vitest' @@ -1238,283 +1155,145 @@ test('assert.containsAllKeys', () => { assert.containsAllKeys({ foo: 1, bar: 2, baz: 3 }, ['foo', 'baz']) assert.containsAllKeys({ foo: 1, bar: 2, baz: 3 }, ['foo', 'bar', 'baz']) assert.containsAllKeys({ foo: 1, bar: 2, baz: 3 }, { foo: 30, baz: 1337 }) - assert.containsAllKeys( - { foo: 1, bar: 2, baz: 3 }, - { foo: 30, bar: 99, baz: 1337 } - ) - assert.containsAllKeys( - new Map([ - [{ foo: 1 }, 'bar'], - ['key', 'value'], - ]), - [{ foo: 1 }] - ) - assert.containsAllKeys( - new Map([ - [{ foo: 1 }, 'bar'], - ['key', 'value'], - ]), - [{ foo: 1 }, 'key'] - ) - assert.containsAllKeys( - new Set([{ foo: 'bar' }, 'anotherKey'], [{ foo: 'bar' }]) - ) - assert.containsAllKeys( - new Set([{ foo: 'bar' }, 'anotherKey'], [{ foo: 'bar' }, 'anotherKey']) - ) + assert.containsAllKeys({ foo: 1, bar: 2, baz: 3 }, { foo: 30, bar: 99, baz: 1337 }) + assert.containsAllKeys(new Map([[{ foo: 1 }, 'bar'], ['key', 'value'],]), [{ foo: 1 }]) + assert.containsAllKeys(new Map([[{ foo: 1 }, 'bar'], ['key', 'value'],]), [{ foo: 1 }, 'key']) + assert.containsAllKeys(new Set([{ foo: 'bar' }, 'anotherKey'], [{ foo: 'bar' }])) + assert.containsAllKeys(new Set([{ foo: 'bar' }, 'anotherKey'], [{ foo: 'bar' }, 'anotherKey'])) }) ``` ## doesNotHaveAnyKeys -- **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` +- **类型:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` -Asserts that `object` has none of the `keys` provided. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. +断言 `object` 不拥有任何提供的 `keys` 。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 ```ts import { assert, test } from 'vitest' test('assert.doesNotHaveAnyKeys', () => { - assert.doesNotHaveAnyKeys({ foo: 1, bar: 2, baz: 3 }, [ - 'one', - 'two', - 'example', - ]) - assert.doesNotHaveAnyKeys( - { foo: 1, bar: 2, baz: 3 }, - { one: 1, two: 2, example: 'foo' } - ) - assert.doesNotHaveAnyKeys( - new Map([ - [{ foo: 1 }, 'bar'], - ['key', 'value'], - ]), - [{ one: 'two' }, 'example'] - ) - assert.doesNotHaveAnyKeys( - new Set([{ foo: 'bar' }, 'anotherKey'], [{ one: 'two' }, 'example']) - ) + assert.doesNotHaveAnyKeys({ foo: 1, bar: 2, baz: 3 }, ['one', 'two', 'example',]) + assert.doesNotHaveAnyKeys({ foo: 1, bar: 2, baz: 3 }, { one: 1, two: 2, example: 'foo' }) + assert.doesNotHaveAnyKeys(new Map([[{ foo: 1 }, 'bar'], ['key', 'value'],]), [{ one: 'two' }, 'example']) + assert.doesNotHaveAnyKeys(new Set([{ foo: 'bar' }, 'anotherKey'], [{ one: 'two' }, 'example'])) }) ``` ## doesNotHaveAllKeys -- **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` +- **类型:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` -Asserts that `object` does not have at least one of the `keys` provided. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. +断言 `object` 至少不拥有一个提供的 `keys` 。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 ```ts import { assert, test } from 'vitest' test('assert.hasAnyKeys', () => { - assert.doesNotHaveAnyKeys({ foo: 1, bar: 2, baz: 3 }, [ - 'one', - 'two', - 'example', - ]) - assert.doesNotHaveAnyKeys( - { foo: 1, bar: 2, baz: 3 }, - { one: 1, two: 2, example: 'foo' } - ) - assert.doesNotHaveAnyKeys( - new Map([ - [{ foo: 1 }, 'bar'], - ['key', 'value'], - ]), - [{ one: 'two' }, 'example'] - ) - assert.doesNotHaveAnyKeys(new Set([{ foo: 'bar' }, 'anotherKey']), [ - { one: 'two' }, - 'example', - ]) + assert.doesNotHaveAnyKeys({ foo: 1, bar: 2, baz: 3 }, ['one', 'two', 'example',]) + assert.doesNotHaveAnyKeys({ foo: 1, bar: 2, baz: 3 }, { one: 1, two: 2, example: 'foo' }) + assert.doesNotHaveAnyKeys(new Map([[{ foo: 1 }, 'bar'], ['key', 'value'],]), [{ one: 'two' }, 'example']) + assert.doesNotHaveAnyKeys(new Set([{ foo: 'bar' }, 'anotherKey']), [{ one: 'two' }, 'example',]) }) ``` ## hasAnyDeepKeys -- **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` +- **类型:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` -Asserts that `object` has at least one of the `keys` provided. Since Sets and Maps can have objects as keys you can use this assertion to perform a deep comparison. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. +断言 `object` 至少拥有一个提供的 `keys` 。由于 Set 和 Map 可以拥有对象作为键,你可以使用这个断言来进行深度比较。你也可以提供一个单独的对象而不是一个 keys 数组,它的键将被用作预期的键集。 ```ts import { assert, test } from 'vitest' test('assert.hasAnyDeepKeys', () => { - assert.hasAnyDeepKeys( - new Map([ - [{ one: 'one' }, 'valueOne'], - [1, 2], - ]), - { one: 'one' } - ) - assert.hasAnyDeepKeys( - new Map([ - [{ one: 'one' }, 'valueOne'], - [1, 2], - ]), - [{ one: 'one' }, { two: 'two' }] - ) - assert.hasAnyDeepKeys( - new Map([ - [{ one: 'one' }, 'valueOne'], - [{ two: 'two' }, 'valueTwo'], - ]), - [{ one: 'one' }, { two: 'two' }] - ) - assert.hasAnyDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), { - one: 'one', - }) - assert.hasAnyDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), [ - { one: 'one' }, - { three: 'three' }, - ]) - assert.hasAnyDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), [ - { one: 'one' }, - { two: 'two' }, - ]) + assert.hasAnyDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [1, 2],]), { one: 'one' }) + assert.hasAnyDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [1, 2],]), [{ one: 'one' }, { two: 'two' }]) + assert.hasAnyDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [{ two: 'two' }, 'valueTwo'],]), [{ one: 'one' }, { two: 'two' }]) + assert.hasAnyDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), { one: 'one', }) + assert.hasAnyDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), [{ one: 'one' }, { three: 'three' },]) + assert.hasAnyDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), [{ one: 'one' }, { two: 'two' },]) }) ``` ## hasAllDeepKeys -- **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` +- **类型:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` -Asserts that `object` has all and only all of the `keys` provided. Since Sets and Maps can have objects as keys you can use this assertion to perform a deep comparison. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. +断言 `object` 拥有且仅拥有所有提供的 `keys` 。由于 Set 和 Map 可以拥有对象作为键,你可以使用这个断言来进行深度比较。你也可以提供一个单独的对象而不是一个 keys 数组,它的键将被用作预期的键集。 ```ts import { assert, test } from 'vitest' test('assert.hasAnyDeepKeys', () => { - assert.hasAllDeepKeys(new Map([[{ one: 'one' }, 'valueOne']]), { - one: 'one', - }) - assert.hasAllDeepKeys( - new Map([ - [{ one: 'one' }, 'valueOne'], - [{ two: 'two' }, 'valueTwo'], - ]), - [{ one: 'one' }, { two: 'two' }] - ) + assert.hasAllDeepKeys(new Map([[{ one: 'one' }, 'valueOne']]), { one: 'one', }) + assert.hasAllDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [{ two: 'two' }, 'valueTwo'],]), [{ one: 'one' }, { two: 'two' }]) assert.hasAllDeepKeys(new Set([{ one: 'one' }]), { one: 'one' }) - assert.hasAllDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), [ - { one: 'one' }, - { two: 'two' }, - ]) + assert.hasAllDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), [{ one: 'one' }, { two: 'two' },]) }) ``` ## containsAllDeepKeys -- **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` +- **类型:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` -Asserts that `object` contains all of the `keys` provided. Since Sets and Maps can have objects as keys you can use this assertion to perform a deep comparison. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. +断言 `object` 包含所有提供的 `keys` 。由于 Set 和 Map 可以拥有对象作为键,你可以使用这个断言来进行深度比较。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 ```ts import { assert, test } from 'vitest' test('assert.containsAllDeepKeys', () => { - assert.containsAllDeepKeys( - new Map([ - [{ one: 'one' }, 'valueOne'], - [1, 2], - ]), - { one: 'one' } - ) - assert.containsAllDeepKeys( - new Map([ - [{ one: 'one' }, 'valueOne'], - [{ two: 'two' }, 'valueTwo'], - ]), - [{ one: 'one' }, { two: 'two' }] - ) - assert.containsAllDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), { - one: 'one', - }) - assert.containsAllDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), [ - { one: 'one' }, - { two: 'two' }, - ]) + assert.containsAllDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [1, 2],]), { one: 'one' }) + assert.containsAllDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [{ two: 'two' }, 'valueTwo'],]), [{ one: 'one' }, { two: 'two' }]) + assert.containsAllDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), { one: 'one', }) + assert.containsAllDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), [{ one: 'one' }, { two: 'two' },]) }) ``` ## doesNotHaveAnyDeepKeys -- **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` +- **类型:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` -Asserts that `object` has none of the `keys` provided. Since Sets and Maps can have objects as keys you can use this assertion to perform a deep comparison. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. +断言 `object` 不拥有任何提供的 `keys` 。由于 Set 和 Map 可以拥有对象作为键,你可以使用这个断言来进行深度比较。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 ```ts import { assert, test } from 'vitest' test('assert.doesNotHaveAnyDeepKeys', () => { - assert.doesNotHaveAnyDeepKeys( - new Map([ - [{ one: 'one' }, 'valueOne'], - [1, 2], - ]), - { thisDoesNot: 'exist' } - ) - assert.doesNotHaveAnyDeepKeys( - new Map([ - [{ one: 'one' }, 'valueOne'], - [{ two: 'two' }, 'valueTwo'], - ]), - [{ twenty: 'twenty' }, { fifty: 'fifty' }] - ) - assert.doesNotHaveAnyDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), { - twenty: 'twenty', - }) - assert.doesNotHaveAnyDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), [ - { twenty: 'twenty' }, - { fifty: 'fifty' }, - ]) + assert.doesNotHaveAnyDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [1, 2],]), { thisDoesNot: 'exist' }) + assert.doesNotHaveAnyDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [{ two: 'two' }, 'valueTwo'],]), [{ twenty: 'twenty' }, { fifty: 'fifty' }]) + assert.doesNotHaveAnyDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), { twenty: 'twenty', }) + assert.doesNotHaveAnyDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), [{ twenty: 'twenty' }, { fifty: 'fifty' },]) }) ``` ## doesNotHaveAllDeepKeys -- **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` +- **类型:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` -Asserts that `object` does not have at least one of the `keys` provided. Since Sets and Maps can have objects as keys you can use this assertion to perform a deep comparison. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. +断言 `object` 至少不拥有一个提供的 `keys` 。由于 Set 和 Map 可以拥有对象作为键,你可以使用这个断言来进行深度比较。你也可以提供一个单独的对象而不是一个 `keys` 数组,它的键将被用作预期的键集。 ```ts import { assert, test } from 'vitest' test('assert.doesNotHaveAllDeepKeys', () => { - assert.doesNotHaveAllDeepKeys( - new Map([ - [{ one: 'one' }, 'valueOne'], - [1, 2], - ]), - { thisDoesNot: 'exist' } - ) - assert.doesNotHaveAllDeepKeys( - new Map([ - [{ one: 'one' }, 'valueOne'], - [{ two: 'two' }, 'valueTwo'], - ]), - [{ twenty: 'twenty' }, { one: 'one' }] - ) - assert.doesNotHaveAllDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), { - twenty: 'twenty', - }) - assert.doesNotHaveAllDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), [ - { one: 'one' }, - { fifty: 'fifty' }, - ]) + assert.doesNotHaveAllDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [1, 2],]), { thisDoesNot: 'exist' }) + assert.doesNotHaveAllDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [{ two: 'two' }, 'valueTwo'],]), [{ twenty: 'twenty' }, { one: 'one' }]) + assert.doesNotHaveAllDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), { twenty: 'twenty', }) + assert.doesNotHaveAllDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), [{ one: 'one' }, { fifty: 'fifty' },]) }) ``` ## throws -- **Type:** +- **类型:** - `(fn: () => void, errMsgMatcher?: RegExp | string, ignored?: any, message?: string) => void` - `(fn: () => void, errorLike?: ErrorConstructor | Error | null, errMsgMatcher?: RegExp | string | null, message?: string) => void` -- **Alias:** +- **别名:** - `throw` - `Throw` -If `errorLike` is an Error constructor, asserts that `fn` will throw an error that is an instance of `errorLike`. If errorLike is an Error instance, asserts that the error thrown is the same instance as `errorLike`. If `errMsgMatcher` is provided, it also asserts that the error thrown will have a message matching `errMsgMatcher`. +如果 `errorLike` 是一个 Error 构造函数,则断言 `fn` 将抛出一个 errorLike 实例的错误。如果 `errorLike` 是一个 Error 实例,则断言抛出的错误与 `errorLike` 是同一个实例。如果提供了 `errMsgMatcher`,它还断言抛出的错误将具有与 `errMsgMatcher` 相匹配的消息。 ```ts import { assert, test } from 'vitest' @@ -1524,35 +1303,19 @@ test('assert.throws', () => { assert.throws(fn, /Error thrown must have a msg that matches this/) assert.throws(fn, ReferenceError) assert.throws(fn, errorInstance) - assert.throws( - fn, - ReferenceError, - 'Error thrown must be a ReferenceError and have this msg' - ) - assert.throws( - fn, - errorInstance, - 'Error thrown must be the same errorInstance and have this msg' - ) - assert.throws( - fn, - ReferenceError, - /Error thrown must be a ReferenceError and match this/ - ) - assert.throws( - fn, - errorInstance, - /Error thrown must be the same errorInstance and match this/ - ) + assert.throws(fn, ReferenceError, 'Error thrown must be a ReferenceError and have this msg') + assert.throws(fn, errorInstance, 'Error thrown must be the same errorInstance and have this msg') + assert.throws(fn, ReferenceError, /Error thrown must be a ReferenceError and match this/) + assert.throws(fn, errorInstance, /Error thrown must be the same errorInstance and match this/) }) ``` ## doesNotThrow -- **Type:** `(fn: () => void, errMsgMatcher?: RegExp | string, ignored?: any, message?: string) => void` -- **Type:** `(fn: () => void, errorLike?: ErrorConstructor | Error | null, errMsgMatcher?: RegExp | string | null, message?: string) => void` +- **类型:** `(fn: () => void, errMsgMatcher?: RegExp | string, ignored?: any, message?: string) => void` +- **类型:** `(fn: () => void, errorLike?: ErrorConstructor | Error | null, errMsgMatcher?: RegExp | string | null, message?: string) => void` -If `errorLike` is an Error constructor, asserts that `fn` will not throw an error that is an instance of `errorLike`. If errorLike is an Error instance, asserts that the error thrown is not the same instance as `errorLike`. If `errMsgMatcher` is provided, it also asserts that the error thrown will not have a message matching `errMsgMatcher`. +如果 `errorLike` 是一个 Error 构造函数,则断言 `fn` 不会 抛出一个 errorLike 实例的错误。如果 `errorLike` 是一个 Error 实例,则断言抛出的错误不是与 errorLike 是同一个实例。如果提供了 `errMsgMatcher`,它还断言抛出的错误不会 具有与 `errMsgMatcher` 相匹配的消息。 ```ts import { assert, test } from 'vitest' @@ -1571,9 +1334,9 @@ test('assert.doesNotThrow', () => { ## operator -- **Type:** `(val1: OperatorComparable, operator: Operator, val2: OperatorComparable, message?: string) => void` +- **类型:** `(val1: OperatorComparable, operator: Operator, val2: OperatorComparable, message?: string) => void` -Compare `val1` and `val2` using `operator`. +使用 `operator` 比较 `val1` 和 `val2` 。 ```ts import { assert, test } from 'vitest' @@ -1585,10 +1348,10 @@ test('assert.operator', () => { ## closeTo -- **Type:** `(actual: number, expected: number, delta: number, message?: string) => void` -- **Alias:** `approximately` +- **类型:** `(actual: number, expected: number, delta: number, message?: string) => void` +- **别名:** `approximately` -Asserts that the `actual` is equal `expected`, to within a +/- `delta` range. +断言 `actual` 等于 `expected`,误差范围控制在 +/- `delta` 内。 ```ts import { assert, test } from 'vitest' @@ -1600,9 +1363,9 @@ test('assert.closeTo', () => { ## sameMembers -- **Type:** `(set1: T[], set2: T[], message?: string) => void` +- **类型:** `(set1: T[], set2: T[], message?: string) => void` -Asserts that `set1` and `set2` have the same members in any order. Uses a strict equality check (===). +断言 `set1` 和 `set2` 具有相同的成员,但顺序可以不同。使用严格相等检查 (===)。 ```ts import { assert, test } from 'vitest' @@ -1614,9 +1377,9 @@ test('assert.sameMembers', () => { ## notSameMembers -- **Type:** `(set1: T[], set2: T[], message?: string) => void` +- **类型:** `(set1: T[], set2: T[], message?: string) => void` -Asserts that `set1` and `set2` don't have the same members in any order. Uses a strict equality check (===). +断言 `set1` 和 `set2` 不具有相同的成员,但顺序可以不同。使用严格相等检查 (===)。 ```ts import { assert, test } from 'vitest' @@ -1628,45 +1391,37 @@ test('assert.sameMembers', () => { ## sameDeepMembers -- **Type:** `(set1: T[], set2: T[], message?: string) => void` +- **类型:** `(set1: T[], set2: T[], message?: string) => void` -Asserts that `set1` and `set2` have the same members in any order. Uses a deep equality check. +断言 `set1` 和 `set2` 具有相同的成员,但顺序可以不同。使用深度相等检查。 ```ts import { assert, test } from 'vitest' test('assert.sameDeepMembers', () => { - assert.sameDeepMembers( - [{ a: 1 }, { b: 2 }, { c: 3 }], - [{ b: 2 }, { a: 1 }, { c: 3 }], - 'same deep members' - ) + assert.sameDeepMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ b: 2 }, { a: 1 }, { c: 3 }], 'same deep members') }) ``` ## notSameDeepMembers -- **Type:** `(set1: T[], set2: T[], message?: string) => void` +- **类型:** `(set1: T[], set2: T[], message?: string) => void` -Asserts that `set1` and `set2` don’t have the same members in any order. Uses a deep equality check. +断言 `set1` 和 `set2` 不具有相同的成员,但顺序可以不同。使用深度相等检查。 ```ts import { assert, test } from 'vitest' test('assert.sameDeepMembers', () => { - assert.sameDeepMembers( - [{ a: 1 }, { b: 2 }, { c: 3 }], - [{ b: 2 }, { a: 1 }, { c: 3 }], - 'same deep members' - ) + assert.sameDeepMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ b: 2 }, { a: 1 }, { c: 3 }], 'same deep members') }) ``` ## sameOrderedMembers -- **Type:** `(set1: T[], set2: T[], message?: string) => void` +- **类型:** `(set1: T[], set2: T[], message?: string) => void` -Asserts that `set1` and `set2` have the same members in the same order. Uses a strict equality check (===). +断言 `set1` 和 `set2` 具有相同的成员,并且顺序也相同。使用严格相等检查 (===)。 ```ts import { assert, test } from 'vitest' @@ -1678,68 +1433,52 @@ test('assert.sameOrderedMembers', () => { ## notSameOrderedMembers -- **Type:** `(set1: T[], set2: T[], message?: string) => void` +- **类型:** `(set1: T[], set2: T[], message?: string) => void` -Asserts that `set1` and `set2` have the same members in the same order. Uses a strict equality check (===). +断言 `set1` 和 `set2` 的成员不相同或顺序不同。使用严格相等比较 (===)。 ```ts import { assert, test } from 'vitest' test('assert.notSameOrderedMembers', () => { - assert.notSameOrderedMembers( - [1, 2, 3], - [2, 1, 3], - 'not same ordered members' - ) + assert.notSameOrderedMembers([1, 2, 3], [2, 1, 3], 'not same ordered members') }) ``` ## sameDeepOrderedMembers -- **Type:** `(set1: T[], set2: T[], message?: string) => void` +- **类型:** `(set1: T[], set2: T[], message?: string) => void` -Asserts that `set1` and `set2` have the same members in the same order. Uses a deep equality check. +断言 `set1` 和 `set2` 的成员相同且顺序相同。使用深度相等比较。 ```ts import { assert, test } from 'vitest' test('assert.sameDeepOrderedMembers', () => { - assert.sameDeepOrderedMembers( - [{ a: 1 }, { b: 2 }, { c: 3 }], - [{ a: 1 }, { b: 2 }, { c: 3 }], - 'same deep ordered members' - ) + assert.sameDeepOrderedMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ a: 1 }, { b: 2 }, { c: 3 }], 'same deep ordered members') }) ``` ## notSameDeepOrderedMembers -- **Type:** `(set1: T[], set2: T[], message?: string) => void` +- **类型:** `(set1: T[], set2: T[], message?: string) => void` -Asserts that `set1` and `set2` don’t have the same members in the same order. Uses a deep equality check. +断言 `set1` 和 `set2` 的成员不相同或顺序不同。使用深度相等比较。 ```ts import { assert, test } from 'vitest' test('assert.notSameDeepOrderedMembers', () => { - assert.notSameDeepOrderedMembers( - [{ a: 1 }, { b: 2 }, { c: 3 }], - [{ a: 1 }, { b: 2 }, { z: 5 }], - 'not same deep ordered members' - ) - assert.notSameDeepOrderedMembers( - [{ a: 1 }, { b: 2 }, { c: 3 }], - [{ b: 2 }, { a: 1 }, { c: 3 }], - 'not same deep ordered members' - ) + assert.notSameDeepOrderedMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ a: 1 }, { b: 2 }, { z: 5 }], 'not same deep ordered members') + assert.notSameDeepOrderedMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ b: 2 }, { a: 1 }, { c: 3 }], 'not same deep ordered members') }) ``` ## includeMembers -- **Type:** `(superset: T[], subset: T[], message?: string) => void` +- **类型:** `(superset: T[], subset: T[], message?: string) => void` -Asserts that `subset` is included in `superset` in any order. Uses a strict equality check (===). Duplicates are ignored. +断言 `subset` 被包含在 `superset` 中,顺序可以不同。使用严格相等比较 (===)。忽略重复项。 ```ts import { assert, test } from 'vitest' @@ -1751,9 +1490,9 @@ test('assert.includeMembers', () => { ## notIncludeMembers -- **Type:** `(superset: T[], subset: T[], message?: string) => void` +- **类型:** `(superset: T[], subset: T[], message?: string) => void` -Asserts that `subset` isn't included in `superset` in any order. Uses a strict equality check (===). Duplicates are ignored. +断言 `subset` 未被包含在 `superset` 中,顺序可以不同。使用严格相等比较 (===)。忽略重复项。 ```ts import { assert, test } from 'vitest' @@ -1765,45 +1504,37 @@ test('assert.notIncludeMembers', () => { ## includeDeepMembers -- **Type:** `(superset: T[], subset: T[], message?: string) => void` +- **类型:** `(superset: T[], subset: T[], message?: string) => void` -Asserts that `subset` is included in `superset` in any order. Uses a deep equality check. Duplicates are ignored. +断言 `subset` 被包含在 `superset` 中,顺序可以不同。使用深度相等比较。忽略重复项。 ```ts import { assert, test } from 'vitest' test('assert.includeDeepMembers', () => { - assert.includeDeepMembers( - [{ a: 1 }, { b: 2 }, { c: 3 }], - [{ b: 2 }, { a: 1 }, { b: 2 }], - 'include deep members' - ) + assert.includeDeepMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ b: 2 }, { a: 1 }, { b: 2 }], 'include deep members') }) ``` ## notIncludeDeepMembers -- **Type:** `(superset: T[], subset: T[], message?: string) => void` +- **类型:** `(superset: T[], subset: T[], message?: string) => void` -Asserts that `subset` isn’t included in `superset` in any order. Uses a deep equality check. Duplicates are ignored. +断言 `subset` 未被包含在 `superset` 中,顺序可以不同。使用深度相等比较。忽略重复项。 ```ts import { assert, test } from 'vitest' test('assert.notIncludeDeepMembers', () => { - assert.notIncludeDeepMembers( - [{ a: 1 }, { b: 2 }, { c: 3 }], - [{ b: 2 }, { f: 5 }], - 'not include deep members' - ) + assert.notIncludeDeepMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ b: 2 }, { f: 5 }], 'not include deep members') }) ``` ## includeOrderedMembers -- **Type:** `(superset: T[], subset: T[], message?: string) => void` +- **类型:** `(superset: T[], subset: T[], message?: string) => void` -Asserts that `subset` is included in `superset` in the same order beginning with the first element in `superset`. Uses a strict equality check (===). +断言 `subset` 被包含在 `superset` 中,顺序相同,从 `superset` 中的第一个元素开始。使用严格相等比较 (===)。 ```ts import { assert, test } from 'vitest' @@ -1815,78 +1546,54 @@ test('assert.includeOrderedMembers', () => { ## notIncludeOrderedMembers -- **Type:** `(superset: T[], subset: T[], message?: string) => void` +- **类型:** `(superset: T[], subset: T[], message?: string) => void` -Asserts that `subset` isn't included in `superset` in the same order beginning with the first element in `superset`. Uses a strict equality check (===). +断言 `subset` 未被包含在 `superset` 中,顺序相同,从 `superset` 中的第一个元素开始。使用严格相等比较 (===)。 ```ts import { assert, test } from 'vitest' test('assert.notIncludeOrderedMembers', () => { - assert.notIncludeOrderedMembers( - [1, 2, 3], - [2, 1], - 'not include ordered members' - ) - assert.notIncludeOrderedMembers( - [1, 2, 3], - [2, 3], - 'not include ordered members' - ) + assert.notIncludeOrderedMembers([1, 2, 3], [2, 1], 'not include ordered members') + assert.notIncludeOrderedMembers([1, 2, 3], [2, 3], 'not include ordered members') }) ``` ## includeDeepOrderedMembers -- **Type:** `(superset: T[], subset: T[], message?: string) => void` +- **类型:** `(superset: T[], subset: T[], message?: string) => void` -Asserts that `subset` is included in `superset` in the same order beginning with the first element in `superset`. Uses a deep equality check. +断言 `subset` 被包含在 `superset` 中,顺序相同,从 `superset` 中的第一个元素开始。使用深度相等比较。 ```ts import { assert, test } from 'vitest' test('assert.includeDeepOrderedMembers', () => { - assert.includeDeepOrderedMembers( - [{ a: 1 }, { b: 2 }, { c: 3 }], - [{ a: 1 }, { b: 2 }], - 'include deep ordered members' - ) + assert.includeDeepOrderedMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ a: 1 }, { b: 2 }], 'include deep ordered members') }) ``` ## notIncludeDeepOrderedMembers -- **Type:** `(superset: T[], subset: T[], message?: string) => void` +- **类型:** `(superset: T[], subset: T[], message?: string) => void` -Asserts that `subset` isn’t included in `superset` in the same order beginning with the first element in superset. Uses a deep equality check. +断言 `subset` 未被包含在 `superset` 中,顺序相同,从 `superset` 中的第一个元素开始。使用深度相等比较。 ```ts import { assert, test } from 'vitest' test('assert.includeDeepOrderedMembers', () => { - assert.notIncludeDeepOrderedMembers( - [{ a: 1 }, { b: 2 }, { c: 3 }], - [{ a: 1 }, { f: 5 }], - 'not include deep ordered members' - ) - assert.notIncludeDeepOrderedMembers( - [{ a: 1 }, { b: 2 }, { c: 3 }], - [{ b: 2 }, { a: 1 }], - 'not include deep ordered members' - ) - assert.notIncludeDeepOrderedMembers( - [{ a: 1 }, { b: 2 }, { c: 3 }], - [{ b: 2 }, { c: 3 }], - 'not include deep ordered members' - ) + assert.notIncludeDeepOrderedMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ a: 1 }, { f: 5 }], 'not include deep ordered members') + assert.notIncludeDeepOrderedMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ b: 2 }, { a: 1 }], 'not include deep ordered members') + assert.notIncludeDeepOrderedMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ b: 2 }, { c: 3 }], 'not include deep ordered members') }) ``` ## oneOf -- **Type:** `(inList: T, list: T[], message?: string) => void` +- **类型:** `(inList: T, list: T[], message?: string) => void` -Asserts that non-object, non-array value `inList` appears in the flat array `list`. +断言非对象、非数组值 `inList` 出现在扁平数组 list 中。 ```ts import { assert, test } from 'vitest' @@ -1898,259 +1605,233 @@ test('assert.oneOf', () => { ## changes -- **Type:** `(modifier: Function, object: T, property: string, message?: string) => void` +- **类型:** `(modifier: Function, object: T, property: string, message?: string) => void` -Asserts that a `modifier` changes the `object` of a `property`. +断言 `函数` 用于修改 `property` 所属 `object` 。 ```ts import { assert, test } from 'vitest' test('assert.changes', () => { const obj = { val: 10 } - function fn() { - obj.val = 22 - } + function fn() { obj.val = 22 }; assert.changes(fn, obj, 'val') }) ``` ## changesBy -- **Type:** `(modifier: Function, object: T, property: string, change: number, message?: string) => void` +- **类型:** `(modifier: Function, object: T, property: string, change: number, message?: string) => void` -Asserts that a `modifier` changes the `object` of a `property` by a `change`. +断言 `函数` 通过 `change` 修改 `property` 所属的 `object` 。 ```ts import { assert, test } from 'vitest' test('assert.changesBy', () => { const obj = { val: 10 } - function fn() { - obj.val += 2 - } + function fn() { obj.val += 2 }; assert.changesBy(fn, obj, 'val', 2) }) ``` ## doesNotChange -- **Type:** `(modifier: Function, object: T, property: string, message?: string) => void` +- **类型:** `(modifier: Function, object: T, property: string, message?: string) => void` -Asserts that a `modifier` does not changes the `object` of a `property`. +断言 `函数` 不会通过 `change` 修改 `property` 或 `函数` 返回值的 `object` 。 ```ts import { assert, test } from 'vitest' test('assert.doesNotChange', () => { const obj = { val: 10 } - function fn() { - obj.val += 2 - } + function fn() { obj.val += 2 }; assert.doesNotChange(fn, obj, 'val', 2) }) ``` ## changesButNotBy -- **Type:** `(modifier: Function, object: T, property: string, change:number, message?: string) => void` +- **类型:** `(modifier: Function, object: T, property: string, change:number, message?: string) => void` -Asserts that a `modifier` does not change the `object` of a `property` or of a `modifier` return value by a `change`. +断言 `函数` 不会通过 `change` 修改 `property` 或 `函数` 返回值的所属对象。 ```ts import { assert, test } from 'vitest' test('assert.changesButNotBy', () => { const obj = { val: 10 } - function fn() { - obj.val += 10 - } + function fn() { obj.val += 10 }; assert.changesButNotBy(fn, obj, 'val', 5) }) ``` ## increases -- **Type:** `(modifier: Function, object: T, property: string, message?: string) => void` +- **类型:** `(modifier: Function, object: T, property: string, message?: string) => void` -Asserts that a `modifier` increases a numeric `object`'s `property`. +断言 `函数` 会增加数值类型对象属性。 ```ts import { assert, test } from 'vitest' test('assert.increases', () => { const obj = { val: 10 } - function fn() { - obj.val = 13 - } + function fn() { obj.val = 13 }; assert.increases(fn, obj, 'val') }) ``` ## increasesBy -- **Type:** `(modifier: Function, object: T, property: string, change: number, message?: string) => void` +- **类型:** `(modifier: Function, object: T, property: string, change: number, message?: string) => void` -Asserts that a `modifier` increases a numeric `object`'s `property` or a `modifier` return value by an `change`. +断言 `函数` 会通过 `change` 增加数值类型对象属性或 `函数` 返回值的数值。 ```ts import { assert, test } from 'vitest' -test('assert.increases', () => { +test('assert.increasesBy', () => { const obj = { val: 10 } - function fn() { - obj.val += 10 - } - assert.increases(fn, obj, 'val', 10) + function fn() { obj.val += 10 }; + assert.increasesBy(fn, obj, 'val', 10) }) ``` ## doesNotIncrease -- **Type:** `(modifier: Function, object: T, property: string, message?: string) => void` +- **类型:** `(modifier: Function, object: T, property: string, message?: string) => void` -Asserts that a `modifier` does not increases a numeric `object`'s `property`. +断言 `函数` 不会增加数值类型对象属性。 ```ts import { assert, test } from 'vitest' test('assert.doesNotIncrease', () => { const obj = { val: 10 } - function fn() { - obj.val = 8 - } + function fn() { obj.val = 8 } assert.doesNotIncrease(fn, obj, 'val') }) ``` ## increasesButNotBy -- **Type:** `(modifier: Function, object: T, property: string, change: number, message?: string) => void` +- **类型:** `(modifier: Function, object: T, property: string, change: number, message?: string) => void` -Asserts that a `modifier` does not increases a numeric `object`'s `property` or a `modifier` return value by an `change`. +断言 `函数` 不会通过 `change` 增加数值类型对象属性或 `函数` 返回值的数值。 ```ts import { assert, test } from 'vitest' test('assert.increasesButNotBy', () => { const obj = { val: 10 } - function fn() { - obj.val += 15 - } + function fn() { obj.val += 15 }; assert.increasesButNotBy(fn, obj, 'val', 10) }) ``` ## decreases -- **Type:** `(modifier: Function, object: T, property: string, message?: string) => void` +- **类型:** `(modifier: Function, object: T, property: string, message?: string) => void` -Asserts that a `modifier` decreases a numeric `object`'s `property`. +断言 `函数` 不会通过 `change` 增加数值类型对象属性或 `函数` 返回值的数值。 ```ts import { assert, test } from 'vitest' test('assert.decreases', () => { const obj = { val: 10 } - function fn() { - obj.val = 5 - } + function fn() { obj.val = 5 }; assert.decreases(fn, obj, 'val') }) ``` ## decreasesBy -- **Type:** `(modifier: Function, object: T, property: string, change: number, message?: string) => void` +- **类型:** `(modifier: Function, object: T, property: string, change: number, message?: string) => void` -Asserts that a `modifier` decreases a numeric `object`'s `property` or a `modifier` return value by a `change`. +断言 `函数` 会通过 `change` 减少数值类型对象属性或 `函数` 返回值的数值。 ```ts import { assert, test } from 'vitest' test('assert.decreasesBy', () => { const obj = { val: 10 } - function fn() { - obj.val -= 5 - } + function fn() { obj.val -= 5 }; assert.decreasesBy(fn, obj, 'val', 5) }) ``` ## doesNotDecrease -- **Type:** `(modifier: Function, object: T, property: string, message?: string) => void` +- **类型:** `(modifier: Function, object: T, property: string, message?: string) => void` -Asserts that a `modifier` dose not decrease a numeric `object`'s `property`. +断言 `函数` 不会减少数值类型对象属性。 ```ts import { assert, test } from 'vitest' test('assert.doesNotDecrease', () => { const obj = { val: 10 } - function fn() { - obj.val = 15 - } + function fn() { obj.val = 15 } assert.doesNotDecrease(fn, obj, 'val') }) ``` ## doesNotDecreaseBy -- **Type:** `(modifier: Function, object: T, property: string, change: number, message?: string) => void` +- **类型:** `(modifier: Function, object: T, property: string, change: number, message?: string) => void` -Asserts that a `modifier` does not decrease a numeric `object`'s `property` or a `modifier` return value by a `change`. +断言 `函数` 不会通过 `change` 减少数值类型对象属性或 `函数` 返回值的数值。 ```ts import { assert, test } from 'vitest' test('assert.doesNotDecreaseBy', () => { const obj = { val: 10 } - function fn() { - obj.val = 5 - } + function fn() { obj.val = 5 }; assert.doesNotDecreaseBy(fn, obj, 'val', 1) }) ``` ## decreasesButNotBy -- **Type:** `(modifier: Function, object: T, property: string, change: number, message?: string) => void` +- **类型:** `(modifier: Function, object: T, property: string, change: number, message?: string) => void` -Asserts that a `modifier` does not decrease a numeric `object`'s `property` or a `modifier` return value by a `change`. +断言 `函数` 不会通过 change 减少数值类型对象属性或 `函数` 返回值的数值。 ```ts import { assert, test } from 'vitest' test('assert.decreasesButNotBy', () => { const obj = { val: 10 } - function fn() { - obj.val = 5 - } + function fn() { obj.val = 5 }; assert.decreasesButNotBy(fn, obj, 'val', 1) }) ``` ## ifError -- **Type:** `(object: T, message?: string) => void` +- **类型:** `(object: T, message?: string) => void` -Asserts if `object` is not a false value, and throws if it is a true value. This is added to allow for chai to be a drop-in replacement for Node’s assert class. +断言 `object` 是否为假值,如果它是真值则抛出错误。这是为了允许 chai 作为 Node 的 assert 类的一个直接替代品。 ```ts import { assert, test } from 'vitest' test('assert.ifError', () => { const err = new Error('I am a custom error') - assert.ifError(err) // Rethrows err! + assert.ifError(err) // 重新抛出错误! }) ``` ## isExtensible -- **Type:** `(object: T, message?: string) => void` -- **Alias:** `extensible` +- **类型:** `(object: T, message?: string) => void` +- **别名:** `extensible` -Asserts that `object` is extensible (can have new properties added to it). +断言 `object` 是可扩展的(可以向其添加新的属性)。 ```ts import { assert, test } from 'vitest' @@ -2162,10 +1843,10 @@ test('assert.isExtensible', () => { ## isNotExtensible -- **Type:** `(object: T, message?: string) => void` -- **Alias:** `notExtensible` +- **类型:** `(object: T, message?: string) => void` +- **别名:** `notExtensible` -Asserts that `object` is not extensible (can not have new properties added to it). +断言 `object` 是不可扩展的 (不能添加新属性)。 ```ts import { assert, test } from 'vitest' @@ -2183,10 +1864,10 @@ test('assert.isNotExtensible', () => { ## isSealed -- **Type:** `(object: T, message?: string) => void` -- **Alias:** `sealed` +- **类型:** `(object: T, message?: string) => void` +- **别名:** `sealed` -Asserts that `object` is sealed (cannot have new properties added to it and its existing properties cannot be removed). +断言 `object` 是密封的(不能向其添加新的属性,也不能删除其现有属性)。 ```ts import { assert, test } from 'vitest' @@ -2202,10 +1883,10 @@ test('assert.isSealed', () => { ## isNotSealed -- **Type:** `(object: T, message?: string) => void` -- **Alias:** `notSealed` +- **类型:** `(object: T, message?: string) => void` +- **别名:** `notSealed` -Asserts that `object` is not sealed (can have new properties added to it and its existing properties can be removed). +断言 `object` 未被密封(可以添加新属性,并且可以删除其现有属性)。 ```ts import { assert, test } from 'vitest' @@ -2217,10 +1898,10 @@ test('assert.isNotSealed', () => { ## isFrozen -- **Type:** `(object: T, message?: string) => void` -- **Alias:** `frozen` +- **类型:** `(object: T, message?: string) => void` +- **别名:** `frozen` -Asserts that object is frozen (cannot have new properties added to it and its existing properties cannot be modified). +断言 `object` 是冻结的(不能向其添加新的属性,也不能修改其现有属性)。 ```ts import { assert, test } from 'vitest' @@ -2233,10 +1914,10 @@ test('assert.isFrozen', () => { ## isNotFrozen -- **Type:** `(object: T, message?: string) => void` -- **Alias:** `notFrozen` +- **类型:** `(object: T, message?: string) => void` +- **别名:** `notFrozen` -Asserts that `object` is not frozen (can have new properties added to it and its existing properties can be modified). +断言 `object` 未被冻结(可以向其添加新属性,并且可以修改其现有属性)。 ```ts import { assert, test } from 'vitest' @@ -2248,10 +1929,10 @@ test('assert.isNotFrozen', () => { ## isEmpty -- **Type:** `(target: T, message?: string) => void` -- **Alias:** `empty` +- **类型:** `(target: T, message?: string) => void` +- **别名:** `empty` -Asserts that the `target` does not contain any values. For arrays and strings, it checks the length property. For Map and Set instances, it checks the size property. For non-function objects, it gets the count of its own enumerable string keys. +断言 `target` 不包含任何值。对于数组和字符串,它检查 length 属性。对于 Map 和 Set 实例,它检查 size 属性。对于非函数对象,它获取自身可枚举字符串键的数量。 ```ts import { assert, test } from 'vitest' @@ -2266,10 +1947,10 @@ test('assert.isEmpty', () => { ## isNotEmpty -- **Type:** `(object: T, message?: string) => void` -- **Alias:** `notEmpty` +- **类型:** `(object: T, message?: string) => void` +- **别名:** `notEmpty` -Asserts that the `target` contains values. For arrays and strings, it checks the length property. For Map and Set instances, it checks the size property. For non-function objects, it gets the count of its own enumerable string keys. +断言 `target` 包含值。对于数组和字符串,它检查 length 属性。对于 Map 和 Set 实例,它检查 size 属性。对于非函数对象,它获取自身可枚举字符串键的数量。 ```ts import { assert, test } from 'vitest' diff --git a/api/expect-typeof.md b/api/expect-typeof.md index 1a8e1ae9..cbae5819 100644 --- a/api/expect-typeof.md +++ b/api/expect-typeof.md @@ -1,22 +1,22 @@ # expectTypeOf -- **类型:** `(a: unknown) => ExpectTypeOf` - ::: warning 在运行时,此函数不执行任何操作。要 [enable typechecking](/guide/testing-types#run-typechecking),不要忘记传递 `--typecheck` 标志。 ::: +- **类型:** `(a: unknown) => ExpectTypeOf` + ## not - **类型:** `ExpectTypeOf` - 你可以使用 `.not` 属性否定所有断言。 +你可以使用 `.not` 属性否定所有断言。 ## toEqualTypeOf - **类型:** `(expected: T) => void` - 该匹配器将检查类型是否完全相同。如果两个对象具有不同的值但类型相同,则此匹配器不会失败。但是,如果对象缺少属性,它将失败。 +该匹配器将检查类型是否完全相同。如果两个对象具有不同的值但类型相同,则此匹配器不会失败。但是,如果对象缺少属性,它将失败。 ```ts twoslash import { expectTypeOf } from 'vitest' @@ -31,7 +31,7 @@ expectTypeOf({ a: 1, b: 1 }).not.toEqualTypeOf<{ a: number }>() - **类型:** `(expected: T) => void` -此匹配器检查期望类型是否扩展了提供的类型。它不同于 `toEqual`,更类似于 [expect's](/api/expect) `toMatchObject()`。使用此匹配器,你可以检查对象是否“匹配”类型。 +此匹配器检查期望类型是否扩展了提供的类型。它不同于 `toEqual`,更类似于 [expect](/api/expect) 的 `toMatchObject()`。使用此匹配器,你可以检查对象是否 “匹配” 类型。 ```ts twoslash import { expectTypeOf } from 'vitest' @@ -50,7 +50,7 @@ expectTypeOf().not.toMatchTypeOf() ```ts twoslash import { expectTypeOf } from 'vitest' -type ResponsiveProp = T | T[] | { xs?: T, sm?: T, md?: T } +type ResponsiveProp = T | T[] | { xs?: T; sm?: T; md?: T } interface CSSProperties { margin?: string @@ -64,15 +64,15 @@ function getResponsiveProp(_props: T): ResponsiveProp { const cssProperties: CSSProperties = { margin: '1px', padding: '2px' } expectTypeOf(getResponsiveProp(cssProperties)) - .extract<{ xs?: any }>() // extracts the last type from a union + .extract<{ xs?: any }>() // 从联合类型中提取最后一个类型 .toEqualTypeOf<{ - xs?: CSSProperties - sm?: CSSProperties - md?: CSSProperties - }>() + xs?: CSSProperties + sm?: CSSProperties + md?: CSSProperties +}>() expectTypeOf(getResponsiveProp(cssProperties)) - .extract() // extracts an array from a union + .extract() // 从联合类型中提取数组 .toEqualTypeOf() ``` @@ -93,17 +93,16 @@ type ResponsiveProp = T | T[] | { xs?: T; sm?: T; md?: T } interface CSSProperties { margin?: string; padding?: string } -function getResponsiveProp(\_props: T): ResponsiveProp { -return {} +function getResponsiveProp(_props: T): ResponsiveProp { + return {} } const cssProperties: CSSProperties = { margin: '1px', padding: '2px' } expectTypeOf(getResponsiveProp(cssProperties)) -.exclude() -.exclude<{ xs?: unknown }>() // or just .exclude() -.toEqualTypeOf() - + .exclude() + .exclude<{ xs?: unknown }>() // 或 just .exclude() + .toEqualTypeOf() ``` ::: warning @@ -181,9 +180,7 @@ expectTypeOf(foo).parameter(1).toBeString() ```ts twoslash import { expectTypeOf } from 'vitest' -expectTypeOf(Date).constructorParameters.toEqualTypeOf< - [] | [string | number | Date] ->() +expectTypeOf(Date).constructorParameters.toEqualTypeOf<[] | [string | number | Date]>() ``` ::: warning diff --git a/api/expect.md b/api/expect.md index 087db3e0..4cc24d69 100644 --- a/api/expect.md +++ b/api/expect.md @@ -19,12 +19,12 @@ expect(input).to.equal(2) // chai API expect(input).toBe(2) // jest API ``` -从技术上讲,这个示例没有使用 [`test`](/api/#test) 函数,因此在控制台中你将看到 Nodejs 错误而不是 Vitest 输出。 要了解更多关于 `test` 的信息,请阅读[测试 API 参考](/api/)。 +从技术上讲,这个示例没有使用 [`test`](/api/#test) 函数,因此在控制台中你将看到 Nodejs 错误而不是 Vitest 输出。 要了解更多关于 `test` 的信息,请阅读 [测试 API 参考](/api/)。 此外,`expect` 可以静态地使用来访问匹配器函数,稍后将会介绍。 ::: warning -如果表达式没有类型错误,则 `expect` 对测试类型没有影响。 如果你想使用 Vitest 作为[类型检查器](/guide/testing-types),请使用 [`expectTypeOf`](/api/expect-typeof) 或 [`assertType`](/api/assert-type) 。 +如果表达式没有类型错误,则 `expect` 对测试类型没有影响。 如果你想使用 Vitest 作为 [类型测试](/guide/testing-types) ,请使用 [`expectTypeOf`](/api/expect-typeof) 或 [`assertType`](/api/assert-type) 。 ::: ## soft @@ -37,10 +37,10 @@ expect(input).toBe(2) // jest API import { expect, test } from 'vitest' test('expect.soft test', () => { - expect.soft(1 + 1).toBe(3) // mark the test as fail and continue - expect.soft(1 + 2).toBe(4) // mark the test as fail and continue + expect.soft(1 + 1).toBe(3) // 将期望标记为失败并继续 + expect.soft(1 + 2).toBe(4) // 将期望标记为失败并继续 }) -// At the end of the test, the above errors will be output. +// 在执行结束后,报告器会报告这两个错误。 ``` 它也可以与 `expect` 一起使用。 如果 `expect` 断言失败,测试将终止并显示所有错误。 @@ -49,9 +49,9 @@ test('expect.soft test', () => { import { expect, test } from 'vitest' test('expect.soft test', () => { - expect.soft(1 + 1).toBe(3) // mark the test as fail and continue - expect(1 + 2).toBe(4) // failed and terminate the test, all previous errors will be output - expect.soft(1 + 3).toBe(5) // do not run + expect.soft(1 + 1).toBe(3) // 将期望标记为失败并继续 + expect(1 + 2).toBe(4) // 测试失败并终止执行,所有先前错误将被输出 + expect.soft(1 + 3).toBe(5) // 不再执行 }) ``` @@ -59,33 +59,6 @@ test('expect.soft test', () => { `expect.soft` 只能在 [`test`](/api/#test) 函数的内部使用。 ::: -## poll - -- **类型:** `ExpectStatic & (actual: () => any, options: { interval, timeout, message }) => Assertions` - -`expect.poll` 重新运行断言,直到成功为止。你可以通过设置 `interval` 和 `timeout` 选项来配置 Vitest 应重新运行 `expect.poll` 回调的次数。 - -如果在 `expect.poll` 回调中抛出错误,Vitest 将重试直到超时为止。 - -::: warning -`expect.poll` 使每个断言都异步,所以不要忘记等待它,否则可能会收到未经处理的 promise 拒绝。 - -`expect.poll` 不适用于多个匹配器: - -- 快照匹配器不受支持,因为它们总是成功的。如果你的情况不稳定,请考虑首先使用 [`vi.waitFor`](/api/vi#vi-waitfor) 解决: - -```ts -import { expect, vi } from 'vitest' - -const flakyValue = await vi.waitFor(() => getFlakyValue()) -expect(flakyValue).toMatchSnapshot() -``` - -- `.resolves` 和 `.rejects` 不支持。 如果它是异步的,`expect.poll` 已经在等待。 -- `toThrow` 及其别名不受支持,因为 `expect.poll` 条件总是在匹配器获取值之前解析。 - -::: - ## not 使用 `not` 将否定该断言。 例如,此代码断言 `input` 值不等于 `2`。 如果相等,断言将抛出错误,测试将失败。 @@ -143,9 +116,9 @@ test.fails('decimals are not equal in javascript', () => { }) test('decimals are rounded to 5 after the point', () => { - // 0.2 + 0.1 is 0.30000 | "000000000004" removed + // 0.2 + 0.1 等于 0.30000 | "000000000004" 被移除 expect(0.2 + 0.1).toBeCloseTo(0.3, 5) - // nothing from 0.30000000000000004 is removed + // 没有从 0.30000000000000004 移除任何内容 expect(0.2 + 0.1).not.toBeCloseTo(0.3, 50) }) ``` @@ -206,7 +179,7 @@ if (stocks.getInfo('Bill')) { } ``` -因此,如果要测试 `stocks.getInfo` 是否真实,可以这样写: +因此,如果要测试 `stocks.getInfo` 是否是 true,可以这样写: ```ts import { expect, test } from 'vitest' @@ -220,7 +193,7 @@ test('if we know Bill stock, sell apples to him', () => { }) ``` -除了 `false`、`null`、`undefined`、`NaN`、`0`、`-0`、`0n`、`""` 和 `document.all` 以外,JavaScript 中的一切都是真实的。 +除了 `false`、`null`、`undefined`、`NaN`、`0`、`-0`、`0n`、`""` 和 `document.all` 以外,JavaScript 中的一切都是 true。 ## toBeFalsy @@ -240,7 +213,7 @@ if (!stocks.stockFailed('Bill')) { } ``` -因此,如果要测试`stocks.stockFailed`是否是虚假的,可以这样写: +因此,如果要测试 `stocks.stockFailed` 是否是 false,可以这样写: ```ts import { expect, test } from 'vitest' @@ -254,7 +227,7 @@ test('if Bill stock hasn\'t failed, sell apples to him', () => { }) ``` -除了 `false`、`null`、`undefined`、`NaN`、`0`、`-0`、`0n`、`""` 和 `document.all` 以外,JavaScript 中的一切都是真实的。 +除了 `false`、`null`、`undefined`、`NaN`、`0`、`-0`、`0n`、`""` 和 `document.all` 以外,JavaScript 中的一切都是 true。 ## toBeNull @@ -278,7 +251,7 @@ test('we don\'t have apples', () => { - **类型:** `() => Awaitable` -`toBeNaN` 简单地断言某些内容是否为 `NaN`。toBe(NaN)` 的别名。 +`toBeNaN` 简单地断言某些内容是否为 `NaN`。`toBe(NaN)` 的别名。 ```ts twoslash import { expect, test } from 'vitest' @@ -462,9 +435,9 @@ test('the fruit list contains orange', () => { expect(getAllFruits()).toContain('orange') const element = document.querySelector('#el') - // element has a class + // element 有 class 属性 flex expect(element.classList).toContain('flex') - // element is inside another one + // element 是 #wrapper 的子元素 expect(document.querySelector('#wrapper')).toContain(element) }) ``` @@ -498,7 +471,7 @@ test('toHaveLength', () => { expect('abc').toHaveLength(3) expect([1, 2, 3]).toHaveLength(3) - expect('').not.toHaveLength(3) // doesn't have .length of 3 + expect('').not.toHaveLength(3) // length 不等于3 expect({ length: 3 }).toHaveLength(3) }) ``` @@ -536,25 +509,25 @@ const invoice = { } test('John Doe Invoice', () => { - expect(invoice).toHaveProperty('isActive') // assert that the key exists - expect(invoice).toHaveProperty('total_amount', 5000) // assert that the key exists and the value is equal + expect(invoice).toHaveProperty('isActive') // 断言对应的key存在 + expect(invoice).toHaveProperty('total_amount', 5000) // 断言对应的key存在,并且值相等 - expect(invoice).not.toHaveProperty('account') // assert that this key does not exist + expect(invoice).not.toHaveProperty('account') // 断言对应的key不存在 - // Deep referencing using dot notation + // 使用点号进行深层引用 expect(invoice).toHaveProperty('customer.first_name') expect(invoice).toHaveProperty('customer.last_name', 'Doe') expect(invoice).not.toHaveProperty('customer.location', 'India') - // Deep referencing using an array containing the key + // 使用含键名的数组进行深层引用 expect(invoice).toHaveProperty('items[0].type', 'apples') - expect(invoice).toHaveProperty('items.0.type', 'apples') // dot notation also works + expect(invoice).toHaveProperty('items.0.type', 'apples') // 也可以使用点号 - // Deep referencing using an array containing the keyPath + // 通过包含键路径的数组实现深层引用 expect(invoice).toHaveProperty(['items', 0, 'type'], 'apples') - expect(invoice).toHaveProperty(['items', '0', 'type'], 'apples') // string notation also works + expect(invoice).toHaveProperty(['items', '0', 'type'], 'apples') // 字符串表示法同样适用 - // Wrap your key in an array to avoid the key from being parsed as a deep reference + // 将键名包裹在数组中,避免其被解析为深层引用 expect(invoice).toHaveProperty(['P.O'], '12345') }) ``` @@ -570,7 +543,7 @@ import { expect, test } from 'vitest' test('top fruits', () => { expect('top fruits include apple, orange and grape').toMatch(/apple/) - expect('applefruits').toMatch('fruit') // toMatch also accepts a string + expect('applefruits').toMatch('fruit') // toMatch 同样支持字符串匹配 }) ``` @@ -618,7 +591,7 @@ test('invoice has john personal details', () => { }) test('the number of elements must match exactly', () => { - // Assert that an array of object matches + // 断言对象数组的匹配情况 expect([{ foo: 'bar' }, { baz: 1 }]).toMatchObject([ { foo: 'bar' }, { baz: 1 }, @@ -653,15 +626,15 @@ function getFruitStock(type: string) { throw new Error('Pineapples are not in stock') } - // Do some other stuff + // 做一些其他的东西 } test('throws on pineapples', () => { - // Test that the error message says "stock" somewhere: these are equivalent + // 测试错误信息包含 “stock”,这两种写法是等效的 expect(() => getFruitStock('pineapples')).toThrowError(/stock/) expect(() => getFruitStock('pineapples')).toThrowError('stock') - // Test the exact error message + // 测试确切的错误消息 expect(() => getFruitStock('pineapples')).toThrowError( /^Pineapples are not in stock$/ ) @@ -728,7 +701,7 @@ import { expect, test } from 'vitest' test('matches inline snapshot', () => { const data = { foo: new Set(['bar', 'snapshot']) } - // Vitest will update following content when updating the snapshot + // Vitest 将在更新快照的同时更新以下内容 expect(data).toMatchInlineSnapshot(` { "foo": Set { @@ -758,7 +731,7 @@ test('matches snapshot', () => { }) ``` -## toMatchFileSnapshot {#tomatchfilesnapshot} +## toMatchFileSnapshot0.30.0+ {#tomatchfilesnapshot} - **类型:** `(filepath: string, message?: string) => Promise` @@ -1016,126 +989,6 @@ test('spy function returns bananas on second call', () => { }) ``` -## toHaveResolved - -- **类型**: `() => Awaitable` - - -此断言检查函数是否至少成功解析过一次值(即未reject)。需要将 spy 函数传递给 `expect`。 - -如果函数返回了一个promise,但尚未resolved,则将会失败。 - -```ts twoslash -// @filename: db/apples.js -/** @type {any} */ -const db = {} -export default db -// @filename: test.ts -// ---cut--- -import { expect, test, vi } from 'vitest' -import db from './db/apples.js' - -async function getApplesPrice(amount: number) { - return amount * await db.get('price') -} - -test('spy function resolved a value', async () => { - const getPriceSpy = vi.fn(getApplesPrice) - - const price = await getPriceSpy(10) - - expect(price).toBe(100) - expect(getPriceSpy).toHaveResolved() -}) -``` - -## toHaveResolvedTimes - -- **类型**: `(amount: number) => Awaitable` - -此断言检查函数是否已成功解析值精确次数(即未reject)。需要将 spy 函数传递给`expect`。 - -这只会计算已resolved的promises。如果函数返回了一个promise,但尚未resolved,则不会计算在内。 - - -```ts twoslash -import { expect, test, vi } from 'vitest' - -test('spy function resolved a value two times', async () => { - const sell = vi.fn((product: string) => Promise.resolve({ product })) - - await sell('apples') - await sell('bananas') - - expect(sell).toHaveResolvedTimes(2) -}) -``` - -## toHaveResolvedWith - -- **类型**: `(returnValue: any) => Awaitable` - - - -您可以调用此断言来检查函数是否至少成功解析过一次某个值。需要将 spy 函数传递给`expect`。 - -如果函数返回了一个promise,但尚未resolved,则将会失败。 - - -```ts twoslash -import { expect, test, vi } from 'vitest' - -test('spy function resolved a product', async () => { - const sell = vi.fn((product: string) => Promise.resolve({ product })) - - await sell('apples') - - expect(sell).toHaveResolvedWith({ product: 'apples' }) -}) -``` - -## toHaveLastResolvedWith - -- **Type**: `(returnValue: any) => Awaitable` - -您可以调用此断言来检查函数在上次调用时是否已成功解析某个值。需要将 spy 函数传递给`expect`。 - -如果函数返回了一个promise,但尚未resolved,则将会失败。 - -```ts twoslash -import { expect, test, vi } from 'vitest' - -test('spy function resolves bananas on a last call', async () => { - const sell = vi.fn((product: string) => Promise.resolve({ product })) - - await sell('apples') - await sell('bananas') - - expect(sell).toHaveLastResolvedWith({ product: 'bananas' }) -}) -``` - -## toHaveNthResolvedWith - -- **Type**: `(time: number, returnValue: any) => Awaitable` - -您可以调用此断言来检查函数在上次调用时是否已成功解析某个值。需要将 spy 函数传递给`expect`。 - -如果函数返回了一个promise,但尚未resolved,则将会失败。 - -```ts twoslash -import { expect, test, vi } from 'vitest' - -test('spy function returns bananas on second call', async () => { - const sell = vi.fn((product: string) => Promise.resolve({ product })) - - await sell('apples') - await sell('bananas') - - expect(sell).toHaveNthResolvedWith(2, { product: 'bananas' }) -}) -``` - ## toSatisfy - **类型:** `(predicate: (value: any) => boolean) => Awaitable` @@ -1175,7 +1028,7 @@ async function buyApples() { } test('buyApples returns new stock id', async () => { - // toEqual returns a promise now, so you HAVE to await it + // toEqual 现在返回一个 Promise,调用时需添加 await await expect(buyApples()).resolves.toEqual({ id: 1 }) // jest API await expect(buyApples()).resolves.to.equal({ id: 1 }) // chai API }) @@ -1205,7 +1058,7 @@ async function buyApples(id) { } test('buyApples throws an error when no id provided', async () => { - // toThrow returns a promise now, so you HAVE to await it + // toThrow 现在返回一个 Promise,调用时需添加 await await expect(buyApples()).rejects.toThrow('no id') }) ``` @@ -1264,7 +1117,7 @@ function onSelect(cb) { cbs.push(cb) } -// after selecting from db, we call all callbacks +// 在数据库查询操作之后,立即执行全部回调函数 function select(id) { return db.select({ id }).then((data) => { return Promise.all(cbs.map(cb => cb(data))) @@ -1274,11 +1127,11 @@ function select(id) { test('callback was called', async () => { expect.hasAssertions() onSelect((data) => { - // should be called on select + // 必须在 select 操作时调用 expect(data).toBeTruthy() }) - // if not awaited, test will fail - // if you don't have expect.hasAssertions(), test will pass + // 若没有添加 await 测试将会失败 + // 若缺少 expect.hasAssertions(), 测试仍会通过 await select(3) }) ``` @@ -1319,7 +1172,7 @@ test.each(errorDirs)('build fails with "%s"', async (dir) => { expect(err.message).toBe(`${dir}/src does not exist`) break default: - // to exhaust all error tests + // 必须覆盖所有错误测试场景 expect.unreachable('All error test must be handled') break } @@ -1357,7 +1210,7 @@ test('"id" is a number', () => { }) ``` -## expect.closeTo {#expect-closeto} +## expect.closeTo 1.0.0 {#expect-closeto} - **类型:** `(expected: any, precision?: number) => any` @@ -1390,7 +1243,11 @@ import { expect, test } from 'vitest' test('basket includes fuji', () => { const basket = { - varieties: ['Empire', 'Fuji', 'Gala'], + varieties: [ + 'Empire', + 'Fuji', + 'Gala' + ], count: 3, } expect(basket).toEqual({ @@ -1486,7 +1343,7 @@ test('variety ends with "re"', () => { - **类型:** `(plugin: PrettyFormatPlugin) => void` -这个方法添加了在创建快照时调用的自定义序列化程序。这是一个高级功能 - 如果想了解更多,请阅读有关[自定义序列化程序的指南](/guide/snapshot#custom-serializer)。 +这个方法添加了在创建快照时调用的自定义序列化程序。这是一个高级功能 - 如果想了解更多,请阅读有关 [自定义序列化程序的指南](/guide/snapshot#custom-serializer)。 如果需要添加自定义序列化程序,应该在 [`setupFiles`](/config/#setupfiles) 中调用此方法。这将影响每个快照。 @@ -1549,7 +1406,7 @@ declare module 'vitest' { 如果想了解更多信息,请查看 [扩展断言 (Matchers) 指南](/guide/extending-matchers)。 ::: -## expect.addEqualityTesters {#expect-addequalitytesters} +## expect.addEqualityTesters 1.2.0 {#expect-addequalitytesters} - **类型:** `(tester: Array) => void` diff --git a/api/index.md b/api/index.md index 6cbb27ea..266d84cd 100644 --- a/api/index.md +++ b/api/index.md @@ -2,7 +2,7 @@ outline: deep --- -# Test API 索引 +# Test API 索引 {#test-api-reference} 下面的类型签名中使用了以下类型: @@ -32,14 +32,23 @@ interface TestOptions { } ``` +自 Vitest 1.3.0 版本起弃用将配置选项作为最后一位参数的写法。在 2.0.0 版本完全移除该语法前,你会看到弃用警告。如需传递配置选项,请使用测试函数的第二个参数: + +```js +import { test } from 'vitest' + +test('flaky test', () => {}, { retry: 3 }) // [!code --] +test('flaky test', { retry: 3 }, () => {}) // [!code ++] +``` + 当测试函数返回承诺时,运行程序将等待它被解析以收集异步期望。 如果承诺被拒绝,测试就会失败。 ::: tip -在 Jest 中,`TestFunction` 也可以是 `(done: DoneCallback) => void` 类型。如果使用这种形式,测试将在调用 `done` 之前不会结束。也可以使用 `async` 函数来实现相同的效果,请参阅[迁移指南中的回调完成部分](/guide/migration#回调完成)。 +在 Jest 中,`TestFunction` 也可以是 `(done: DoneCallback) => void` 类型。如果使用这种形式,测试将在调用 `done` 之前不会结束。也可以使用 `async` 函数来实现相同的效果,请参阅 [迁移指南中的回调完成部分](/guide/migration#done-callback)。 ::: -大多数选项都支持点语法和对象语法,允许您使用您喜欢的任何样式。 +自 Vitest 1.3.0 起,大多数选项都支持点语法和对象语法,允许您使用您喜欢的任何样式。 :::code-group @@ -51,7 +60,7 @@ test.skip('skipped test', () => { }) ``` -```ts [object-syntax] twoslash +```ts [object-syntax 1.3.0] twoslash import { test } from 'vitest' test('skipped test', { skip: true }, () => { @@ -78,12 +87,12 @@ test('should work as expected', () => { }) ``` -### test.extend {#test-extended} +### test.extend 0.32.3 {#test-extended} - **类型:** `>(fixtures: Fixtures): TestAPI` - **别名:** `it.extend` -使用 `test.extend` 来使用自定义的 fixtures 扩展测试上下文。这将返回一个新的 `test`,它也是可扩展的,因此可以根据需要扩展更多的 fixtures 或覆盖现有的 fixtures。有关更多信息,请参阅[扩展测试上下文](/guide/test-context.html#test-extend)。 +使用 `test.extend` 来使用自定义的 fixtures 扩展测试上下文。这将返回一个新的 `test`,它也是可扩展的,因此可以根据需要扩展更多的 fixtures 或覆盖现有的 fixtures。有关更多信息,请参阅 [扩展测试上下文](/guide/test-context.html#test-extend)。 ```ts import { expect, test } from 'vitest' @@ -119,7 +128,7 @@ myTest('add item', ({ todos }) => { import { assert, test } from 'vitest' test.skip('skipped test', () => { - // Test skipped, no error + // 测试被跳过,没有错误 assert.equal(Math.sqrt(4), 3) }) ``` @@ -131,7 +140,7 @@ import { assert, test } from 'vitest' test('skipped test', (context) => { context.skip() - // Test skipped, no error + // 测试被跳过,没有错误 assert.equal(Math.sqrt(4), 3) }) ``` @@ -149,12 +158,12 @@ import { assert, test } from 'vitest' const isDev = process.env.NODE_ENV === 'development' test.skipIf(isDev)('prod only test', () => { - // this test only runs in production + // 此测试仅在生产环境中运行 }) ``` ::: warning -在将 Vitest 用作[类型检查器](/guide/testing-types)时,不能使用此语法。 +在将 Vitest 用作 [类型检查器](/guide/testing-types) 时,不能使用此语法。 ::: ### test.runIf @@ -170,12 +179,12 @@ import { assert, test } from 'vitest' const isDev = process.env.NODE_ENV === 'development' test.runIf(isDev)('dev only test', () => { - // this test only runs in development + // 此测试仅在开发环境中运行 }) ``` ::: warning -在将 Vitest 用作[类型检查器](/guide/testing-types)时,不能使用此语法。 +在将 Vitest 用作 [类型检查器](/guide/testing-types) 时,不能使用此语法。 ::: ### test.only @@ -191,7 +200,7 @@ test.runIf(isDev)('dev only test', () => { import { assert, test } from 'vitest' test.only('test', () => { - // Only this test (and others marked with only) are run + // 只有此测试(以及其他标记为 `only` 的测试)会被运行。 assert.equal(Math.sqrt(4), 2) }) ``` @@ -214,17 +223,11 @@ test.only('test', () => { ```ts twoslash import { describe, test } from 'vitest' -// The two tests marked with concurrent will be run in parallel +// 标记为 `concurrent` 的两个测试将并行运行。 describe('suite', () => { - test('serial test', async () => { - /* ... */ - }) - test.concurrent('concurrent test 1', async () => { - /* ... */ - }) - test.concurrent('concurrent test 2', async () => { - /* ... */ - }) + test('serial test', async () => { /* ... */ }) + test.concurrent('concurrent test 1', async () => { /* ... */ }) + test.concurrent('concurrent test 2', async () => { /* ... */ }) }) ``` @@ -237,7 +240,7 @@ test.only.concurrent(/* ... */) // or test.concurrent.only(/* ... */) test.todo.concurrent(/* ... */) // or test.concurrent.todo(/* ... */) ``` -运行并发测试时,快照和断言必须使用本地[测试上下文](/guide/test-context.md)中的 `expect`,以确保检测到正确的测试。 +运行并发测试时,快照和断言必须使用本地 [测试上下文](/guide/test-context) 中的 `expect`,以确保检测到正确的测试。 ```ts test.concurrent('test 1', async ({ expect }) => { @@ -249,7 +252,7 @@ test.concurrent('test 2', async ({ expect }) => { ``` ::: warning -在将 Vitest 用作[类型检查器](/guide/testing-types)时,不能使用此语法。 +在将 Vitest 用作 [类型检查器](/guide/testing-types) 时,不能使用此语法。 ::: ### test.sequential @@ -262,36 +265,20 @@ test.concurrent('test 2', async ({ expect }) => { import { describe, test } from 'vitest' // ---cut--- -// with config option { sequence: { concurrent: true } } -test('concurrent test 1', async () => { - /* ... */ -}) -test('concurrent test 2', async () => { - /* ... */ -}) +// 使用配置选项 `{ sequence: { concurrent: true } }` +test('concurrent test 1', async () => { /* ... */ }) +test('concurrent test 2', async () => { /* ... */ }) -test.sequential('sequential test 1', async () => { - /* ... */ -}) -test.sequential('sequential test 2', async () => { - /* ... */ -}) +test.sequential('sequential test 1', async () => { /* ... */ }) +test.sequential('sequential test 2', async () => { /* ... */ }) -// within concurrent suite +// 在并发套件中 describe.concurrent('suite', () => { - test('concurrent test 1', async () => { - /* ... */ - }) - test('concurrent test 2', async () => { - /* ... */ - }) + test('concurrent test 1', async () => { /* ... */ }) + test('concurrent test 2', async () => { /* ... */ }) - test.sequential('sequential test 1', async () => { - /* ... */ - }) - test.sequential('sequential test 2', async () => { - /* ... */ - }) + test.sequential('sequential test 1', async () => { /* ... */ }) + test.sequential('sequential test 2', async () => { /* ... */ }) }) ``` @@ -303,7 +290,7 @@ describe.concurrent('suite', () => { 使用 `test.todo` 来存根测试,以便稍后实施。测试报告中将显示一个条目,以便知道还有多少测试需要执行。 ```ts -// An entry will be shown in the report for this test +// 此测试将在报告中显示一个条目 test.todo('unimplemented test') ``` @@ -326,7 +313,7 @@ test.fails('fail test', async () => { ``` ::: warning -在将 Vitest 用作[类型检查器](/guide/testing-types)时,不能使用此语法。 +在将 Vitest 用作 [类型检查器](/guide/testing-types) 时,不能使用此语法。 ::: ### test.each @@ -334,10 +321,6 @@ test.fails('fail test', async () => { - **类型:** `(cases: ReadonlyArray, ...args: any[]) => void` - **别名:** `it.each` -::: tip - `test.each` 是为了与 Jest 兼容而提供的,Vitest 还提供了 [`test.for`](#test-for),并集成了 [`TestContext`](/guide/test-context)。 -::: - 当需要使用不同变量运行同一测试时,请使用 `test.each`。 我们可以按照测试功能参数的顺序,在测试名称中注入带有 [printf formatting](https://nodejs.org/api/util.html#util_util_format_format_args) 的参数。 @@ -362,7 +345,7 @@ test.each([ expect(a + b).toBe(expected) }) -// this will return +// 这将返回 // ✓ add(1, 1) -> 2 // ✓ add(1, 2) -> 3 // ✓ add(2, 1) -> 3 @@ -379,7 +362,7 @@ test.each([ expect(a + b).toBe(expected) }) -// this will return +// 这将返回 // ✓ add(1, 1) -> 2 // ✓ add(1, 2) -> 3 // ✓ add(2, 1) -> 3 @@ -400,7 +383,7 @@ test.each` expect(a.val + b).toBe(expected) }) -// this will return +// 这将返回 // ✓ add(1, b) -> 1b // ✓ add(2, b) -> 2b // ✓ add(3, b) -> 3b @@ -425,55 +408,16 @@ test.each` }) ``` +如果你想访问 `TestContext` ,请在单个测试中使用 `describe.each` 。 + ::: tip Vitest 使用 chai `format` 方法处理 `$values`。如果数值太短,可以在配置文件中增加 [chaiConfig.truncateThreshold](/config/#chaiconfig-truncatethreshold)。 ::: ::: warning -在将 Vitest 用作[类型检查器](/guide/testing-types)时,不能使用此语法。 +在将 Vitest 用作 [类型检查器](/guide/testing-types) 时,不能使用此语法。 ::: -### test.for - -- **Alias:** `it.for` - -作为 `test.each` 的替代,提供 [`TestContext`](/guide/test-context)。 - -与 `test.each` 的区别在于如何在参数中提供数组情况。 -其他非数组情况(包括模板字符串的使用)完全相同。 - -```ts -// `each` spreads array case -test.each([ - [1, 1, 2], - [1, 2, 3], - [2, 1, 3], -])('add(%i, %i) -> %i', (a, b, expected) => { // [!code --] - expect(a + b).toBe(expected) -}) - -// `for` doesn't spread array case -test.for([ - [1, 1, 2], - [1, 2, 3], - [2, 1, 3], -])('add(%i, %i) -> %i', ([a, b, expected]) => { // [!code ++] - expect(a + b).toBe(expected) -}) -``` - -第二个参数是 [`TestContext`](/guide/test-context),可用于并发快照等 - -```ts -test.concurrent.for([ - [1, 1], - [1, 2], - [2, 1], -])('add(%i, %i)', ([a, b], { expect }) => { - expect(a + b).matchSnapshot() -}) -``` - ## bench - **类型:** `(name: string | Function, fn: BenchFunction, options?: BenchOptions) => void` @@ -549,7 +493,7 @@ export interface Options { - **类型:** `(name: string | Function, fn: BenchFunction, options?: BenchOptions) => void` -可以使用 "bench.skip "语法跳过运行某些基准。 +可以使用 `bench.skip` 语法跳过运行某些基准。 ```ts twoslash import { bench } from 'vitest' @@ -597,7 +541,7 @@ bench.todo('unimplemented test') ```ts twoslash // basic.spec.ts -// organizing tests +// 组织测试 import { describe, expect, test } from 'vitest' @@ -623,7 +567,7 @@ describe('person', () => { ```ts twoslash // basic.bench.ts -// organizing benchmarks +// 组织基准测试 import { bench, describe } from 'vitest' @@ -650,9 +594,8 @@ describe('sort', () => { import { describe, expect, test } from 'vitest' function numberToCurrency(value: number | string) { - if (typeof value !== 'number') { - throw new TypeError('Value must be a number') - } + if (typeof value !== 'number') + throw new Error('Value must be a number') return value .toFixed(2) @@ -686,7 +629,7 @@ import { assert, describe, test } from 'vitest' describe.skip('skipped suite', () => { test('sqrt', () => { - // Suite skipped, no error + // 跳过测试套件,不报错 assert.equal(Math.sqrt(4), 3) }) }) @@ -704,12 +647,12 @@ import { describe, test } from 'vitest' const isDev = process.env.NODE_ENV === 'development' describe.skipIf(isDev)('prod only test suite', () => { - // this test suite only runs in production + // 此测试套件仅在生产环境中运行 }) ``` ::: warning -将 Vitest 用作[类型检查器](/guide/testing-types)时,你不能使用此语法。 +将 Vitest 用作 [类型检查器](/guide/testing-types) 时,你不能使用此语法。 ::: ### describe.runIf @@ -724,12 +667,12 @@ import { assert, describe, test } from 'vitest' const isDev = process.env.NODE_ENV === 'development' describe.runIf(isDev)('dev only test suite', () => { - // this test suite only runs in development + // 此测试套件仅在开发环境中运行 }) ``` ::: warning -将 Vitest 用作[类型检查器](/guide/testing-types)时,你不能使用此语法。 +将 Vitest 用作 [类型检查器](/guide/testing-types) 时,你不能使用此语法。 ::: ### describe.only @@ -741,7 +684,7 @@ describe.runIf(isDev)('dev only test suite', () => { ```ts twoslash import { assert, describe, test } from 'vitest' // ---cut--- -// Only this suite (and others marked with only) are run +// 只有此测试套件(以及其他标记为 `only` 的测试套件)会被运行。 describe.only('suite', () => { test('sqrt', () => { assert.equal(Math.sqrt(4), 3) @@ -749,12 +692,10 @@ describe.only('suite', () => { }) describe('other suite', () => { - // ... will be skipped + // 将被跳过... }) ``` -In order to do that run `vitest` with specific file containing the tests in question. - 有时,只运行某个文件中的测试套件,而忽略整个测试套件中的所有其他测试是非常有用的,因为这些测试会污染输出。 要做到这一点,请在包含相关测试的特定文件中运行 `vitest`。 @@ -772,22 +713,14 @@ In order to do that run `vitest` with specific file containing the tests in ques ```ts twoslash import { describe, test } from 'vitest' // ---cut--- -// All suites and tests within this suite will be run in parallel +// 此测试套件中的所有测试套件和测试将并行运行。 describe.concurrent('suite', () => { - test('concurrent test 1', async () => { - /* ... */ - }) + test('concurrent test 1', async () => { /* ... */ }) describe('concurrent suite 2', async () => { - test('concurrent test inner 1', async () => { - /* ... */ - }) - test('concurrent test inner 2', async () => { - /* ... */ - }) - }) - test.concurrent('concurrent test 3', async () => { - /* ... */ + test('concurrent test inner 1', async () => { /* ... */ }) + test('concurrent test inner 2', async () => { /* ... */ }) }) + test.concurrent('concurrent test 3', async () => { /* ... */ }) }) ``` @@ -800,7 +733,7 @@ describe.only.concurrent(/* ... */) // or describe.concurrent.only(/* ... */) describe.todo.concurrent(/* ... */) // or describe.concurrent.todo(/* ... */) ``` -运行并发测试时,快照和断言必须使用本地[测试上下文](/guide/test-context.md)中的 `expect` ,以确保检测到正确的测试。 +运行并发测试时,快照和断言必须使用本地 [测试上下文](/guide/test-context) 中的 `expect` ,以确保检测到正确的测试。 ```ts describe.concurrent('suite', () => { @@ -814,7 +747,7 @@ describe.concurrent('suite', () => { ``` ::: warning -在将 Vitest 用作[类型检查器](/guide/testing-types)时,不能使用此语法。 +在将 Vitest 用作 [类型检查器](/guide/testing-types) 时,不能使用此语法。 ::: ### describe.sequential @@ -827,20 +760,12 @@ describe.concurrent('suite', () => { import { describe, test } from 'vitest' // ---cut--- describe.concurrent('suite', () => { - test('concurrent test 1', async () => { - /* ... */ - }) - test('concurrent test 2', async () => { - /* ... */ - }) + test('concurrent test 1', async () => { /* ... */ }) + test('concurrent test 2', async () => { /* ... */ }) describe.sequential('', () => { - test('sequential test 1', async () => { - /* ... */ - }) - test('sequential test 2', async () => { - /* ... */ - }) + test('sequential test 1', async () => { /* ... */ }) + test('sequential test 2', async () => { /* ... */ }) }) }) ``` @@ -855,23 +780,17 @@ Vitest 通过 CLI 标志 [`--sequence.shuffle`](/guide/cli) 或配置选项 [`se import { describe, test } from 'vitest' // ---cut--- describe.shuffle('suite', () => { - test('random test 1', async () => { - /* ... */ - }) - test('random test 2', async () => { - /* ... */ - }) - test('random test 3', async () => { - /* ... */ - }) + test('random test 1', async () => { /* ... */ }) + test('random test 2', async () => { /* ... */ }) + test('random test 3', async () => { /* ... */ }) }) -// order depends on sequence.seed option in config (Date.now() by default) +// 顺序取决于配置中的 `sequence.seed` 选项(默认为 `Date.now()`) ``` `.skip`、`.only`和`.todo`适用于随机测试套件。 ::: warning -在将 Vitest 用作[类型检查器](/guide/testing-types)时,不能使用此语法。 +在将 Vitest 用作 [类型检查器](/guide/testing-types) 时,不能使用此语法。 ::: ### describe.todo @@ -881,7 +800,7 @@ describe.shuffle('suite', () => { 使用 `describe.todo` 来暂存待以后实施的套件。测试报告中会显示一个条目,这样就能知道还有多少测试需要执行。 ```ts -// An entry will be shown in the report for this suite +// 此测试套件将在报告中显示一个条目 describe.todo('unimplemented suite') ``` @@ -936,7 +855,7 @@ describe.each` ``` ::: warning -在将 Vitest 用作[类型检查器](/guide/testing-types)时,不能使用此语法。 +在将 Vitest 用作 [类型检查器](/guide/testing-types) 时,不能使用此语法。 ::: ## Setup and Teardown @@ -957,7 +876,7 @@ describe.each` import { beforeEach } from 'vitest' beforeEach(async () => { - // Clear mocks and add some testing data after before each test run + // 每次执行测试前,先重置所有 mock,然后准备好需要用到的测试数据 await stopMocking() await addUser({ name: 'John' }) }) @@ -965,16 +884,16 @@ beforeEach(async () => { 这里, `beforeEach` 确保每次测试都会添加用户。 -`beforeEach` 还接受一个可选的清理函数(相当于 `afterEach`)。 +自 Vitest v0.10.0 起,`beforeEach` 还接受一个可选的清理函数(相当于 `afterEach`)。 ```ts import { beforeEach } from 'vitest' beforeEach(async () => { - // called once before each test run + // 在每个测试运行之前调用一次 await prepareSomething() - // clean up function, called once after each test run + // 清理函数,在每个测试运行之后调用一次 return async () => { await resetSomething() } @@ -994,14 +913,14 @@ beforeEach(async () => { import { afterEach } from 'vitest' afterEach(async () => { - await clearTestingData() // clear testing data after each test run + await clearTestingData() // 在每个测试运行之后清除测试数据 }) ``` 在这里,`afterEach` 可确保在每次测试运行后清除测试数据。 ::: tip -Vitest 在 1.3.0 新增 [`onTestFinished`](#ontestfinished)。你可以在测试执行过程中调用它,以便在测试运行结束后清理任何状态。 +Vitest 在 1.3.0 起,新增 [`onTestFinished`](#ontestfinished)。你可以在测试执行过程中调用它,以便在测试运行结束后清理任何状态。 ::: ### beforeAll @@ -1017,22 +936,22 @@ Vitest 在 1.3.0 新增 [`onTestFinished`](#ontestfinished)。你可以在测试 import { beforeAll } from 'vitest' beforeAll(async () => { - await startMocking() // called once before all tests run + await startMocking() // 在所有测试运行之前调用一次 }) ``` 这里的 `beforeAll` 确保在测试运行前设置好模拟数据。 -`beforeAll` 还接受一个可选的清理函数(相当于 `afterAll`)。 +自 Vitest v0.10.0 起,`beforeAll` 还接受一个可选的清理函数(相当于 `afterAll`)。 ```ts import { beforeAll } from 'vitest' beforeAll(async () => { - // called once before all tests run + // 在所有测试运行之前调用一次 await startMocking() - // clean up function, called once after all tests run + // 清理函数,在所有测试运行之后调用一次 return async () => { await stopMocking() } @@ -1052,7 +971,7 @@ beforeAll(async () => { import { afterAll } from 'vitest' afterAll(async () => { - await stopMocking() // this method is called after all tests run + await stopMocking() // 此方法在所有测试运行之后被调用 }) ``` @@ -1066,7 +985,7 @@ Vitest 提供了一些 hooks,你可以在 _测试执行期间_ 调用这些钩 如果在测试体之外调用这些 hooks ,则会出错。 ::: -### onTestFinished {#ontestfinished} +### onTestFinished 1.3.0 {#ontestfinished} 这个 hook 总是在测试运行结束后调用。它在 `afterEach` 之后被调用,因为它们会影响测试结果。它接收一个包含当前测试结果的 `TaskResult` 。 diff --git a/api/mock.md b/api/mock.md index 6d63955b..6c0d9993 100644 --- a/api/mock.md +++ b/api/mock.md @@ -42,7 +42,7 @@ getApplesSpy.mock.calls.length === 1 清除每次调用的所有信息。调用该方法后,`.mock` 上的所有属性都将返回空状态。此方法不会重置实现。如果需要在不同断言之间清理 mock,该方法非常有用。 -如果我们希望在每次测试前自动调用该方法,可以在配置中启用 [`clearMocks``](/config/#clearmocks)设置。 +如果我们希望在每次测试前自动调用该方法,可以在配置中启用 [`clearMocks`](/config/#clearmocks) 设置。 ## mockName @@ -60,7 +60,7 @@ getApplesSpy.mock.calls.length === 1 import { vi } from 'vitest' // ---cut--- const mockFn = vi.fn().mockImplementation((apples: number) => apples + 1) -// or: vi.fn(apples => apples + 1); +// 或:vi.fn(apples => apples + 1); const NelliesBucket = mockFn(0) const BobsBucket = mockFn(1) @@ -116,12 +116,9 @@ import { vi } from 'vitest' // ---cut--- const myMockFn = vi.fn(() => 'original') -myMockFn.withImplementation( - () => 'temp', - () => { - myMockFn() // 'temp' - } -) +myMockFn.withImplementation(() => 'temp', () => { + myMockFn() // 'temp' +}) myMockFn() // 'original' ``` @@ -132,7 +129,7 @@ myMockFn() // 'original' test('async callback', () => { const myMockFn = vi.fn(() => 'original') - // We await this call since the callback is async + // 由于回调是异步的,我们要等待这个调用 await myMockFn.withImplementation( () => 'temp', async () => { @@ -157,7 +154,7 @@ import { vi } from 'vitest' // ---cut--- const asyncMock = vi.fn().mockRejectedValue(new Error('Async error')) -await asyncMock() // throws "Async error" +await asyncMock() // 抛出 "Async error" ``` ## mockRejectedValueOnce @@ -174,8 +171,8 @@ const asyncMock = vi .mockResolvedValueOnce('first call') .mockRejectedValueOnce(new Error('Async error')) -await asyncMock() // first call -await asyncMock() // throws "Async error" +await asyncMock() // 'first call' +await asyncMock() // 抛出 "Async error" ``` ## mockReset @@ -290,8 +287,7 @@ const fn = vi.fn() fn('arg1', 'arg2') fn('arg3') -fn.mock.calls -=== [ +fn.mock.calls === [ ['arg1', 'arg2'], // first call ['arg3'], // second call ] @@ -308,7 +304,6 @@ fn.mock.calls - `'return'` - 函数返回时没有抛出。 - `'throw'` - 函数抛出了一个值。 - `value` 属性包含返回值或抛出的错误。如果函数返回一个 `Promise`,那么即使Promise rejected,`result` 也将始终为 `'return'`。 ```js @@ -319,21 +314,20 @@ const fn = vi throw new Error('thrown error') }) -const result = fn() // returned 'result' +const result = fn() // 返回 'result' try { - fn() // threw Error + fn() // 抛出错误 } catch {} -fn.mock.results -=== [ - // first result +fn.mock.results === [ + // 首次调用结果 { type: 'return', value: 'result', }, - // last result + // 最后调用结果 { type: 'throw', value: Error, @@ -341,29 +335,6 @@ fn.mock.results ] ``` -## mock.settledResults - -包含函数中`resolved` 或 `rejected` 的所有值的数组。 - -如果函数从未`resolved` 或 `rejected` ,则此数组将为空。 - -```js -const fn = vi.fn().mockResolvedValueOnce('result') - -const result = fn() - -fn.mock.settledResults === [] - -await result - -fn.mock.settledResults === [ - { - type: 'fulfilled', - value: 'result', - }, -] -``` - ## mock.invocationCallOrder 此属性返回 mock 函数的执行顺序。它是一个数字数组,由所有定义的 mock 共享。 @@ -380,21 +351,6 @@ fn1.mock.invocationCallOrder === [1, 3] fn2.mock.invocationCallOrder === [2] ``` -## mock.contexts - -此属性是每次调用模拟函数时使用的 `this` 值的数组。 - -```js -const fn = vi.fn() -const context = {} - -fn.apply(context) -fn.call(context) - -fn.mock.contexts[0] === context -fn.mock.contexts[1] === context -``` - ## mock.instances 此属性是一个数组,其中包含使用 `new` 关键字调用模拟时创建的所有实例。请注意,这是函数的实际上下文(`this`),而不是返回值。 diff --git a/api/vi.md b/api/vi.md index db5ed558..8b62fa14 100644 --- a/api/vi.md +++ b/api/vi.md @@ -10,14 +10,13 @@ Vitest 通过其 `vi` 辅助工具提供实用功能来帮助你。可以全局 import { vi } from 'vitest' ``` -## Mock Modules +## 模拟模块 {#mock-modules} 本节介绍在 [模拟模块](/guide/mocking#modules) 时可以使用的 API。请注意,Vitest 不支持模拟使用 `require()` 导入的模块。 ### vi.mock - **类型**: `(path: string, factory?: (importOriginal: () => unknown) => unknown) => void` -- **类型**: `(path: Promise, factory?: (importOriginal: () => T) => unknown) => void` 用另一个模块替换提供的 `path` 中的所有导入模块。我们可以在路径内使用配置的 Vite 别名。对 `vi.mock` 的调用是悬挂式的,因此在何处调用并不重要。它总是在所有导入之前执行。如果需要在其作用域之外引用某些变量,可以在 [`vi.hoisted`](/api/vi#vi-hoisted)中定义它们,并在 `vi.mock` 中引用它们。 @@ -29,6 +28,9 @@ import { vi } from 'vitest' Vitest 不会模拟 [setup file](/config/#setupfiles) 中导入的模块,因为这些模块在运行测试文件时已被缓存。我们可以在 [`vi.hoisted`](#vi-hoisted) 中调用 [`vi.resetModules()`](#vi-resetmodules) ,在运行测试文件前清除所有模块缓存。 ::: +::: warning +[浏览器模式](/guide/browser) 目前不支持模拟模块。可以在这个 [issue](https://github.com/vitest-dev/vitest/issues/3046) 中持续关注此功能。 +::: 如果定义了 `factory`,所有导入都将返回其结果。Vitest 只调用一次 factory,并缓存所有后续导入的结果,直到 [`vi.unmock`](#vii-unmock) 或 [`vi.doUnmock`](#vii-dounmock) 被调用。 @@ -62,21 +64,6 @@ vi.mock('./path/to/module.js', async (importOriginal) => { }) ``` -Vitest 支持模块 promise,而不是 `vi.mock` 方法中的字符串,以获得更好的 IDE 支持(当文件被移动时,路径将被更新,`importOriginal` 也会自动继承类型)。 - -```ts -vi.mock(import('./path/to/module.js'), async (importOriginal) => { - const mod = await importOriginal() // type is inferred - return { - ...mod, - // replace some exports - namedExport: vi.fn(), - } -}) -``` - -在此钩子下,Vitest 仍然对字符串而不是模块对象进行操作。 - ::: warning `vi.mock` 被提升(换句话说,_移动_)到**文件的顶部**。这意味着无论何时写入它(无论是在 `beforeEach` 还是 `test`),它都会在此之前被调用。 @@ -117,7 +104,7 @@ vi.mock('./path/to/module.js', () => { return { default: { myDefaultKey: vi.fn() }, namedExport: vi.fn(), - // etc... + // 更多 ... } }) ``` @@ -141,13 +128,13 @@ vi.mock('./path/to/module.js', () => { 如果在没有提供工厂的测试文件中调用 `vi.mock` ,它会在 `__mocks__` 文件夹中找到一个文件作为模块使用: ```ts +// axios 是 `__mocks__/axios.js` 默认导出项 +import axios from 'axios' + // increment.test.js import { vi } from 'vitest' -// axios is a default export from `__mocks__/axios.js` -import axios from 'axios' - -// increment is a named export from `src/__mocks__/increment.js` +// increment 是 `src/__mocks__/increment.js` 具名导出 import { increment } from '../increment.js' vi.mock('axios') @@ -161,20 +148,19 @@ axios.get(`/apples/${increment(1)}`) 请注意,如果不调用 `vi.mock` ,模块**不会**被自动模拟。要复制 Jest 的自动锁定行为,可以在 [`setupFiles`](/config/#setupfiles) 中为每个所需的模块调用 `vi.mock` 。 ::: -如果没有提供 `__mocks__` 文件夹或工厂,Vitest 将导入原始模块并自动模拟其所有输出。有关应用的规则,请参阅[模块](/guide/mocking#%E6%A8%A1%E5%9D%97)。 +如果没有提供 `__mocks__` 文件夹或工厂,Vitest 将导入原始模块并自动模拟其所有输出。有关应用的规则,请参阅 [算法](/guide/mocking#automocking-algorithm)。 ### vi.doMock - **类型**: `(path: string, factory?: (importOriginal: () => unknown) => unknown) => void` -与 [`vi.mock`](#vi-mock) 相同,但它不会被移动到文件顶部,因此我们可以引用全局文件作用域中的变量。模块的下一个 [dynamic import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) 将被模拟。 +与 [`vi.mock`](#vi-mock) 相同,但它不会被移动到文件顶部,因此我们可以引用全局文件作用域中的变量。模块的下一个 [动态导入](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) 将被模拟。 ::: warning 这将不会模拟在调用此调用之前导入的模块。不要忘记,ESM 中的所有静态导入都是 [hoaded](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#hoisting),因此在静态导入前调用此调用不会强制在导入前调用: ```ts -// this will be called _after_ the import statement - +// 这将在 import 语句之后被调用 import { increment } from './increment.js' vi.doMock('./increment.js') ``` @@ -192,21 +178,21 @@ export function increment(number) { import { beforeEach, test } from 'vitest' import { increment } from './increment.js' -// the module is not mocked, because vi.doMock is not called yet +// 该模块尚未被模拟,因为从未调用过 vi.doMock increment(1) === 2 let mockedIncrement = 100 beforeEach(() => { - // you can access variables inside a factory + // 你可以在工厂函数内部访问变量 vi.doMock('./increment.js', () => ({ increment: () => ++mockedIncrement })) }) test('importing the next module imports mocked one', async () => { - // original import WAS NOT MOCKED, because vi.doMock is evaluated AFTER imports + // 原始模块并未模拟,是因为 vi.doMock 是在导入语句之后执行的 expect(increment(1)).toBe(2) const { increment: mockedIncrement } = await import('./increment.js') - // new dynamic import returns mocked module + // 新的动态导入,返回模拟模块 expect(mockedIncrement(1)).toBe(101) expect(mockedIncrement(1)).toBe(102) expect(mockedIncrement(1)).toBe(103) @@ -218,7 +204,7 @@ test('importing the next module imports mocked one', async () => { - **类型**: `(obj: T, deep?: boolean) => MaybeMockedDeep` - **类型**: `(obj: T, options?: { partial?: boolean; deep?: boolean }) => MaybePartiallyMockedDeep` -TypeScript 的类型助手。只返回传入的对象。 +TypeScript 的类型工具函数。只返回传入的对象。 当 `partial` 为 `true` 时,它将期望一个 `Partial` 作为返回值。默认情况下,这只会让 TypeScript 认为第一层的值是模拟的。我们可以将 `{ deep: true }` 作为第二个参数传递给 TypeScript,告诉它整个对象都是模拟的(如果实际上是的话)。 @@ -251,7 +237,7 @@ vi.mock('./example.js', async () => { - **类型**: `(path: string) => Promise>` -导入模块并模拟其所有属性(包括嵌套属性)。遵循与 [`vi.mock`](#vi-mock) 相同的规则。有关应用的规则,请参阅[模块](/guide/mocking#%E6%A8%A1%E5%9D%97)。 +导入模块并模拟其所有属性(包括嵌套属性)。遵循与 [`vi.mock`](#vi-mock) 相同的规则。有关应用的规则,请参阅 [算法](/guide/mocking#automocking-algorithm)。 ### vi.unmock @@ -275,24 +261,24 @@ export function increment(number) { ```ts import { increment } from './increment.js' -// increment is already mocked, because vi.mock is hoisted +// increment 已被模拟, 因为 vi.mock 已导入声明提升 increment(1) === 100 -// this is hoisted, and factory is called before the import on line 1 +// 此处存在函数提升,工厂函数会在第 1 行 import 之前被调用 vi.mock('./increment.js', () => ({ increment: () => 100 })) -// all calls are mocked, and `increment` always returns 100 +// 所有调用均被模拟,并且 `increment` 始终返回 100 increment(1) === 100 increment(30) === 100 -// this is not hoisted, so other import will return unmocked module +// 此处不存在函数提升,因为其它导入操作返回未模拟的模块 vi.doUnmock('./increment.js') -// this STILL returns 100, because `vi.doUnmock` doesn't reevaluate a module +// 此处仍会返回 100,因 `vi.doUnmock` 不会重新评估模块 increment(1) === 100 increment(30) === 100 -// the next import is unmocked, now `increment` is the original function that returns count + 1 +// 下一次导入时解除模拟,此时 `increment` 恢复为原始函数(返回 count + 1) const { increment: unmockedIncrement } = await import('./increment.js') unmockedIncrement(1) === 2 @@ -308,20 +294,20 @@ unmockedIncrement(30) === 31 ```ts import { vi } from 'vitest' -import { data } from './data.js' // Will not get reevaluated beforeEach test +import { data } from './data.js' // 每次测试前不会重新计算 beforeEach(() => { vi.resetModules() }) test('change state', async () => { - const mod = await import('./some/path.js') // Will get reevaluated + const mod = await import('./some/path.js') // 将会被重新计算 mod.changeLocalState('new value') expect(mod.getLocalState()).toBe('new value') }) test('module has old state', async () => { - const mod = await import('./some/path.js') // Will get reevaluated + const mod = await import('./some/path.js') // 将会被重新计算 expect(mod.getLocalState()).toBe('old value') }) ``` @@ -337,7 +323,7 @@ test('module has old state', async () => { ```ts import { expect, test } from 'vitest' -// cannot track import because Promise is not returned +// 无法追踪导入操作,因未返回 Promise function renderComponent() { import('./component.js').then(({ render }) => { render() @@ -357,16 +343,15 @@ test('operations are resolved', async () => { 该方法还将在导入解析后等待下一个 `setTimeout` 跟他挂钩,因此所有同步操作都应在解析时完成。 ::: -## 模拟函数和对象 +## 模拟函数和对象 {#mocking-functions-and-objects} -本节介绍如何使用 [method mock](/api/mock) 替换环境变量和全局变量。 +本节介绍如何使用 [模拟方法](/api/mock) 以及替换环境变量和全局变量。 ### vi.fn - **类型:** `(fn?: Function) => Mock` -创建函数的监视程序,但也可以不创建监视程序。每次调用函数时,它都会存储调用参数、返回值和实例。此外,我们还可以使用 [methods](/api/mock) 操纵它的行为。 -如果没有给出函数,调用 mock 时将返回 `undefined`。 +创建一个函数监视器(可无需初始函数)。每次调用函数时,它都会存储调用参数、返回值和实例。此外,我们还可以使用 [方法](/api/mock) 操纵它的行为。若未提供初始函数,调用 mock 时将返回 `undefined`。 ```ts twoslash import { expect, vi } from 'vitest' @@ -407,7 +392,7 @@ expect(getApples).toHaveNthReturnedWith(2, 5) - **类型:** `(object: T, method: K, accessType?: 'get' | 'set') => MockInstance` -创建与 [`vi.fn()`](/#vi-fn) 类似的对象的方法或 getter/setter 的监听(spy) 。它会返回一个 [mock 函数](/api/mock) 。 +创建与 [`vi.fn()`](/#vi-fn) 类似的对象的方法或 getter/setter 的监听(spy) 。它会返回一个 [模拟函数](/api/mock) 。 ```ts twoslash import { expect, vi } from 'vitest' @@ -427,7 +412,7 @@ expect(spy).toHaveReturnedWith(1) ``` ::: tip -你可以在 [`afterEach`](/api/#aftereach)(或启用 [`test.restoreMocks`](/config/#restoreMocks) )中调用 [`vi.restoreAllMocks`](#vi-restoreallmocks) ,将所有方法还原为原始实现。这将还原原始的 [object descriptor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) ,因此无法更改方法的实现: +你可以在 [`afterEach`](/api/#aftereach)(或启用 [`test.restoreMocks`](/config/#restoreMocks) )中调用 [`vi.restoreAllMocks`](#vi-restoreallmocks) ,将所有方法还原为原始实现。这将还原原始的 [对象描述符](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) ,因此无法更改方法的实现: ```ts const cart = { @@ -440,7 +425,7 @@ console.log(cart.getApples()) // 10 vi.restoreAllMocks() console.log(cart.getApples()) // 42 spy.mockReturnValue(10) -console.log(cart.getApples()) // still 42! +console.log(cart.getApples()) // 仍然为 42! ``` ::: @@ -454,14 +439,14 @@ console.log(cart.getApples()) // still 42! ```ts import { vi } from 'vitest' -// `process.env.NODE_ENV` and `import.meta.env.NODE_ENV` -// are "development" before calling "vi.stubEnv" +// `process.env.NODE_ENV` 和 `import.meta.env.NODE_ENV` +// 在调用 "vi.stubEnv" 之前是 "development" vi.stubEnv('NODE_ENV', 'production') process.env.NODE_ENV === 'production' import.meta.env.NODE_ENV === 'production' -// doesn't change other envs +// 不会改变其他env import.meta.env.MODE === 'development' ``` @@ -483,8 +468,8 @@ import.meta.env.MODE = 'test' ```ts import { vi } from 'vitest' -// `process.env.NODE_ENV` and `import.meta.env.NODE_ENV` -// are "development" before calling stubEnv +// `process.env.NODE_ENV` 和 `import.meta.env.NODE_ENV` +// 在调用 stubEnv 之前是 "development" vi.stubEnv('NODE_ENV', 'production') @@ -498,7 +483,7 @@ import.meta.env.NODE_ENV === 'staging' vi.unstubAllEnvs() -// restores to the value that were stored before the first "stubEnv" call +// 还原到在第一次 “stubEnv” 调用之前存储的值 process.env.NODE_ENV === 'development' import.meta.env.NODE_ENV === 'development' ``` @@ -512,13 +497,13 @@ import.meta.env.NODE_ENV === 'development' ```ts twoslash import { vi } from 'vitest' -// `innerWidth` is "0" before calling stubGlobal +// 在调用 stubGlobal 之前 `innerWidth` 是 "0" vi.stubGlobal('innerWidth', 100) innerWidth === 100 globalThis.innerWidth === 100 -// if you are using jsdom or happy-dom +// 如果你正在使用 jsdom 或 happy-dom window.innerWidth === 100 ``` @@ -527,7 +512,7 @@ window.innerWidth === 100 ```ts globalThis.innerWidth = 100 -// if you are using jsdom or happy-dom +// 如果你正在使用 jsdom 或 happy-dom window.innerWidth = 100 ``` @@ -544,21 +529,21 @@ import { vi } from 'vitest' const Mock = vi.fn() -// IntersectionObserver is "undefined" before calling "stubGlobal" +// 在调用 "stubGlobal" 之前 IntersectionObserver 是 "undefined" vi.stubGlobal('IntersectionObserver', Mock) IntersectionObserver === Mock global.IntersectionObserver === Mock globalThis.IntersectionObserver === Mock -// if you are using jsdom or happy-dom +// 如果你正在使用 jsdom 或 happy-dom window.IntersectionObserver === Mock vi.unstubAllGlobals() globalThis.IntersectionObserver === undefined 'IntersectionObserver' in globalThis === false -// throws ReferenceError, because it's not defined +// 抛出 ReferenceError,因为变量未定义 IntersectionObserver === undefined ``` @@ -580,9 +565,9 @@ setInterval(() => console.log(++i), 50) vi.advanceTimersByTime(150) -// log: 1 -// log: 2 -// log: 3 +// 输出: 1 +// 输出: 2 +// 输出: 3 ``` ### vi.advanceTimersByTimeAsync @@ -599,9 +584,9 @@ setInterval(() => Promise.resolve().then(() => console.log(++i)), 50) await vi.advanceTimersByTimeAsync(150) -// log: 1 -// log: 2 -// log: 3 +// 输出: 1 +// 输出: 2 +// 输出: 3 ``` ### vi.advanceTimersToNextTimer @@ -616,9 +601,9 @@ import { vi } from 'vitest' let i = 0 setInterval(() => console.log(++i), 50) -vi.advanceTimersToNextTimer() // log: 1 - .advanceTimersToNextTimer() // log: 2 - .advanceTimersToNextTimer() // log: 3 +vi.advanceTimersToNextTimer() // 输出: 1 + .advanceTimersToNextTimer() // 输出: 2 + .advanceTimersToNextTimer() // 输出: 3 ``` ### vi.advanceTimersToNextTimerAsync @@ -633,11 +618,11 @@ import { expect, vi } from 'vitest' let i = 0 setInterval(() => Promise.resolve().then(() => console.log(++i)), 50) -await vi.advanceTimersToNextTimerAsync() // log: 1 +await vi.advanceTimersToNextTimerAsync() // 输出: 1 expect(console.log).toHaveBeenCalledWith(1) -await vi.advanceTimersToNextTimerAsync() // log: 2 -await vi.advanceTimersToNextTimerAsync() // log: 3 +await vi.advanceTimersToNextTimerAsync() // 输出: 2 +await vi.advanceTimersToNextTimerAsync() // 输出: 3 ``` ### vi.getTimerCount @@ -688,9 +673,9 @@ const interval = setInterval(() => { vi.runAllTimers() -// log: 1 -// log: 2 -// log: 3 +// 输出: 1 +// 输出: 2 +// 输出: 3 ``` ### vi.runAllTimersAsync @@ -709,7 +694,7 @@ setTimeout(async () => { await vi.runAllTimersAsync() -// log: result +// 输出: result ``` ### vi.runOnlyPendingTimers @@ -726,7 +711,7 @@ setInterval(() => console.log(++i), 50) vi.runOnlyPendingTimers() -// log: 1 +// 输出: 1 ``` ### vi.runOnlyPendingTimersAsync @@ -752,10 +737,10 @@ setTimeout(() => { await vi.runOnlyPendingTimersAsync() -// log: 2 -// log: 3 -// log: 3 -// log: 1 +// 输出: 2 +// 输出: 3 +// 输出: 3 +// 输出: 1 ``` ### vi.setSystemTime @@ -764,7 +749,7 @@ await vi.runOnlyPendingTimersAsync() 如果启用了伪计时器,此方法将模拟用户更改系统时钟(将影响与日期相关的 API,如 `hrtime` 、`performance.now` 或 `new Date()` ),但不会触发任何计时器。如果未启用假定时器,该方法将仅模拟 `Date.*` 调用。 -如果我们需要测试任何依赖于当前日期的内容 -- 例如在代码中调用 [luxon](https://github.com/moment/luxon/) --则非常有用。 +当您需要测试依赖当前日期的逻辑时(例如代码中的 [Luxon](https://github.com/moment/luxon/)() 调用)非常实用。 ```ts twoslash import { expect, vi } from 'vitest' @@ -806,9 +791,9 @@ vi.useRealTimers() 定时器用完后,可以调用此方法将模拟的定时器返回到其原始实现。之前调度的所有计时器都将被丢弃。 -## Miscellaneous +## 杂项 {#miscellaneous} -Vitest 提供的一组有用的辅助函数。 +Vitest 提供的一组有用的工具函数。 ### vi.waitFor {#vi-waitfor} @@ -834,8 +819,8 @@ test('Server started successfully', async () => { console.log('Server started') }, { - timeout: 500, // default is 1000 - interval: 20, // default is 50 + timeout: 500, // 默认为 1000 + interval: 20, // 默认为 50 } ) expect(server.isReady).toBe(true) @@ -851,20 +836,20 @@ import { expect, test, vi } from 'vitest' import { getDOMElementAsync, populateDOMAsync } from './dom.js' test('Element exists in a DOM', async () => { - // start populating DOM + // 开始填充 DOM populateDOMAsync() const element = await vi.waitFor( async () => { - // try to get the element until it exists + // 尝试获取元素直到其存在 const element = (await getDOMElementAsync()) as HTMLElement | null expect(element).toBeTruthy() expect(element.dataset.initialized).toBeTruthy() return element }, { - timeout: 500, // default is 1000 - interval: 20, // default is 50 + timeout: 500, // 默认为 1000 + interval: 20, // 默认为 50 } ) expect(element).toBeInstanceOf(HTMLElement) @@ -886,16 +871,16 @@ import { expect, test, vi } from 'vitest' test('Element render correctly', async () => { const element = await vi.waitUntil(() => document.querySelector('.element'), { - timeout: 500, // default is 1000 - interval: 20, // default is 50 + timeout: 500, // 默认为 1000 + interval: 20, // 默认为 50 }) - // do something with the element + // 对元素进行某些操作 expect(element.querySelector('.element-child')).toBeTruthy() }) ``` -### vi.hoisted {#vi-hoisted} +### vi.hoisted - **类型**: `(factory: () => T) => T` @@ -937,7 +922,7 @@ mockedMethod.mockReturnValue(100) expect(originalMethod()).toBe(100) ``` -请注意,即使我们的环境不支持顶级等待,也可以异步调用此方法: +请注意,即使您的运行环境不支持顶层 await,此方法仍可异步调用: ```ts const promised = await vi.hoisted(async () => { @@ -961,12 +946,12 @@ vi.setConfig({ restoreMocks: true, fakeTimers: { now: new Date(2021, 11, 19), - // supports the whole object + // 支持完整对象 }, maxConcurrency: 10, sequence: { hooks: 'stack', - // supports only "sequence.hooks" + // 仅支持 "sequence.hooks" }, }) ``` diff --git a/config/file.md b/config/file.md index 4b2bbb8a..61acca65 100644 --- a/config/file.md +++ b/config/file.md @@ -2,17 +2,17 @@ outline: deep --- -# 管理 Vitest 配置文件 +# 管理 Vitest 配置文件 {#managing-vitest-config-file} -如果你正在使用 Vite,并且有一个 `vite.config` 文件,Vitest 会读取它并以插件匹配,并将其设置为你的 Vite 应用程序。如果你想使用不同的配置进行测试,或者你的主应用程序不特别依赖 Vite,你可以: +如果你正在使用 Vite,并且有 `vite.config` 文件,Vitest 会读取 Vite 配置并应用插件。如果你想使用不同的配置进行测试,或者你的主应用程序不特别依赖 Vite,你可以: -- 创建 `vitest.config.ts`,它将具有更高的优先级,并**覆盖** `vite.config.ts` 中的配置(Vitest 支持所有传统的 JS 和 TS 扩展,但不支持 `json`)-这意味着 `vite.config` 中的所有选项都将被**忽略** +- 创建 `vitest.config.ts`,它将具有更高的优先级,并 **覆盖** `vite.config.ts` 中的配置(Vitest 支持所有传统的 JS 和 TS 扩展,但不支持 `json`),这意味着 `vite.config` 中的所有选项都将被 **忽略** - 将 `--config` 选项传递给 CLI,例如 `vitest --config ./path/to/vitest.config.ts` - 使用`process.env.VITEST` 或者 `defineConfig` 上的 `mode` 属性(如果未被`mode`覆盖,则将设置为`test`/`benchmark`),以有条件地应用 `vite.config.ts` 中的不同配置 -要配置 `vitest` 本身,请在 Vite 配置中添加 `test` 属性。如果你要从 `vite` 本身导入`defineConfig`,你还需要使用[三斜杠命令](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-types-)添加对 Vitest 类型的引用。 +要配置 `vitest` 本身,请在 Vite 配置中添加 `test` 属性。如果你要从 `vite` 本身导入`defineConfig`,还需要在配置文件顶部使用 [三斜杠命令](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-types-) 添加 Vitest 类型引用。 -使用 `vite` 中的 `defineConfig`,你应该如下配置: +使用 `vite` 中的 `defineConfig`,你应该遵循如下配置: ```ts twoslash /// @@ -20,7 +20,7 @@ import { defineConfig } from 'vite' export default defineConfig({ test: { - // ... Specify options here. + // 在此配置选项... }, }) ``` @@ -32,7 +32,7 @@ import { defineConfig } from 'vitest/config' export default defineConfig({ test: { - // ... Specify options here. + // 在此配置选项... }, }) ``` diff --git a/config/index.md b/config/index.md index 711d0c2f..9eb0d702 100644 --- a/config/index.md +++ b/config/index.md @@ -2,134 +2,51 @@ outline: deep --- -# 配置索引 +# 配置索引 {#configuring-vitest} -## 配置 - -`vitest` 将读取你的项目根目录的 `vite.config.ts` 文件以匹配插件并设置为你的 Vite 应用。如果想使用不同的配置进行测试,你可以: - -- 创建 `vitest.config.ts`,优先级更高。 -- 将 `--config` 选项传递给 CLI,例如 `vitest --config ./path/to/vitest.config.ts` 。 -- 在 `defineConfig` 中使用 `process.env.VITEST` 或 `mode` 属性(默认值是 `test`)在 `vite.config.ts` 中有条件的应用不同的配置。 - -要配置 `vitest` 本身,请在你的 Vite 配置中添加 `test` 属性。如果你使用 `vite` 的 `defineConfig` 你还需要将 [三斜线指令](https://www.tslang.cn/docs/handbook/triple-slash-directives.html#-reference-types-) 写在配置文件的顶部。 - -使用 `vite` 的 `defineConfig` 可以参考下面的格式: - -```ts -/// -import { defineConfig } from 'vite' - -export default defineConfig({ - test: { - // ... Specify options here. - }, -}) -``` - -使用 `vitest/config` 中的 `defineConfig` 可以参考下面的格式: - -```ts -import { defineConfig } from 'vitest/config' - -export default defineConfig({ - test: { - // ... Specify options here. - }, -}) -``` - -如果有需要,你可以获取到 Vitest 的默认选项以扩展它们: - -```ts -import { configDefaults, defineConfig } from 'vitest/config' - -export default defineConfig({ - test: { - exclude: [...configDefaults.exclude, 'packages/template/*'], - }, -}) -``` - -## 选项 - -当使用单独的 `vitest.config.js` 时,如果需要,你还可以从另一个配置文件扩展 Vite 的选项: - -```ts -import { defineConfig, mergeConfig } from 'vitest/config' -import viteConfig from './vite.config' - -export default mergeConfig( - viteConfig, - defineConfig({ - test: { - exclude: ['packages/template/*'], - }, - }) -) -``` +要创建 Vitest 配置文件,请遵循 [指南](/config/file) 进行操作。在继续之前,请确保您已理解 Vitest 配置解析的工作原理。 ::: warning -`mergeConfig` helper 在 Vitest v0.30.0 之后可用。如果使用低版本,你可以直接从 `vite` 导入它。 -::: - -如果你的 vite 配置被定义为一个函数,可以像这样定义配置: - -```ts -import { defineConfig, mergeConfig } from 'vitest/config' -import viteConfig from './vite.config' - -export default defineConfig(configEnv => - mergeConfig( - viteConfig(configEnv), - defineConfig({ - test: { - exclude: ['packages/template/*'], - }, - }) - ) -) -``` - -## 配置选项 - -:::tip -除了以下选项,你还可以使用 [Vite](https://vitejs.dev/config/) 中的任何配置选项。 例如,`define` 定义全局变量,或 `resolve.alias` 定义别名。 - -*此处列出的*所有选项都位于配置中的 `test` 属性上: +本页上列出的 _所有选项_ 都位于配置内的 `test` 属性内: -```ts +```ts [vitest.config.js] export default defineConfig({ test: { exclude: [], }, }) ``` - ::: ::: tip -所有不支持在 [workspace](/guide/workspace) 项目配置中的配置选项都会有 标记。 + +由于 Vitest 使用 Vite 的配置,我们也可以使用 [Vite](https://cn.vite.dev/config/) 中的任何配置选项。例如,使用 `define` 来定义全局变量,或者使用 `resolve.alias` 来定义别名——这些选项应该在顶级定义,而不是在 `test` 属性内部。 + +所有在 [workspace](/guide/workspace) 项目配置中不受支持的选项均标有 号。 ::: ### include - **类型:** `string[]` - **默认值:** `['**/*.{test,spec}.?(c|m)[jt]s?(x)']` +- **命令行终端:** `vitest [...include], vitest **/*.test.js` 匹配包含测试文件的 glob 规则。 -::: tip NOTE -使用 coverage 时,Vitest 会自动将测试文件的 `include` 模式添加到 coverage 的默认 `exclude` 模式中。请参见 [`coverage.exclude`](#coverage-exclude)。 -::: - ### exclude - **类型:** `string[]` -- **默认值:** `['**/node_modules/**', '**/dist/**', '**/cypress/**', '**/.{idea,git,cache,output,temp}/**', '**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build}.config.*']` +- **默认值:** `['**/node_modules/**', '**/dist/**', '**/cypress/**', '**/.{idea,git,cache,output,temp}/**', '**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,eslint,prettier}.config.*']` +- **命令行终端:** `vitest --exclude "**/excluded-file"` 匹配排除测试文件的 glob 规则。 +::: warning +该选项不会影响代码覆盖率。如需从覆盖率报告中排除特定文件,请使用 [`coverage.exclude`](#coverage-exclude)。 + +如果使用命令行参数,这是唯一一个不会被覆盖配置的选项的参数。通过 `--exclude` 标志添加的所有 glob 规则都将追加到 `exclude` 中。 +::: + ### includeSource - **类型:** `string[]` @@ -139,10 +56,9 @@ export default defineConfig({ 当定义时,Vitest 将运行所有包含 `import.meta.vitest` 的匹配文件。 -### server {#server} +### server 0.34.0 {#server} - **类型:** `{ sourcemap?, deps?, ... }` -- **版本:** Since Vitest 0.34.0 Vite-Node 服务端选项。 @@ -175,7 +91,7 @@ Vite-Node 调试器选项。 - **类型:** `{ external?, inline?, ... }` -对依赖关系进行内联或外联的处理 +对依赖关系进行内联或外联的处理。 #### server.deps.external @@ -186,7 +102,7 @@ Vite-Node 调试器选项。 这些选项支持在 `node_modules` 中编写的包名称或在 [`deps.moduleDirectories`](#deps-moduledirectories) 中指定的包名称。例如,位于 `packages/some-name` 内的包`@company/some-name` 应指定为 `some-name`,并且 `packages` 应包含在 `deps.moduleDirectories` 中。基本上,Vitest 总是检查文件路径,而不是实际的包名称。 -如果成功匹配,Vitest 会在 _file path_ 上调用它,而不是包名称。 +如果使用正则匹配,Vitest 会在 _file path_ 上调用它,而不是包名称。 #### server.deps.inline @@ -219,10 +135,10 @@ Vite 将处理内联模块。这可能有助于处理以 ESM 格式传送 `.js` 处理依赖关系解析。 -#### deps.optimizer {#deps-optimizer} +#### deps.optimizer 0.34.0 {#deps-optimizer} -- **类型:** `{ ssr?, web? }` -- **参考:** [依赖优化选项](https://cn.vitejs.dev/config/dep-optimization-options.html) +- **类型:** `{ ssr?, client? }` +- **详情参阅:** [依赖优化选项](https://cn.vite.dev/config/dep-optimization-options.html) 启用依赖优化。如果你有很多测试,这可能会提高它们的性能。 @@ -233,7 +149,7 @@ Vite 将处理内联模块。这可能有助于处理以 ESM 格式传送 `.js` - 你的 `alias` 配置现在在捆绑包中得到处理 - 测试中的代码更接近于它在浏览器中的运行方式 -请注意,只有 `deps.experimentalOptimizer?.[mode].include` 选项中的包会被捆绑(一些插件会自动填充它,比如 Svelte)。 你可以在 [Vite](/config/dep-optimization-options) 文档中阅读有关可用选项的更多信息。默认情况,Vitest 的 `experimentalOptimizer.web` 用在 `jsdom` 和 `happy-dom`, 在 `node` 和 `edge` 环境下使用 `experimentalOptimizer.ssr`,但这可以在 [`transformMode`](#transformmode) 进行配置。 +请注意,只有 `deps.experimentalOptimizer?.[mode].include` 选项中的包会被捆绑(一些插件会自动填充它,比如 Svelte)。 你可以在 [Vite](https://cn.vite.dev/config/dep-optimization-options.html) 文档中阅读有关可用选项的更多信息。默认情况,Vitest 的 `experimentalOptimizer.web` 用在 `jsdom` 和 `happy-dom`, 在 `node` 和 `edge` 环境下使用 `experimentalOptimizer.ssr`,但这可以在 [`transformMode`](#transformmode) 进行配置。 此选项还继承了你的 `optimizeDeps` 配置(对于 web 环境, Vitest 将会继承 `optimizeDeps`,对于 ssr 则是 `ssr.optimizeDeps`)。如果你在 `deps.experimentalOptimizer` 中重新定义 `include`/`exclude`/`entries` 选项,它将在运行测试时覆盖你的 `optimizeDeps`。如果它们在 `exclude` 中配置,Vitest 会自动从 `include` 中删除相同的选项。 @@ -241,7 +157,7 @@ Vite 将处理内联模块。这可能有助于处理以 ESM 格式传送 `.js` 你将无法编辑用于调试的 `node_modules` 代码,因为该代码实际上位于你的 `cacheDir` 或 `test.cache.dir` 目录中。如果你想使用 `console.log` 语句进行调试,请直接编辑它或使用 `deps.experimentalOptimizer?.[mode].force` 选项强制重新绑定。 ::: -#### deps.optimizer.{mode}.enabled +#### deps.optimizer.{mode}.enabled 1.3.0 {#deps-optimizer-mode-enabled} - **类型:** `boolean` - **默认值:** `false` @@ -249,13 +165,10 @@ Vite 将处理内联模块。这可能有助于处理以 ESM 格式传送 `.js` 启用依赖优化。 ::: warning -此选项仅适用于 Vite 4.3.2 及更高版本。 +目前,此选项仅支持 Vite 4.3.2 及其更高版本。 ::: -#### deps.web 0.34.2+ - -#### deps.web - +#### deps.web 0.34.2 {#deps-web} - **类型:** `{ transformAssets?, ... }` 当转换模式设置为 `web` 时应用于外部文件的选项。默认情况下,`jsdom` 和 `happy-dom` 使用 `web` 模式,而 `node` 和 `edge` 环境使用 `ssr` 转换模式,因此这些选项不会影响这些环境中的文件。 @@ -322,7 +235,7 @@ TypeError: createAsyncThunk is not a function TypeError: default is not a function ``` -如果你使用的是绕过此 Node.js 限制的捆绑器或转译器,则可以手动启用此选项。默认情况下,当 `environment` 为 `node` 时,Vitest 假定你使用的是 Node ESM 语法,并且不关心命名导出。 +默认情况下,Vitest 假设你使用的是打包工具来绕过此问题,不会失败,但如果代码未被处理,你可以手动禁用此行为。 #### deps.moduleDirectories @@ -350,9 +263,9 @@ export default defineConfig({ ### runner - **类型**: `VitestRunnerConstructor` -- **默认值**: `node`, when running tests, or `benchmark`, when running benchmarks +- **默认值**: 运行测试时为`node`,运行基准测试时为 `benchmark` -自定义测试运行程序的路径。这是一项高级功能,应与自定义库运行器一起使用。你可以在 [文档](/advanced/runner) 中阅读更多相关信息。 +自定义测试运行器的路径。这是一项高级功能,应与自定义库运行器一起使用。你可以在 [文档](/advanced/runner) 中阅读更多相关信息。 ### benchmark @@ -388,7 +301,7 @@ export default defineConfig({ - **类型:** `Arrayable` - **默认值:** `'default'` -用于定义输出的自定义报告器。它可以包含一个或多个内置报告名称、报告实例和(或)自定义报告的路径。 +用于定义输出的自定义报告器。它可以包含一个或多个内置报告名称、报告实例和(或)自定义报告的路径。 #### benchmark.outputFile @@ -399,7 +312,7 @@ export default defineConfig({ 通过 CLI 命令提供对象,请使用以下语法: `--outputFile.json=./path --outputFile.junit=./other-path`. -#### benchmark.outputJson {#benchmark-outputJson} +#### benchmark.outputJson 1.6.0 {#benchmark-outputJson} - **类型:** `string | undefined` - **默认值:** `undefined` @@ -409,16 +322,16 @@ export default defineConfig({ 例如: ```sh -# save main branch's result +# 保存主分支的结果 git checkout main vitest bench --outputJson main.json -# change a branch and compare against main +# 切换到另一个分支并与主分支进行比较 git checkout feature vitest bench --compare main.json ``` -#### benchmark.compare {#benchmark-compare} +#### benchmark.compare 1.6.0 {#benchmark-compare} - **类型:** `string | undefined` - **默认值:** `undefined` @@ -432,12 +345,12 @@ vitest bench --compare main.json 在测试内部运行时定义自定义别名。它们将与来自 `resolve.alias` 的别名合并。 ::: warning -Vitest 使用 Vite SSR 基元来运行测试,这有[一定的缺陷](https://vitejs.dev/guide/ssr.html#ssr-externals)。 +Vitest 使用 Vite SSR 基元来运行测试,这有 [一定的缺陷](https://cn.vite.dev/guide/ssr.html#ssr-externals)。 -1. 别名只影响由[inlined](#server-deps-inline)模块直接用`import`关键字导入的模块(默认情况下所有源代码都是内联的)。 +1. 别名只影响由 [inlined](#server-deps-inline) 模块直接用 `import` 关键字导入的模块(默认情况下所有源代码都是内联的)。 2. Vitest 不支持对 `require` 调用进行别名。 -3. 如果您要别名外部依赖(例如,`react` -> `preact`),您可能需要别名实际的 `node_modules` 包,以使其适用于外部依赖。[Yarn](https://classic.yarnpkg.com/en/docs/cli/add/#toc-yarn-add-alias) 和 [pnpm](https://pnpm.io/aliases/) 都支持通过 `npm:` 前缀进行别名。 - ::: +3. 如果我们要别名外部依赖(例如,`react` -> `preact`),我们可能需要别名实际的 `node_modules` 包,以使其适用于外部依赖。[Yarn](https://classic.yarnpkg.com/en/docs/cli/add/#toc-yarn-add-alias) 和 [pnpm](https://pnpm.io/aliases/) 都支持通过 `npm:` 前缀进行别名。 +::: ### globals @@ -473,14 +386,14 @@ export default defineConfig({ ```ts // vitest.config.ts -import { defineConfig } from 'vitest/config' import AutoImport from 'unplugin-auto-import/vite' +import { defineConfig } from 'vitest/config' export default defineConfig({ plugins: [ AutoImport({ imports: ['vitest'], - dts: true, // generate TypeScript declaration + dts: true, // 生成 TypeScript 声明文件 }), ], }) @@ -492,16 +405,11 @@ export default defineConfig({ - **默认值:** `'node'` - **命令行终端:** `--environment=` -Vitest 中的默认测试环境是一个 Node.js 环境。如果你正在构建 Web 端应用,你可以使用 [`jsdom`](https://github.com/jsdom/jsdom) 或 [`happy-dom`](https://github.com/capricorn86/happy-dom) 这种类似浏览器(browser-like)的环境来替代 Node.js。 -如果你正在构建边缘计算函数,你可以使用 [`edge-runtime`](https://edge-runtime.vercel.app/packages/vm) 环境 - -::: tip -你还可以使用 [浏览器模式](/guide/browser/) 在浏览器中运行集成或单元测试,而无需模拟环境。 -::: +Vitest 中的默认测试环境是一个 Node.js 环境。如果你正在构建 Web 端应用,你可以使用 [`jsdom`](https://github.com/jsdom/jsdom) 或 [`happy-dom`](https://github.com/capricorn86/happy-dom) 这种类似浏览器(browser-like)的环境来替代 Node.js。 +如果你正在构建边缘计算函数,你可以使用 [`edge-runtime`](https://edge-runtime.vercel.app/packages/vm) 环境。 你可以通过在文件顶部添加包含 `@vitest-environment` 的文档块或注释,为某个测试文件中的所有测试指定环境: - 文档块格式: ```js @@ -543,17 +451,17 @@ test('use jsdom in this test file', () => { 从 0.23.0 开始,你还可以定义自定义环境。 当使用非内置环境时,Vitest 将尝试加载包 `vitest-environment-${name}`。 该包应导出一个具有 `Environment` 属性的对象: -```ts +```ts [environment.js] import type { Environment } from 'vitest' export default { name: 'custom', - transformMode: 'ssr', + viteEnvironment: 'ssr', setup() { - // custom setup + // 自定义设置 return { teardown() { - // called after all tests with this env have been run + // 在所有使用此环境的测试运行完毕后调用。 }, } }, @@ -563,9 +471,9 @@ export default { Vitest 还通过 `vitest/environments` 入口导出 `builtinEnvironments`,以防你只想扩展它。 你可以在 [测试环境指南](/guide/environment) 中阅读有关扩展测试环境的更多信息。 ::: tip -jsdom 环境变量导出了等同于当前[JSDOM](https://github.com/jsdom/jsdom) 的 `jsdom` 全局变量实例。如果你想让 TypeScript 识别它,可以在使用此环境时将 `vitest/jsdom`添加到 `tsconfig.json` 中: +自 1.3.0 起,jsdom 环境变量导出了等同于当前 [JSDOM](https://github.com/jsdom/jsdom) 的 `jsdom` 全局变量实例。如果你想让 TypeScript 识别它,可以在使用此环境时将 `vitest/jsdom`添加到 `tsconfig.json` 中: -```json +```json [tsconfig.json] { "compilerOptions": { "types": ["vitest/jsdom"] @@ -597,9 +505,9 @@ import { defineConfig } from 'vitest/config' export default defineConfig({ test: { environmentMatchGlobs: [ - // all tests in tests/dom will run in jsdom + // `tests/dom` 目录中的所有测试将在 jsdom 中运行。 ['tests/dom/**', 'jsdom'], - // all tests in tests/ with .edge.test.ts will run in edge-runtime + // `tests/` 目录中所有以 `.edge.test.ts` 结尾的测试将在 edge-runtime 中运行。 ['**/*.edge.test.ts', 'edge-runtime'], // ... ], @@ -607,7 +515,7 @@ export default defineConfig({ }) ``` -### poolMatchGlobs {#poolmatchglobs} +### poolMatchGlobs 0.29.4 {#poolmatchglobs} - **类型:** `[string, 'threads' | 'forks' | 'vmThreads' | 'vmForks' | 'typescript'][]` - **默认值:** `[]` @@ -622,11 +530,11 @@ import { defineConfig } from 'vitest/config' export default defineConfig({ test: { poolMatchGlobs: [ - // all tests in "worker-specific" directory will run inside a worker as if you enabled `--pool=threads` for them, + // "worker-specific" 目录下的所有测试将在 worker 中运行,等效于对这些测试启用 `--pool=threads` ['**/tests/worker-specific/**', 'threads'], - // run all tests in "browser" directory in an actual browser + // "browser" 目录下的所有测试将在真实浏览器中运行 ['**/tests/browser/**', 'browser'], - // all other tests will run based on "browser.enabled" and "threads" options, if you didn't specify other globs + // 其余测试将根据 "browser.enabled" 和 "threads" 配置项决定运行环境(未指定其他 glob 规则时) // ... ], }, @@ -644,17 +552,17 @@ export default defineConfig({ ### watch - **类型:** `boolean` -- **默认值:** `!process.env.CI` +- **默认值:** `!process.env.CI && process.stdin.isTTY` - **命令行终端:** `-w`, `--watch`, `--watch=false` -启动监听模式 +启动监听模式。 ### root - **类型:** `string` - **命令行终端:** `-r `, `--root=` -项目的根目录 +项目的根目录。 ### reporters @@ -662,17 +570,7 @@ export default defineConfig({ - **默认值:** `'default'` - **命令行终端:** `--reporter=`, `--reporter= --reporter=` -用于输出的自定义 reporters 。 Reporters 可以是 [一个 Reporter 实例](https://github.com/vitest-dev/vitest/blob/main/packages/vitest/src/types/reporter.ts) 或选择内置的 reporters 字符串: - -- `'default'` - 当他们经过测试套件 -- `'basic'` - 给定一个类似于 CI 中的默认报告实例 -- `'verbose'` - 保持完整的任务树可见 -- `'dot'` - 将每个任务显示为一个点 -- `'junit'` - JUnit XML 报告器(你可以使用 `VITEST_JUNIT_SUITE_NAME` 环境变量配置 `test suites` 标签名称) -- `'json'` - 给出一个简单的 JSON 总结 -- `'html'` - 根据 [`@vitest/ui`](/guide/ui) 输出 HTML 报告 -- `'hanging-process'` - 如果 Vitest 无法安全退出进程,则显示挂起进程列表。 这可能是一个复杂的操作,只有在 Vitest 始终无法退出进程时才启用它 -- 自定义报告的路径 (例如 `'./path/to/reporter.ts'`, `'@scope/reporter'`) +自定义 [报告器](/guide/reporters) 输出。报告器可以是 [一个 Reporter 实例](https://github.com/vitest-dev/vitest/blob/main/packages/vitest/src/node/types/reporter.ts)、用于选择内置报告器的字符串,或自定义实现的路径(例如 `./path/to/reporter.ts`、`@scope/reporter`)。 ### outputFile @@ -681,7 +579,7 @@ export default defineConfig({ 当指定 `--reporter=json`、`--reporter=html` 或 `--reporter=junit` 时,将测试结果写入一个文件。通过提供对象而不是字符串,你可以在使用多个报告器时定义单独的输出。 -### pool {#pool} +### pool 1.0.0 {#pool} - **类型:** `'threads' | 'forks' | 'vmThreads' | 'vmForks'` - **默认值:** `'forks'` @@ -699,9 +597,9 @@ export default defineConfig({ #### vmThreads -在 `threads` 线程池中使用[ VM 上下文](https://nodejs.org/api/vm.html)(在受限环境中)运行测试。 +在 `threads` 线程池中使用 [VM 上下文](https://nodejs.org/api/vm.html)(在沙箱环境中)运行测试。 -这样可以加快测试速度,但是当运行[ ESM 代码](https://github.com/nodejs/node/issues/37648)时,VM 模块可能不稳定。你的测试可能会[泄漏内存](https://github.com/nodejs/node/issues/33439),为了解决这个问题,考虑手动编辑 [`poolOptions.vmThreads.memoryLimit`](#pooloptions-vmthreads-memorylimit) 的值。 +这样可以加快测试速度,但是当运行 [ESM 代码](https://github.com/nodejs/node/issues/37648) 时,VM 模块可能不稳定。你的测试可能会 [泄漏内存](https://github.com/nodejs/node/issues/33439),为了解决这个问题,考虑手动编辑 [`poolOptions.vmThreads.memoryLimit`](#pooloptions-vmthreads-memorylimit) 的值。 ::: warning 在沙箱中运行代码有一些优点(测试速度更快),但也有许多缺点。 @@ -718,7 +616,7 @@ catch (err) { ``` - 导入 ES 模块会无限期地缓存它们,如果你有很多上下文(测试文件),这会导致内存泄漏。Node.js 中没有可以清除该缓存的 API。 -- 在沙盒环境中访问全局变量[需要更长的时间](https://github.com/nodejs/node/issues/31658)。 +- 在沙盒环境中访问全局变量 [需要更长的时间](https://github.com/nodejs/node/issues/31658)。 使用此选项时请注意这些问题。Vitest 团队无法解决我们这边的任何问题。 ::: @@ -727,14 +625,14 @@ catch (err) { 与 `vmThreads` 池类似,但通过 [tinypool](https://github.com/tinylibs/tinypool) 使用 `child_process` 而不使用 `worker_threads`。测试与主进程之间的通信速度虽然不如 `vmThreads` 快。但进程相关的 API(如 `process.chdir()` )在 `vmForks` 中却可以使用。请注意,这个与 `vmThreads` 中列出的池具有相同的缺陷。 -### poolOptions {#pooloptions} +### poolOptions 1.0.0 {#pooloptions} - **类型:** `Record<'threads' | 'forks' | 'vmThreads' | 'vmForks', {}>` - **默认值:** `{}` #### poolOptions.threads -`threads` 池的选项。 +`threads` 池相关选项。 ```ts import { defineConfig } from 'vitest/config' @@ -743,7 +641,7 @@ export default defineConfig({ test: { poolOptions: { threads: { - // Threads related options here + // 此处是与线程相关的选项 }, }, }, @@ -753,14 +651,14 @@ export default defineConfig({ ##### poolOptions.threads.maxThreads - **类型:** `number | string` -- **默认值:** _available CPUs_ +- **默认值:** _可用 CPU 核心数_ 最大线程数或百分比。还可以使用`VITEST_MAX_THREADS`环境变量进行设置。 ##### poolOptions.threads.minThreads - **类型:** `number | string` -- **默认值:** _available CPUs_ +- **默认值:** _可用 CPU 核心数_ 最小线程数或百分比。还可以使用`VITEST_MIN_THREADS`环境变量进行设置。 @@ -806,7 +704,7 @@ export default defineConfig({ #### poolOptions.forks -`forks` 池的选项。 +`forks` 池相关选项。 ```ts import { defineConfig } from 'vitest/config' @@ -815,7 +713,7 @@ export default defineConfig({ test: { poolOptions: { forks: { - // Forks related options here + // 此处是 Fork 相关的选项 }, }, }, @@ -825,14 +723,14 @@ export default defineConfig({ ##### poolOptions.forks.maxForks - **类型:** `number | string` -- **默认值:** _available CPUs_ +- **默认值:** _可用 CPU 核心数_ 最大分支数量或百分比。 ##### poolOptions.forks.minForks - **类型:** `number | string` -- **默认值:** _available CPUs_ +- **默认值:** _可用 CPU 核心数_ 最小分支数量或百分比。 @@ -869,7 +767,7 @@ export default defineConfig({ #### poolOptions.vmThreads -`vmThreads` 池的选项。 +`vmThreads` 池相关选项。 ```ts import { defineConfig } from 'vitest/config' @@ -878,7 +776,7 @@ export default defineConfig({ test: { poolOptions: { vmThreads: { - // VM threads related options here + // 此处是与虚拟线程相关的选项 }, }, }, @@ -887,22 +785,22 @@ export default defineConfig({ ##### poolOptions.vmThreads.maxThreads -- **类型:** `number | string` -- **默认:** _available CPUs_ +- **类型:** `number` +- **默认值:** _可用 CPU 核心数_ 最大线程数或百分比。还可以使用`VITEST_MAX_THREADS`环境变量进行设置。 ##### poolOptions.vmThreads.minThreads -- **类型:** `number | string` -- **默认值:** _available CPUs_ +- **类型:** `number` +- **默认值:** _可用 CPU 核心数_ -最小线程数或百分比。还可以使用`VITEST_MIN_THREADS`环境变量进行设置。 +最小线程数或百分比。还可以使用`VITEST_MAX_THREADS`环境变量进行设置。 ##### poolOptions.vmThreads.memoryLimit - **类型:** `string | number` -- **命令行终端:** `1 / CPU Cores` +- **命令行终端:** `1 / CPU 核心` 指定工作线程被回收之前的内存限制。该值在很大程度上取决于你的运行环境,因此最好手动指定它,而不是依赖默认值。 @@ -916,15 +814,17 @@ export default defineConfig({ - 有单位时 - `50%` - 如上,占系统总内存的百分比 - `100KB`, `65MB`, 等 - 用单位表示固定的内存限制 - - `K` / `KB` - Kilobytes (x1000) - - `KiB` - Kibibytes (x1024) - - `M` / `MB` - Megabytes - `MiB` - Mebibytes - - `G` / `GB` - Gigabytes - `GiB` - Gibibytes + - `K` / `KB` - 千字节 (x1000) + - `KiB` - 千字节 (x1024) + - `M` / `MB`- 千字节 + - `MiB` - 兆字节 + - `G` / `GB` - 千兆字节 + - `GiB` - 千兆字节 ::: ::: warning -由于系统内存报告不正确,基于百分比的内存限制[在 Linux CircleCI 上不起作用](https://github.com/jestjs/jest/issues/11956#issuecomment-1212925677)。 +由于系统内存报告不正确,基于百分比的内存限制 [在 Linux CircleCI 上不起作用](https://github.com/jestjs/jest/issues/11956#issuecomment-1212925677)。 ::: ##### poolOptions.vmThreads.useAtomics @@ -941,7 +841,7 @@ export default defineConfig({ - **类型:** `string[]` - **默认值:** `[]` -将附加参数传递给虚拟机上下文中的 `node` 进程。更多信息,详细信息可以浏览 [Command-line API | Node.js](https://nodejs.org/docs/latest/api/cli.html) 。 +将附加参数传递给虚拟机上下文中的 `node` 进程。详情请参阅 [Command-line API | Node.js](https://nodejs.org/docs/latest/api/cli.html) 。 :::warning 使用时要小心,因为某些选项(如 --prof、--title )可能会导致 worker 崩溃。详细信息可以浏览 https://github.com/nodejs/node/issues/41103。 @@ -949,7 +849,7 @@ export default defineConfig({ #### poolOptions.vmForks -`vmForks` 池的选项 +`vmForks` 池相关选项 ```ts import { defineConfig } from 'vitest/config' @@ -958,7 +858,7 @@ export default defineConfig({ test: { poolOptions: { vmForks: { - // VM forks related options here + // 此处是虚拟 Fork 相关的选项 }, }, }, @@ -967,22 +867,22 @@ export default defineConfig({ ##### poolOptions.vmForks.maxForks -- **类型:** `number | string` -- **默认值:** _available CPUs_ +- **类型:** `number` +- **默认值:** _可用 CPU 核心数_ -最大线程数或百分比。也可以使用 `VITEST_MAX_FORKS` 环境变量。 +最大线程数或百分比。你也可以使用 `VITEST_MAX_FORKS` 环境变量。 ##### poolOptions.vmForks.minForks -- **类型:** `number | string` -- **默认值:** _available CPUs_ +- **类型:** `number` +- **默认值:** _可用 CPU 核心数_ -最小线程数或百分比。也可以使用 `VITEST_MIN_FORKS` 环境变量。 +最小线程数或百分比。你也可以使用 `VITEST_MIN_FORKS` 环境变量。 ##### poolOptions.vmForks.memoryLimit - **类型:** `string | number` -- **默认值:** `1 / CPU Cores` +- **默认值:** `1 / CPU 核心` 指定 Worker 被回收前的内存限制。该值在很大程度上取决于环境,因此最好手动指定,而不是依赖默认值。该值的计算方法查看 [`poolOptions.vmThreads.memoryLimit`](#pooloptions-vmthreads-memorylimit) @@ -991,13 +891,13 @@ export default defineConfig({ - **类型:** `string[]` - **默认值:** `[]` -将附加参数传递给虚拟机上下文中的 `node` 进程。更多信息,查看 [Command-line API | Node.js](https://nodejs.org/docs/latest/api/cli.html) 了解更多详情。。 +将附加参数传递给虚拟机上下文中的 `node` 进程。更多内容请参阅 [Command-line API | Node.js](https://nodejs.org/docs/latest/api/cli.html)。 :::warning 使用时要小心,因为某些选项(如 `--prof` 、`--title`)可能会导致 worker 崩溃。查看 https://github.com/nodejs/node/issues/41103 了解更多详情。 ::: -### fileParallelism {#fileparallelism} +### fileParallelism 1.1.0 {#fileparallelism} - **类型:** `boolean` - **默认值:** `true` @@ -1006,25 +906,25 @@ export default defineConfig({ 所有测试文件应该并行运行。将其设置为 `false` 将覆盖 `maxWorkers` 和 `minWorkers` 选项为 `1`。 ::: tip -此选项不会影响在同一文件中运行的测试。如果你想并行运行这些程序,请在[description](/api/#describe-concurrent)或通过[a config](#sequence-concurrent) 上使用 `concurrent` 选项。 +此选项不会影响在同一文件中运行的测试。如果你想并行运行这些程序,请在 [describe](/api/#describe-concurrent) 或通过 [配置](#sequence-concurrent) 上使用 `concurrent` 选项。 ::: -### maxWorkers {#maxworkers} +### maxWorkers 1.1.0 {#maxworkers} -- **类型:** `number | string` +- **类型:** `number` 运行测试时设置的最大工作线程数或百分比。`poolOptions。{threads,vmThreads}.maxThreads `/`poolOptions.forks.maxForks` 具有更高的优先级。 -### minWorkers {#minworkers} +### minWorkers 1.1.0 {#minworkers} -- **类型:** `number | string` +- **类型:** `number` 运行测试时设置的最小工作线程数或百分比。`poolOptions.{threads,vmThreads}.minThreads`/`poolOptions.forks.minForks` 具有更高的优先级。 ### testTimeout - **类型:** `number` -- **默认值:** `5_000` in Node.js, `15_000` if `browser.enabled` is `true` +- **默认值:** 在 Node.js 环境下为 `5_000`,当 browser.enabled 为 `true` 时为 `15_000` - **命令行终端:** `--test-timeout=5000`, `--testTimeout=5000` 测试的默认超时时间(以毫秒为单位)。 @@ -1032,10 +932,10 @@ export default defineConfig({ ### hookTimeout - **类型:** `number` -- **默认值:** `10_000` in Node.js, `30_000` if `browser.enabled` is `true` +- **默认值:** 在 Node.js 环境下为 `10_000`,当 browser.enabled 为 `true` 时为 `30_000` - **命令行终端:** `--hook-timeout=10000`, `--hookTimeout=10000` -钩子(hook)的默认超时时间(以毫秒为单位)。 +钩子 (hook) 的默认超时时间(以毫秒为单位)。 ### teardownTimeout @@ -1047,7 +947,7 @@ Vitest 关闭时等待关闭的默认超时时间,以毫秒为单位 ### silent -- **类型:** `boolean` +- **类型:** `boolean | 'passed-only'` - **默认值:** `false` - **命令行终端:** `--silent`, `--silent=false` @@ -1057,10 +957,10 @@ Vitest 关闭时等待关闭的默认超时时间,以毫秒为单位 - **类型:** `string | string[]` -setup 文件的路径。它们将运行在每个测试文件之前。 +setup 文件的路径。它们将在每个测试文件之前运行。 ::: info 提示 -更改配置文件将触发所有测试的重新运行。 +编辑设置文件将自动触发所有测试的重新运行。 ::: 你可以在全局设置文件中使用 `process.env.VITEST_POOL_ID`(类似整数的字符串)来区分不同的线程。 @@ -1080,7 +980,7 @@ if (!globalThis.defined) { globalThis.defined = true } -// hooks are reset before each suite +// Hook 在每个套件之前重置 afterEach(() => { cleanup() }) @@ -1101,18 +1001,16 @@ globalThis.resetBeforeEachTest = true ::: ::: warning -全局设置只有在至少有一个正在运行的测试时才运行。这意味着在测试文件更改后,全局安装程序可能会在监视模式下开始运行(测试文件将等待全局安装程序完成后再运行)。 +自 Vitest 1.0.0-beta 起,全局设置只有在至少有一个正在运行的测试时才运行。这意味着在测试文件更改后,全局安装程序可能会在监视模式下开始运行(测试文件将等待全局安装程序完成后再运行)。 -请注意,全局设置在不同的全局范围内运行,因此你的测试无法访问此处定义的变量。悬停,从 1.0.0 开始,你可以通过 `provide` 方法将可序列化数据传递给测试: +请注意,全局设置在不同的全局范围内运行,因此你的测试无法访问此处定义的变量。然而,自 1.0.0 起,你可以通过 [`provide`](#provide) 方法将可序列化数据传递给测试: :::code-group - -```js [globalSetup.js] +```ts [globalSetup.js] export default function setup({ provide }) { provide('wsPort', 3000) } ``` - ```ts [globalSetup.ts] import type { GlobalSetupContext } from 'vitest/node' @@ -1120,15 +1018,13 @@ export default function setup({ provide }: GlobalSetupContext) { provide('wsPort', 3000) } -// 你还可以可以扩展 `ProvidedContext` 类型,以便对 `provide/inject` 方法进行类型安全访问: declare module 'vitest' { export interface ProvidedContext { wsPort: number } } ``` - -```ts [example.test.js] +``` js [example.test.js] import { inject } from 'vitest' inject('wsPort') === 3000 @@ -1136,30 +1032,37 @@ inject('wsPort') === 3000 ::: +### watchExclude + +- **类型:** `string[]` +- **默认值:** `['**/node_modules/**', '**/dist/**']` +- **弃用** 请使用 [`server.watch.ignored`](https://cn.vite.dev/config/server-options.html#server-watch) + +触发监视重新运行时要忽略的文件路径(glob 模式)。 + ### forceRerunTriggers - **类型**: `string[]` - **默认值:** `['**/package.json/**', '**/vitest.config.*/**', '**/vite.config.*/**']` -将触发整个套件重新运行的文件路径的全局 glob 模式。 如果在 git diff 中找到触发器,则与 --changed 参数配对时,将运行整个测试套件。 +将触发整个套件重新运行的文件路径的 glob 模式。 如果在 git diff 中找到触发器,则与 --changed 参数配对时,将运行整个测试套件。 如果你正在测试调用 CLI 命令时很有用,因为 Vite 无法构建模块依赖树: ```ts test('execute a script', async () => { - // Vitest cannot rerun this test, if content of `dist/index.js` changes + // 如果 `dist/index.js` 的内容发生变化,Vitest 无法重新运行此测试 await execa('node', ['dist/index.js']) }) ``` :::tip 提醒 -请确保您的文件没有被 [`server.watch.ignored`](https://vitejs.dev/config/server-options.html#server-watch)排除在外。 +请确保我们的文件没有被 [`server.watch.ignored`](https://cn.vite.dev/config/server-options.html#server-watch)排除在外。 ::: ### coverage -- **类型:** `CoverageC8Options | CoverageIstanbulOptions` -- **默认值:** `undefined` +你可以选择 [`v8`](/guide/coverage.html#v8-provider)、[`istanbul`](/guide/coverage.html#istanbul-provider),或者 [自定义覆盖率工具](/guide/coverage#custom-coverage-provider) 来进行代码覆盖率统计。 你可以使用点符号向 CLI 提供覆盖选项: @@ -1168,7 +1071,7 @@ npx vitest --coverage.enabled --coverage.provider=istanbul --coverage.all ``` ::: warning -如果你使用带点符号的覆盖选项,请不要忘记指定 `--coverage.enabled`。 在这种情况下,不要提供单个 --coverage 选项。 +如果正在使用点表示法配置测试覆盖率选项,请务必指定 `--coverage.enabled`。在这种情况下,切勿单独传递 `--coverage` 选项。 ::: #### coverage.provider @@ -1191,16 +1094,16 @@ npx vitest --coverage.enabled --coverage.provider=istanbul --coverage.all #### coverage.include - **类型:** `string[]` -- **默认值:** `['**']` +- **默认值:** 在执行测试过程中所引入的文件。 - **可用的测试提供者:** `'v8' | 'istanbul'` -- **命令行终端:** `--coverage.include=`, `--coverage.include= --coverage.include=` +- **命令行终端:** `--coverage.include=`, `--coverage.include= --coverage.include=` -匹配包含测试覆盖率的 glob 规则 +以 glob 模式指定需要统计覆盖率的文件列表。 #### coverage.extension - **类型:** `string | string[]` -- **默认值:** `['.js', '.cjs', '.mjs', '.ts', '.mts', '.cts', '.tsx', '.jsx', '.vue', '.svelte', '.marko']` +- **默认值:** `['.js', '.cjs', '.mjs', '.ts', '.mts', '.tsx', '.jsx', '.vue', '.svelte', '.marko', '.astro']` - **可用的测试提供者:** `'v8' | 'istanbul'` - **命令行终端:** `--coverage.extension=`, `--coverage.extension= --coverage.extension=` @@ -1250,10 +1153,6 @@ export default defineConfig({ }) ``` -::: tip NOTE -Vitest 会自动将测试文件的 `include` 模式添加到 `coverage.exclude` 的默认值中。 -::: - #### coverage.all - **类型:** `boolean` @@ -1279,23 +1178,7 @@ Vitest 会自动将测试文件的 `include` 模式添加到 `coverage.exclude` - **可用的测试提供者:** `'v8' | 'istanbul'` - **命令行终端:** `--coverage.cleanOnRerun`, `--coverage.cleanOnRerun=false` -是否将所有文件(包括未测试的文件)包括在报告中。 - -#### clean - -- **类型:** `boolean` -- **默认值:** `true` -- **可用的测试提供者:** `'v8' | 'istanbul'` - -运行测试之前是否清除覆盖率结果 - -#### cleanOnRerun - -- **类型:** `boolean` -- **默认值:** `false` -- **可用的测试提供者:** `'v8' | 'istanbul'` - -监视重新运行时是否清除覆盖率报告 +监视重新运行时是否清除覆盖率报告。设置为 `false` 可保留观察模式下上次运行的覆盖结果。 #### coverage.reportsDirectory @@ -1310,7 +1193,7 @@ Vitest 会自动将测试文件的 `include` 模式添加到 `coverage.exclude` 配置测试覆盖率报告写入的目录。 -要预览覆盖范围报告,请使用 [HTML reporter](/guide/reporters.html#html-reporter), 该选项必须设置为 html 报告目录的子目录 (比如 `./html/coverage`). +要预览覆盖范围报告,请使用 [HTML 报告器](/guide/reporters.html#html-reporter), 该选项必须设置为 html 报告目录的子目录 (比如 `./html/coverage`). #### coverage.reporter @@ -1337,27 +1220,27 @@ Vitest 会自动将测试文件的 `include` 模式添加到 `coverage.exclude` } ``` -我们还可以传递自定义覆盖报告器。查看[自定义覆盖报告器](/guide/coverage#%E8%87%AA%E5%AE%9A%E4%B9%89%E8%A6%86%E7%9B%96%E7%8E%87%E6%8F%90%E4%BE%9B%E8%80%85)了解更多详情。 +我们还可以传递自定义覆盖报告器。查看 [自定义覆盖率的报告器](/guide/coverage#custom-coverage-reporter) 了解更多详情。 ```ts { reporter: [ - // Specify reporter using name of the NPM package + // 使用 NPM 包的名称指定报告器 "@vitest/custom-coverage-reporter", ["@vitest/custom-coverage-reporter", { someOption: true }], - // Specify reporter using local path + // 使用本地路径指定报告器 "/absolute/path/to/custom-reporter.cjs", ["/absolute/path/to/custom-reporter.cjs", { someOption: true }], ]; } ``` -我们可以在 Vitest UI 中查看覆盖率报告:查看 [Vitest UI Coverage](/guide/coverage#vitest-ui) 了解更多详情。 +自 Vitest 0.31.0 起,我们可以在 Vitest UI 中查看覆盖率报告:查看 [UI 模式](/guide/coverage#vitest-ui) 了解更多详情。 -#### coverage.reportOnFailure {#coverage-reportonfailure} +#### coverage.reportOnFailure 0.31.2 {#coverage-reportonfailure} - **类型:** `boolean` - **默认值:** `false` @@ -1386,7 +1269,7 @@ Vitest 会自动将测试文件的 `include` 模式添加到 `coverage.exclude` #### coverage.thresholds -覆盖范围阈值选项 +覆盖率阈值选项。 ##### coverage.thresholds.lines @@ -1394,8 +1277,7 @@ Vitest 会自动将测试文件的 `include` 模式添加到 `coverage.exclude` - **可用的测试提供者:** `'v8' | 'istanbul'` - **命令行终端:** `--coverage.thresholds.lines=` -lines 的全局阈值。 -更多信息请查看 [istanbul documentation](https://github.com/istanbuljs/nyc#coverage-thresholds)。 +lines 的全局阈值。详见 [istanbul 文档](https://github.com/istanbuljs/nyc#coverage-thresholds) 了解更多信息。 ##### coverage.thresholds.functions @@ -1403,8 +1285,7 @@ lines 的全局阈值。 - **可用的测试提供者:** `'v8' | 'istanbul'` - **命令行终端:** `--coverage.thresholds.functions=` -functions 的全局阈值。 -更多信息请查看 [istanbul documentation](https://github.com/istanbuljs/nyc#coverage-thresholds)。 +functions 的全局阈值。详见 [istanbul 文档](https://github.com/istanbuljs/nyc#coverage-thresholds) 了解更多信息。 ##### coverage.thresholds.branches @@ -1412,8 +1293,7 @@ functions 的全局阈值。 - **可用的测试提供者:** `'v8' | 'istanbul'` - **命令行终端:** `--coverage.thresholds.branches=` -branches 的全局阈值。 -更多信息请查看 [istanbul documentation](https://github.com/istanbuljs/nyc#coverage-thresholds)。 +branches 的全局阈值。详见 [istanbul 文档](https://github.com/istanbuljs/nyc#coverage-thresholds) 了解更多信息。 ##### coverage.thresholds.statements @@ -1422,7 +1302,6 @@ branches 的全局阈值。 - **命令行终端:** `--coverage.thresholds.statements=` statements 的全局阈值。 -更多信息请查看 [istanbul documentation](https://github.com/istanbuljs/nyc#coverage-thresholds)。 ##### coverage.thresholds.perFile @@ -1435,12 +1314,12 @@ statements 的全局阈值。 ##### coverage.thresholds.autoUpdate -- **类型:** `boolean` +- **类型:** `boolean | function` - **默认值:** `false` - **可用的测试提供者:** `'v8' | 'istanbul'` - **命令行终端:** `--coverage.thresholds.autoUpdate=` -如果当前覆盖率高于配置的阈值时,将所有阈值 `lines` 、`functions` 、`branches` 和 `statements` 更新到配置文件中。 +如果当前覆盖率优于配置的阈值时,将所有阈值 `lines`、`functions`、`branches` 和 `statements` 更新到配置文件中。 此选项有助于在覆盖率提高时保持阈值不变。 ##### coverage.thresholds.100 @@ -1461,17 +1340,22 @@ statements 的全局阈值。 设置与 glob 模式匹配的文件的阈值。 +::: tip NOTE +Vitest 会将所有文件,包括那些被 glob 模式覆盖的文件,计入全局覆盖率阈值。 +这与 Jest 的行为不同。 +::: + ```ts { coverage: { thresholds: { - // Thresholds for all files + // 所有文件的阈值 functions: 95, branches: 70, - // Thresholds for matching glob pattern + // 匹配全局模式的阈值 'src/utils/**.ts': { statements: 95, functions: 90, @@ -1479,8 +1363,8 @@ statements 的全局阈值。 lines: 80, }, - // Files matching this pattern will only have lines thresholds set. - // Global thresholds are not inherited. + // 匹配此模式的文件将仅设置行阈值。 + // 全局阈值不会被继承。 '**/math.ts': { lines: 100, } @@ -1501,7 +1385,7 @@ statements 的全局阈值。 该选项只有在使用的编译器删除了转译代码中的注释和其他非运行时代码时才有效。 默认情况下,Vite 使用 ESBuild,它会删除 `.ts`、`.tsx` 和 `.jsx` 文件中的注释和 Typescript 类型。 -如果还想将 ESBuild 应用于其他文件,请在 [`esbuild` options](https://cn.vitejs.dev/config/shared-options.html#esbuild) 中定义它们: +如果还想将 ESBuild 应用于其他文件,请在 [`esbuild` 选项](https://cn.vitejs.dev/config/shared-options.html#esbuild) 中定义它们: ```ts import { defineConfig } from 'vitest/config' @@ -1525,7 +1409,7 @@ export default defineConfig({ - **类型:** `string[]` - **默认值:** `[]` -- **可用的测试提供者:** `'istanbul'` +- **可用的测试提供者:** `'v8' | 'istanbul'` - **命令行终端:** `--coverage.ignoreClassMethods=` 设置为要忽略覆盖率的类方法名称数组。参考 [istanbul 文档](https://github.com/istanbuljs/nyc#ignoring-methods) 来了解详情。 @@ -1559,7 +1443,7 @@ export default defineConfig({ - **可用的测试提供者:** `'v8' | 'istanbul'` - **命令行终端:** `--coverage.watermarks.statements=50,80`, `--coverage.watermarks.branches=50,80` -语句、行、分支和函数的水印。有关更多信息,请参见 [istanbul 文档](https://github.com/istanbuljs/nyc#high-and-low-watermarks)。 +语句、行、分支和函数的水印。更多内容请参阅 [istanbul 文档](https://github.com/istanbuljs/nyc#high-and-low-watermarks)。 #### coverage.processingConcurrency @@ -1576,7 +1460,7 @@ export default defineConfig({ - **可用的测试提供者:** `'custom'` - **命令行终端:** `--coverage.customProviderModule=` -指定自定义覆盖率提供者的模块名称或路径。有关详细信息,请参阅[指南 - 自定义覆盖率提供者](/guide/coverage#custom-coverage-provider)。 +指定自定义覆盖率提供者的模块名称或路径。有关详细信息,请参阅 [指南 - 自定义覆盖率提供者](/guide/coverage#custom-coverage-provider)。 ### testNamePattern @@ -1589,12 +1473,12 @@ export default defineConfig({ ```js import { expect, test } from 'vitest' -// run +// 运行 test('OnlyRunThis', () => { expect(true).toBe(true) }) -// skipped +// 跳过 test('doNotRun', () => { expect(true).toBe(true) }) @@ -1616,18 +1500,17 @@ test('doNotRun', () => { 提供 API 服务的端口。当设置为 true 时,默认端口为 51204 -### browser {#browser} +### browser 0.29.4 {#browser} -- **类型:** `{ enabled?, name?, provider?, headless?, api? }` +- **类型:** `{ enabled?, name?, provider?, headless?, api?, slowHijackESM? }` - **默认值:** `{ enabled: false, headless: process.env.CI, api: 63315 }` -- **命令行终端:** `--browser`, `--browser=`, `--browser.name=chrome --browser.headless` +- **命令行终端:** `--browser=`, `--browser.name=chrome --browser.headless` -在浏览器中运行 Vitest 测试。我们默认使用 [WebdriverIO](https://webdriver.io/) 来运行测试,但可以使用 [browser.provider](#browser-provider) 选项进行配置。 +在浏览器环境中运行 Vitest 测试。默认使用 [WebdriverIO](https://webdriver.io/) 作为测试运行器,但可通过 [browser.provider](/config/#browser-provider) 选项进行配置。 ::: tip NOTE -在 [指南页面](/guide/browser/) 中阅读有关在真实浏览器中进行测试的更多信息。 +更多关于真实浏览器测试的详细信息,请参阅 [浏览器模式 | 指南](/guide/browser/)。 ::: - ::: warning 这是一项实验性功能。重大更改可能不会遵循 semver,请在使用时锁定 Vitest 的版本。 ::: @@ -1638,9 +1521,9 @@ test('doNotRun', () => { - **默认值:** `false` - **命令行终端:** `--browser`, `--browser.enabled=false` -默认情况下在浏览器中运行所有测试。可以用 [`poolMatchGlobs`](#poolmatchglobs) 选项覆盖。 +默认在浏览器内运行所有测试。可使用 `poolMatchGlobs` 选项覆盖。 -#### browser.name +#### browser.name - **类型:** `string` - **命令行终端:** `--browser=safari` @@ -1657,7 +1540,7 @@ test('doNotRun', () => { - **默认值:** `process.env.CI` - **命令行终端:** `--browser.headless`, `--browser.headless=false` -以 `headless` 模式运行浏览器。如果你在 CI 中运行 Vitest,它将默认启用。 +以 `无头` 模式运行浏览器。如果你在 CI 中运行 Vitest,它将默认启用。 #### browser.isolate @@ -1667,6 +1550,20 @@ test('doNotRun', () => { 在单独的 iframe 中运行每个测试。 +### browser.fileParallelism 1.3.0 {#browser-fileparallelism} + +- **类型:** `boolean` +- **默认值:** 与 [`fileParallelism`](#fileparallelism-110) 相同 +- **命令行终端:** `--browser.fileParallelism=false` + +同时创建所有测试 iframe,使它们并行运行。 + +这样就无法使用交互式 API(如点击或悬停),因为屏幕上会同时出现多个 iframe,但如果在测试中不依赖于这些 API,那么同时运行所有 iframe 可能会快很多。 + +::: tip +如果通过 [`browser.isolate=false`](#browserisolate) 禁用了隔离,由于测试运行器的特性,测试文件仍会一个接一个地运行。 +::: + #### browser.api - **类型:** `number | { port?, strictPort?, host? }` @@ -1677,11 +1574,11 @@ test('doNotRun', () => { #### browser.provider -- **类型:** `'webdriverio' | 'playwright' | string` -- **默认值:** `'webdriverio'` +- **类型:** `'webdriverio' | 'playwright' | 'preview' | string` +- **默认值:** `'preview'` - **命令行终端:** `--browser.provider=playwright` -设置运行浏览器测试时浏览器的路径。Vitest 提供了两个浏览器驱动选项: `webdriverio`(默认) 和 `playwright`。自定义提供商应该使用 `default` 进行导出,并具有如下类型签名: +设置运行浏览器测试时浏览器的路径。Vitest 提供了三个浏览器驱动选项: `preview`(默认)、`webdriverio` 和 `playwright` 。自定义提供商应该使用 `default` 进行导出,并具有如下类型签名: ```ts export interface BrowserProvider { @@ -1689,7 +1586,7 @@ export interface BrowserProvider { getSupportedBrowsers: () => readonly string[] initialize: ( ctx: Vitest, - options: { browser: string, options?: BrowserProviderOptions } + options: { browser: string; options?: BrowserProviderOptions } ) => Awaitable openPage: (url: string) => Awaitable close: () => Awaitable @@ -1700,13 +1597,15 @@ export interface BrowserProvider { 这是一个对库作者友好的的高级 API。如果你只需要在浏览器中运行测试,请使用 [browser](#browser) 选项。 ::: -#### browser.providerOptions {#browser-provideroptions} +#### browser.providerOptions 1.0.0 {#browser-provideroptions} - **类型:** `BrowserProviderOptions` 调用 `provider.initialize` 时将传递给提供程序的选项。 ```ts +import { defineConfig } from 'vitest/config' + export default defineConfig({ test: { browser: { @@ -1721,8 +1620,7 @@ export default defineConfig({ ``` ::: tip -为了在使用内置提供程序时获得更好的类型安全性,你可以将以下类型之一(针对正在使用的提供程序)添加到 tsconfig 的 `compilerOptions.types` 字段中: - +为提升使用内置提供程序时的类型安全性,您可在 tsconfig 的 `compilerOptions.types` 字段中添加对应提供程序的类型声明(根据实际使用的提供程序选择): ```json { "compilerOptions": { @@ -1736,36 +1634,18 @@ export default defineConfig({ ::: -#### browser.ui {#browser-ui} +#### browser.slowHijackESM 0.31.0 {#browser-slowhijackesm} - **类型:** `boolean` -- **默认值:** `!isCI` -- **命令行终端:** `--browser.ui=false` - -是否应该将 Vitest UI 注入到页面中。默认情况下,在开发过程中注入 UI iframe。 - -#### browser.viewport {#browser-viewport} - -- **Type:** `{ width, height }` -- **Default:** `414x896` - -默认 iframe 的viewport。 - -#### browser.screenshotDirectory {#browser-screenshotdirectory} - -- **Type:** `string` -- **Default:** `__snapshots__` in the test file directory - -相对于 `root` 的快照目录路径。 +- **默认值:** `false` -#### browser.screenshotFailures {#browser-screenshotfailures} +在 Node.js 中运行测试时,Vitest 可以使用自己的模块解析来轻松地使用 `vi.mock` 语法模拟模块。但是,在浏览器中复制 ES 模块解析并不容易,因此我们需要在浏览器可以使用它之前转换你的源文件。 -- **Type:** `boolean` -- **Default:** `!browser.ui` +此选项对在 Node.js 中运行的测试没有影响。 -如果测试失败,Vitest 是否应截图。 +在浏览器中运行时,默认情况下会启用此选项。如果你依赖于使用 `vi.spyOn` 监视 ES 模块,则可以启用此实验功能来监视模块导出。 -#### browser.orchestratorScripts {#browser-orchestratorscripts} +#### browser.indexScripts 1.6.0 {#browser-indexscripts} - **类型:** `BrowserScript[]` - **默认值:** `[]` @@ -1805,7 +1685,7 @@ export interface BrowserScript { } ``` -#### browser.testerScripts {#browser-testerscripts} +#### browser.testerScripts 1.6.0 {#browser-testerscripts} - **类型:** `BrowserScript[]` - **默认值:** `[]` @@ -1814,49 +1694,43 @@ export interface BrowserScript { 脚本 `src` 和 `content` 将由 Vite 插件处理。 -#### browser.commands {#browser-commands} - -- **类型:** `Record` -- **默认值:** `{ readFile, writeFile, ... }` - -自定义[命令](/guide/browser/commands),可在浏览器测试期间从 `@vitest/browser/commands` 导入。 - ### clearMocks - **类型:** `boolean` - **默认值:** `false` -是否在每次测试前对所有监听(Spy)调用 [`.mockClear()`](/api/mock#mockclear)。这将清除模拟历史记录,但不会将其实现重置为默认值。 +每次测试前,会对所有监听对象调用 [`.mockClear()`](/api/mock#mockclear)。这将清除模拟历史记录,但不会影响模拟实现。 ### mockReset - **类型:** `boolean` - **默认值:** `false` -是否在每次测试之前对所有监听(Spy)调用 [`.mockReset()`](/api/mock#mockreset)。 这将清除模拟历史并将其实现重置为空函数(将返回`undefined`)。 +每次测试前,都会对所有监听对象调用 [`.mockReset()`](/api/mock#mockreset)。这将清除模拟历史记录,并将每个实现重置为其原始状态。 ### restoreMocks - **类型:** `boolean` - **默认值:** `false` -是否在每次测试之前对所有监听(Spy)调用 [`.mockRestore()`](/api/#mockrestore)。 这将清除模拟历史并将其实现重置为原始历史。 +每次测试前,都会对所有监听对象调用 [`.mockRestore()`](/api/mock#mockrestore)。 +这将清除模拟历史记录,将每个实现恢复为原始实现,并恢复被监视对象的原始描述符。 -### unstubEnvs {#unstubenvs} +### unstubEnvs 0.26.0 {#unstubenvs} - **类型:** `boolean` - **默认值:** `false` 将在每次测试前调用 [`vi.unstubAllEnvs`](/api/#vi-unstuballenvs)。 -### unstubGlobals {#unstubglobals} +### unstubGlobals 0.26.0 {#unstubglobals} - **类型:** `boolean` - **默认值:** `false` 将在每次测试前调用 [`vi.unstubAllGlobals`](/api/#vi-unstuballglobals)。 -### testTransformMode {#testtransformmode} +### testTransformMode 0.34.0 {#testtransformmode} - **类型:** `{ web?, ssr? }` @@ -1870,33 +1744,19 @@ export interface BrowserScript { 对指定的文件使用 SSR 转换管道。
Vite 插件在处理这些文件时会收到 `ssr: true` 标志。 -#### testTransformMode.web +#### testTransformMode.web - **类型:** `string[]` - **默认值:** `[]` -首先会进行正常的转换管道(针对浏览器),然后进行 SSR 重写以在 Node 中运行代码。
+首先会进行正常的转换流程(针对浏览器环境),然后进行 SSR 重写以在 Node 中运行代码。
Vite 插件在处理这些文件时会收到 `ssr: false` 标志。 -当你使用 JSX 作为 React 以外的组件模型(例如 Vue JSX 或 SolidJS)时,你可能需要进行如下配置以使 `.tsx` / `.jsx` 转换为客户端组件: - -```ts -import { defineConfig } from 'vitest/config' - -export default defineConfig({ - test: { - transformMode: { - web: [/\.[jt]sx$/], - }, - }, -}) -``` - ### snapshotFormat - **类型:** `PrettyFormatOptions` -测试快照的格式选项。这些选项被传递给 [`pretty-format`](https://www.npmjs.com/package/pretty-format)。 +快照测试的格式选项。这些选项被传递给 [`pretty-format`](https://www.npmjs.com/package/pretty-format)。 ::: tip 请注意,此对象上的 `plugins` 字段将被忽略。 @@ -1904,17 +1764,17 @@ export default defineConfig({ 如果你需要通过 pretty-format 插件扩展快照序列器,请使用 [`expect.addSnapshotSerializer`](/api/expect#expect-addsnapshotserializer) 或 [snapshotSerializers](#snapshotserializers) 选项。 ::: -### snapshotSerializers {#snapshotserializers} +### snapshotSerializers 1.3.0 {#snapshotserializers} - **类型:** `string[]` - **默认值:** `[]` -快照测试的快照序列化程序模块的路径列表,如果要添加自定义快照序列化器,则非常有用。有关详细信息,请参阅[自定义序列化器](/guide/snapshot#custom-serializer)。 +快照测试的快照序列化程序模块的路径列表,如果要添加自定义快照序列化器,则非常有用。更多内容请参阅 [自定义序列化器](/guide/snapshot#custom-serializer)。 ### resolveSnapshotPath -- **类型**: `(testPath: string, snapExtension: string) => string` -- **默认值**: 存储快照文件在 `__snapshots__` 目录 +- **类型**: `(testPath: string, snapExtension: string, context: { config: SerializedConfig }) => string` +- **默认值**: 将快照文件存储在 `__snapshots__` 目录中 覆盖快照的默认路径。例如,要在测试文件旁边存储一下快照: @@ -1997,7 +1857,7 @@ export default defineConfig({ 如果你决定处理 CSS 文件,你可以配置 CSS 模块中的类名是否在限定范围内。 默认情况下,Vitest 会导出一个代理,绕过 CSS 模块处理。 你可以选择以下选项之一: - `stable`: 类名将生成为`_${name}_${hashedFilename}`,这意味着如果 CSS 内容发生变化,生成的类将保持不变,但如果文件名被修改,或者文件名将发生变化 被移动到另一个文件夹。 如果你使用快照功能,此设置很有用。 -- `scoped`: 类名将照常生成,遵照 `css.modules.generateScopeName` 方法,如果你有的话。 默认情况下,文件名将生成为`_${name}_${hash}`,其中 hash 包括文件名和文件内容。 +- `scoped`: 类名将照常生成,遵照 `css.modules.generateScopedName` 方法,如果你有的话。 默认情况下,文件名将生成为`_${name}_${hash}`,其中 hash 包括文件名和文件内容。 - `non-scoped`: 类名将保留 CSS 中定义的名称。 ::: warning @@ -2021,26 +1881,6 @@ export default defineConfig({ 如果要禁用缓存功能,请使用此选项。目前,Vitest 会对测试结果进行缓存,优先运行时间较长和失败的测试。 -缓存目录由 Vite 的 [`cacheDir`](https://vitejs.dev/config/shared-options.html#cachedir) 选项控制: - -```ts -import { defineConfig } from 'vitest/config' - -export default defineConfig({ - cacheDir: 'custom-folder/.vitest' -}) -``` - -您可以使用 `process.env.VITEST` 来限制目录,使其仅用于 Vitest: - -```ts -import { defineConfig } from 'vitest/config' - -export default defineConfig({ - cacheDir: process.env.VITEST ? 'custom-folder/.vitest' : undefined -}) -``` - ### sequence - **类型**: `{ sequencer?, shuffle?, seed?, hooks?, setupFiles? }` @@ -2070,29 +1910,25 @@ npx vitest --sequence.shuffle --sequence.seed=1000 如果你希望测试随机运行,可以使用此选项或 CLI 参数 [`--sequence.shuffle`](/guide/cli) 启用它。 -Vitest 通常使用缓存对测试进行排序,因此长时间运行的测试会更早开始 - 这会使测试运行得更快。 如果你的测试将以随机顺序运行,你将失去这种性能改进,但跟踪意外依赖于先前运行的测试可能很有用。 +Vitest 通常使用缓存对测试进行排序,因此长时间运行的测试会更早开始,这会使测试运行得更快。如果你的测试将以随机顺序运行,你将失去这种性能改进,但跟踪意外依赖于先前运行的测试可能很有用。 -- **类型**: `boolean | { files?, tests? }` -- **默认值**: `false` -- **命令行终端**: `--sequence.shuffle`, `--sequence.shuffle=false` - -#### sequence.shuffle.files {#sequence-shuffle-files} +#### sequence.shuffle.files 1.4.0 {#sequence-shuffle-files} - **类型**: `boolean` - **默认值**: `false` - **命令行终端**: `--sequence.shuffle.files`, `--sequence.shuffle.files=false` -是否随机化文件,注意如果启用此选项,长时间运行的测试将不会提前启动。 +是否随机执行测试文件顺序。请注意,若启用此选项,耗时较长的测试将不会提前开始执行。 -#### sequence.shuffle.tests {#sequence-shuffle-tests} +#### sequence.shuffle.tests 1.4.0 {#sequence-shuffle-tests} - **类型**: `boolean` - **默认值**: `false` - **命令行终端**: `--sequence.shuffle.tests`, `--sequence.shuffle.tests=false` -是否随机测试。 +是否随机执行测试顺序。 -#### sequence.concurrent {#sequence-concurrent} +#### sequence.concurrent 0.32.2 {#sequence-concurrent} - **类型**: `boolean` - **默认值**: `false` @@ -2111,7 +1947,7 @@ Vitest 通常使用缓存对测试进行排序,因此长时间运行的测试 #### sequence.hooks - **类型**: `'stack' | 'list' | 'parallel'` -- **默认值**: `'parallel'` +- **默认值**: `'stack'` - **命令行终端**: `--sequence.hooks=` 更改钩子的执行顺序。 @@ -2124,7 +1960,7 @@ Vitest 通常使用缓存对测试进行排序,因此长时间运行的测试 该选项不会影响 [`onTestFinished`](/api/#ontestfinished)。它总是以相反的顺序调用。 ::: -#### sequence.setupFiles {#sequence-setupfiles} +#### sequence.setupFiles 0.29.3 {#sequence-setupfiles} - **类型**: `'list' | 'parallel'` - **默认值**: `'parallel'` @@ -2137,17 +1973,17 @@ Vitest 通常使用缓存对测试进行排序,因此长时间运行的测试 ### typecheck -用于配置 [typechecking](/guide/testing-types) 测试环境的选项。 +[类型测试](/guide/testing-types) 测试环境的配置选项。 -#### typecheck.enabled {#typecheck-enabled} +#### typecheck.enabled 1.0.0 {#typecheck-enabled} - **类型**: `boolean` - **默认值**: `false` - **命令行终端**: `--typecheck`, `--typecheck.enabled` -常规测试时是否进行类型检查。 +在常规测试的同时启用类型检查。 -#### typecheck.only {#typecheck-only} +#### typecheck.only 1.0.0 {#typecheck-only} - **类型**: `boolean` - **默认值**: `false` @@ -2212,9 +2048,9 @@ Vitest 通常使用缓存对测试进行排序,因此长时间运行的测试 - **默认值**: `300` - **命令行终端:**:`--slow-test-threshold=`, `--slowTestThreshold=` -如果测试被认为是缓慢的,那么会在报告结果中显示毫秒值。 +测试或测试套件执行超过该毫秒数即被视为缓慢,并在结果中相应标注。 -### chaiConfig {#chaiconfig} +### chaiConfig - **类型:** `{ includeStack?, showDiff?, truncateThreshold? }` - **默认值:** `{ includeStack: false, showDiff: true, truncateThreshold: 40 }` @@ -2244,7 +2080,7 @@ Vitest 通常使用缓存对测试进行排序,因此长时间运行的测试 此配置选项影响在 `test.each` 标题和断言错误消息中截断值的方式。 -### bail {#bail} +### bail 0.31.0 {#bail} - **类型:** `number` - **默认值:** `0` @@ -2254,7 +2090,7 @@ Vitest 通常使用缓存对测试进行排序,因此长时间运行的测试 默认情况下,即使其中一些测试失败,Vitest 也会运行你的所有测试用例。这可能不适用于 CI 构建,你只对 100% 成功的构建感兴趣,并且希望在测试失败时尽早停止测试执行。`bail` 选项可用于通过在发生故障时防止运行更多测试来加速 CI 运行。 -### retry {#retry} +### retry 0.32.3 {#retry} - **类型:** `number` - **默认值:** `0` @@ -2266,7 +2102,7 @@ Vitest 通常使用缓存对测试进行排序,因此长时间运行的测试 - **类型**: `(log: string, type: 'stdout' | 'stderr') => boolean | void` -在测试自定义 `console.log` 的处理程序。如果返回 `false`,Vitest 将不会将日志打印到控制台上。 +用于自定义处理测试中调用的 `console` 方法。如果返回值是 `false` , Vitest 将不会将日志打印到控制台。需要注意的是, Vitest 会忽略除 `false` 之外的其他假值。 这在过滤掉来自第三方库的日志时会非常有用。 @@ -2282,7 +2118,7 @@ export default defineConfig({ }) ``` -### onStackTrace {#onstacktrace} +### onStackTrace 1.0.0 {#onstacktrace} - **类型**: `(error: Error, frame: ParsedStack) => boolean | void` @@ -2297,12 +2133,12 @@ import { defineConfig } from 'vitest/config' export default defineConfig({ test: { onStackTrace(error: Error, { file }: ParsedStack): boolean | void { - // If we've encountered a ReferenceError, show the whole stack. + // 如果我们遇到引用错误,显示整个堆栈。 if (error.name === 'ReferenceError') { return } - // Reject all frames from third party libraries. + // 拒绝来自第三方库的所有框架。 if (file.includes('node_modules')) { return false } @@ -2311,14 +2147,14 @@ export default defineConfig({ }) ``` -### diff +### diff 0.34.5 {#diff} - **类型:** `string` -- **命令行终端:** `--diff=` +- **命令行终端:** `--diff=` -生成差异界面时使用的不同配置的路径。如果你想自定义差异显示,这将非常有用。 +用于生成差异对比界面的配置文件路径。如需自定义差异显示效果,可通过此配置实现。 -:::code-group +::: code-group ```ts [vitest.diff.ts] import type { DiffOptions } from 'vitest' @@ -2331,16 +2167,15 @@ export default { } satisfies DiffOptions ``` -```ts [vitest.config.js] +```js [vitest.config.js] import { defineConfig } from 'vitest/config' export default defineConfig({ test: { - diff: './vitest.diff.ts', - }, + diff: './vitest.diff.ts' + } }) ``` - ::: #### diff.truncateThreshold @@ -2381,7 +2216,7 @@ export default defineConfig({ #### fakeTimers.toFake - **类型:** `('setTimeout' | 'clearTimeout' | 'setImmediate' | 'clearImmediate' | 'setInterval' | 'clearInterval' | 'Date' | 'nextTick' | 'hrtime' | 'requestAnimationFrame' | 'cancelAnimationFrame' | 'requestIdleCallback' | 'cancelIdleCallback' | 'performance' | 'queueMicrotask')[]` -- **默认值:** `['setTimeout', 'clearTimeout', 'setImmediate', 'clearImmediate', 'setInterval', 'clearInterval', 'Date']` +- **默认值:** 除 `nextTick` 和 `queueMicrotask` 外的所有全局可用方法 包含要伪造的全局方法和 API 名称的数组。 @@ -2413,19 +2248,19 @@ export default defineConfig({ #### fakeTimers.shouldClearNativeTimers - **类型:** `boolean` -- **默认值:** `false` +- **默认值:** `true` -通过委托各自的处理程序,告诉假冒计时器清除 "native"(即非假冒)计时器。这些计时器默认情况下不会被清除,如果计时器在假计时器会话启动前就已存在,则可能会导致意外行为。 +通过委托各自的处理程序,告诉假冒计时器清除 "native"(即非假冒)计时器。禁用时,如果计时器在启动假计时器会话之前已经存在,则可能导致意外行为。 -### workspace {#workspace} +### workspace 1.1.0 {#workspace} -- **类型:** `string` +- **类型:** `string | TestProjectConfiguration` - **命令行终端:** `--workspace=./file.js` -- **默认值:** `vitest.{workspace,projects}.{js,ts,json}` close to the config file or root +- **默认值:** 配置文件或根目录附近的 `vitest.{workspace,projects}.{js,ts,json}` -相对于[root](#root) 的 [workspace](/guide/workspace) 配置文件的路径。 +相对于 [root](#root) 的 [workspace](/guide/projects) 配置文件的路径。 -### isolate +### isolate 1.1.0 {#isolate} - **类型:** `boolean` - **默认值:** `true` @@ -2433,37 +2268,32 @@ export default defineConfig({ 在隔离的环境中运行测试。此选项对 `vmThreads` 和 `vmForks` 池没有影响。 -如果你的代码不依赖于副作用(对于具有 `node` 环境的项目通常如此),禁用此选项可能会[改进性能](/guide/improving-performance)。 +如果你的代码不依赖于副作用(这在 `node` 环境的项目通常如此),禁用此选项可能会 [性能提升](/guide/improving-performance)。 ::: tip 你可以使用 [`poolOptions`](#poolOptions) 属性禁用特定池的隔离。 ::: -### includeTaskLocation {#includeTaskLocation} +### includeTaskLocation 1.4.0{#includeTaskLocation} - **类型:** `boolean` - **默认值:** `false` -Vitest API 在 [reporters](#reporters) 中接收任务时是否应包含`location`属性。如果您有大量测试,这可能会导致性能小幅下降。 +Vitest API 在 [reporters](#reporters) 中接收任务时是否应包含 `location` 属性。如果我们有大量测试,这可能会导致性能小幅下降。 `location` 属性的 `列` 和 `行` 值与原始文件中的 `test` 或 `describe` 位置相对应。 -如果您没有明确禁用该选项,并且在运行 Vitest 时使用了该选项,则该选项将自动启用: -- [Vitest UI](/guide/ui) -- 或使用不带 [headless](/guide/browser/#headless) 模式的 [浏览器模式](/guide/browser/) -- 或使用[HTML 报告器](/guide/reporters#html-reporter) - ::: tip 如果不使用依赖于该选项的自定义代码,该选项将不起作用。 ::: -### snapshotEnvironment {#snapshotEnvironment} +### snapshotEnvironment 1.6.0 {#snapshotEnvironment} - **类型:** `string` 自定义快照环境实现的路径。如果在不支持 Node.js API 的环境中运行测试,该选项将非常有用。此选项对浏览器运行程序没有任何影响。 -该对象应具有 `SnapshotEnvironment` 的形状,用于解析和读/写快照文件: +该对象应具有 `SnapshotEnvironment` 的接口规范,用于解析和读/写快照文件: ```ts export interface SnapshotEnvironment { @@ -2482,54 +2312,5 @@ export interface SnapshotEnvironment { ::: warning 这是一个低级选项,仅适用于无法访问默认 Node.js API 的高级情况。 -如果只需要配置快照功能,请使用 [`snapshotFormat`](#snapshotformat)或 [`resolveSnapshotPath`](#resolvesnapshotpath)选项。 -::: - -### env {#env} - -- **类型:** `Partial` - -测试期间在 `process.env` 和 `import.meta.env` 中可用的环境变量。这些变量在主进程中不可用(例如在 `globalSetup` 中)。 - -### expect - -- **类型:** `ExpectOptions` - -#### expect.requireAssertions - -- **类型:** `boolean` -- **默认值:** `false` - -与每次测试开始时调用 [`expect.hasAssertions()`](/api/expect#expect-hasassertions) 相同。这可确保不会意外通过任何测试。 - -::: tip -这仅适用于 Vitest 的`expect`。如果您使用`assert`或`.should`断言,它们将不计算在内,并且您的测试将因缺少 expect 断言而失败。 - -您可以通过调用 `vi.setConfig({ expect: { requireAssertions: false } })` 来更改此值。该配置将应用于每个后续 `expect` 调用,直到手动调用 `vi.resetConfig`。 +如果只需要配置快照功能,请使用 [`snapshotFormat`](#snapshotformat) 或 [`resolveSnapshotPath`](#resolvesnapshotpath) 选项。 ::: - -#### expect.poll - -[`expect.poll`](/api/expect#poll) 的全局配置选项。这些选项与您可以传递给 `expect.poll(condition, options)` 的选项相同。 - -##### expect.poll.interval - -- **类型:** `number` -- **默认值:** `50` - -轮询间隔(以毫秒为单位) - -##### expect.poll.timeout - -- **类型:** `number` -- **默认值:** `1000` - -轮询超时时间(以毫秒为单位) - -### printConsoleTrace - -- **类型:** `boolean` -- **默认值:** `false` - -调用任何`console`方法时始终打印控制台跟踪。这对于调试很有用。 - diff --git a/eslint.config.js b/eslint.config.js index 406c82fd..2143715c 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,4 +1,4 @@ -import antfu from '@antfu/eslint-config' +import antfu, { GLOB_SRC } from '@antfu/eslint-config' export default antfu({ stylistic: true, @@ -14,9 +14,44 @@ export default antfu({ '*.d.ts', 'coverage', '!.vitepress', + 'guide/examples/*.md', ], rules: { - 'no-restricted-globals': 'off', + // prefer global Buffer to not initialize the whole module + 'node/prefer-global/buffer': 'off', + 'node/prefer-global/process': 'off', 'no-empty-pattern': 'off', + 'antfu/indent-binary-ops': 'off', + 'unused-imports/no-unused-imports': 'error', + 'style/member-delimiter-style': [ + 'error', + { + multiline: { delimiter: 'none' }, + singleline: { delimiter: 'semi' }, + }, + ], + + 'ts/no-invalid-this': 'off', + + // TODO: migrate and turn it back on + 'ts/ban-types': 'off', + + 'no-restricted-imports': [ + 'error', + { + paths: ['path'], + }, + ], + + 'import/no-named-as-default': 'off', + }, +}, { + files: [`**/*.md`, `**/*.md/${GLOB_SRC}`], + rules: { + 'style/max-statements-per-line': 'off', + 'import/newline-after-import': 'off', + 'import/first': 'off', + 'no-restricted-globals': 'off', + 'unused-imports/no-unused-imports': 'off', }, }) diff --git a/guide/browser.md b/guide/browser.md new file mode 100644 index 00000000..e880b347 --- /dev/null +++ b/guide/browser.md @@ -0,0 +1,128 @@ +--- +title: 浏览器模式 | 指南 +--- + +# 浏览器模式 实验性 {#browser-mode} + +此页面提供有关 Vitest API 中实验性浏览器模式功能的信息,该功能允许你在浏览器中本地运行测试,提供对 window 和 document 等浏览器全局变量的访问。此功能目前正在开发中,API 未来可能会更改。 + +## 浏览器兼容性 {#browser-compatibility} + +Vitest 使用 [Vite 开发服务器](https://cn.vitejs.dev/guide/#browser-support) 来运行您的测试,因此我们只支持 [`esbuild.target`](https://cn.vitejs.dev/config/shared-options#esbuild)选项(默认为 `esnext`)中指定的功能。 + +默认情况下,Vite 的目标浏览器支持本地 [ES Modules](https://caniuse.com/es6-module)、原生 [ESM dynamic import](https://caniuse.com/es6-module-dynamic-import) 和 [`import.meta`](https://caniuse.com/mdn-javascript_operators_import_meta)。此外,我们还利用 [`BroadcastChannel`](https://caniuse.com/?search=BroadcastChannel)在 iframe 之间进行通信: + +- Chrome >=87 +- Firefox >=78 +- Safari >=15.4 +- Edge >=88 + +## 动机 {#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 浏览器在初始化过程中需要启动提供程序和浏览器,这可能需要一些时间。与其他测试模式相比,这可能导致更长的初始化时间。 + +## 配置 {#configuration} + +要在 Vitest 配置中启用浏览器模式,你可以使用 `--browser` 标志或在你的 Vitest 配置文件中将 `browser.enabled` 字段设置为 `true`。这是使用浏览器字段的示例配置: + +```ts +export default defineConfig({ + test: { + browser: { + enabled: true, + name: 'chrome', // 浏览器名称必填 + }, + }, +}) +``` + +## 浏览器选项类型 {#browser-option-types} + +Vitest 中的浏览器选项取决于提供者。如果你传递 `--browser` 并且未在配置文件中指定其名称,Vitest 将失败。可用选项: + +- `webdriverio` (默认) 支持以下浏览器: + - `firefox` + - `chrome` + - `edge` + - `safari` +- `playwright` 支持以下浏览器: + - `firefox` + - `webkit` + - `chromium` + +## 跨浏览器测试 {#cross-browser-testing} + +当你在浏览器选项中指定浏览器名称时,Vitest 将默认尝试使用 [WebdriverIO](https://webdriver.io/) 运行指定的浏览器,然后在那里运行测试。此功能使跨浏览器测试易于在 CI 等环境中使用和配置。如果不想使用 WebdriverIO,可以使用 `browser.provider` 选项配置自定义浏览器提供程序。 + +要使用 CLI 指定浏览器,请使用 `--browser` 标志后跟浏览器名称,如下所示: + +```sh +npx vitest --browser=chrome +``` + +或者你可以使用点符号向 CLI 提供浏览器选项: + +```sh +npx vitest --browser.name=chrome --browser.headless +``` + +::: tip NOTE +当使用带有 WebdriverIO 的 Safari 浏览器选项时,需要通过在你的设备上运行 `sudo safaridriver --enable` 来激活`safaridriver`。 + +此外,在运行测试时,Vitest 将尝试安装一些驱动程序用于兼容 `safaridriver`。 +::: + +## 无头模式 {#headless} + +无头模式是浏览器模式下可用的另一个选项。在无头模式下,浏览器在没有用户界面的情况下在后台运行,这对于运行自动化测试非常有用。Vitest 中的 headless 选项可以设置为布尔值以启用或禁用无头模式。 + +这是启用无头模式的示例配置: + +```ts +export default defineConfig({ + test: { + browser: { + enabled: true, + headless: true, + }, + }, +}) +``` + +你还可以在 CLI 中使用 `--browser.headless` 标志设置无头模式,如下所示: + +```sh +npx vitest --browser.name=chrome --browser.headless +``` + +在这种情况下,Vitest 将使用 Chrome 浏览器以 headless 模式运行。 + +## 限制 {#limitations} + +### 线程阻塞对话框 {#thread-blocking-dialogs} + +使用 Vitest 浏览器时,需要注意的是像 `alert` 或 `confirm` 这样的线程阻塞对话框不能在本地使用。这是因为它们阻塞了网页,这意味着 Vitest 无法继续与该页面通信,导致执行挂起。 + +在这种情况下,Vitest 为这些 API 提供默认模拟和默认返回值。这确保如果用户不小心使用了同步弹出式 Web API,执行不会挂起。但是,仍然建议用户模拟这些 Web API 以获得更好的体验。在 [模拟](/guide/mocking) 中阅读更多内容。 diff --git a/guide/browser/assertion-api.md b/guide/browser/assertion-api.md deleted file mode 100644 index 790bc38d..00000000 --- a/guide/browser/assertion-api.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: Assertion API | Browser Mode ---- - -# Assertion API - -Vitest 捆绑了 [`@testing-library/jest-dom`](https://github.com/testing-library/jest-dom)库,以提供各种开箱即用的 DOM 断言。有关详细文档,请阅读 `jest-dom` readme: - -- [`toBeDisabled`](https://github.com/testing-library/jest-dom#toBeDisabled) -- [`toBeEnabled`](https://github.com/testing-library/jest-dom#toBeEnabled) -- [`toBeEmptyDOMElement`](https://github.com/testing-library/jest-dom#toBeEmptyDOMElement) -- [`toBeInTheDocument`](https://github.com/testing-library/jest-dom#toBeInTheDocument) -- [`toBeInvalid`](https://github.com/testing-library/jest-dom#toBeInvalid) -- [`toBeRequired`](https://github.com/testing-library/jest-dom#toBeRequired) -- [`toBeValid`](https://github.com/testing-library/jest-dom#toBeValid) -- [`toBeVisible`](https://github.com/testing-library/jest-dom#toBeVisible) -- [`toContainElement`](https://github.com/testing-library/jest-dom#toContainElement) -- [`toContainHTML`](https://github.com/testing-library/jest-dom#toContainHTML) -- [`toHaveAccessibleDescription`](https://github.com/testing-library/jest-dom#toHaveAccessibleDescription) -- [`toHaveAccessibleErrorMessage`](https://github.com/testing-library/jest-dom#toHaveAccessibleErrorMessage) -- [`toHaveAccessibleName`](https://github.com/testing-library/jest-dom#toHaveAccessibleName) -- [`toHaveAttribute`](https://github.com/testing-library/jest-dom#toHaveAttribute) -- [`toHaveClass`](https://github.com/testing-library/jest-dom#toHaveClass) -- [`toHaveFocus`](https://github.com/testing-library/jest-dom#toHaveFocus) -- [`toHaveFormValues`](https://github.com/testing-library/jest-dom#toHaveFormValues) -- [`toHaveStyle`](https://github.com/testing-library/jest-dom#toHaveStyle) -- [`toHaveTextContent`](https://github.com/testing-library/jest-dom#toHaveTextContent) -- [`toHaveValue`](https://github.com/testing-library/jest-dom#toHaveValue) -- [`toHaveDisplayValue`](https://github.com/testing-library/jest-dom#toHaveDisplayValue) -- [`toBeChecked`](https://github.com/testing-library/jest-dom#toBeChecked) -- [`toBePartiallyChecked`](https://github.com/testing-library/jest-dom#toBePartiallyChecked) -- [`toHaveRole`](https://github.com/testing-library/jest-dom#toHaveRole) -- [`toHaveErrorMessage`](https://github.com/testing-library/jest-dom#toHaveErrorMessage) - -如果使用 TypeScript 或希望在 `expect` 中获得正确的类型提示,请确保根据使用的提供程序,在 `tsconfig` 中指定了 `@vitest/browser/providers/playwright` 或 `@vitest/browser/providers/webdriverio`。如果使用默认的 `preview` 提供程序,则可指定 `@vitest/browser/matchers` 代替。 - -::: code-group -```json [preview] -{ - "compilerOptions": { - "types": [ - "@vitest/browser/matchers" - ] - } -} -``` -```json [playwright] -{ - "compilerOptions": { - "types": [ - "@vitest/browser/providers/playwright" - ] - } -} -``` -```json [webdriverio] -{ - "compilerOptions": { - "types": [ - "@vitest/browser/providers/webdriverio" - ] - } -} -``` -::: diff --git a/guide/browser/commands.md b/guide/browser/commands.md deleted file mode 100644 index 01ced276..00000000 --- a/guide/browser/commands.md +++ /dev/null @@ -1,181 +0,0 @@ ---- -title: Commands | Browser Mode -outline: deep ---- - -# Commands - -命令是一个函数,它调用服务器上的另一个函数并将结果传递回浏览器。Vitest 公开了几个可以在浏览器测试中使用的内置命令。 - -## 内置命令 - -### 文件处理 - -你可以使用 `readFile` 、`writeFile` 和 `removeFile` API 来处理浏览器测试中的文件。所有路径都是相对于测试文件解析的,即使它们是在位于另一个文件中的辅助函数中调用的。 - -默认情况下,Vitest 使用 `utf-8` 编码,但你可以使用选项覆盖它。 - -::: tip -此 API 遵循 [`server.fs`](https://vitejs.dev/config/server-options.html#server-fs-allow) 出于安全原因的限制。 -::: - -```ts -import { server } from '@vitest/browser/context' - -const { readFile, writeFile, removeFile } = server.commands - -it('handles files', async () => { - const file = './test.txt' - - await writeFile(file, 'hello world') - const content = await readFile(file) - - expect(content).toBe('hello world') - - await removeFile(file) -}) -``` - -## CDP Session - -Vitest 通过 `@vitest/browser/context` 中导出的 `cdp` 方法访问原始 Chrome Devtools 协议。它主要用于库作者在其基础上构建工具。 - -```ts -import { cdp } from '@vitest/browser/context' - -const input = document.createElement('input') -document.body.appendChild(input) -input.focus() - -await cdp().send('Input.dispatchKeyEvent', { - type: 'keyDown', - text: 'a', -}) - -expect(input).toHaveValue('a') -``` - -::: warning -CDP session仅适用于 `playwright` provider,并且仅在使用 `chromium` 浏览器时有效。有关详细信息,请参阅 playwright 的 [`CDPSession`](https://playwright.dev/docs/api/class-cdpsession)文档。 -::: - -## Custom Commands - -你也可以通过 [`browser.commands`](/config/#browser-commands) 配置选项添加自己的命令。如果你开发了一个库,你可以通过插件内的 `config` 钩子来提供它们: - -```ts -import type { Plugin } from 'vitest/config' -import type { BrowserCommand } from 'vitest/node' - -const myCustomCommand: BrowserCommand<[arg1: string, arg2: string]> = ({ - testPath, - provider -}, arg1, arg2) => { - if (provider.name === 'playwright') { - console.log(testPath, arg1, arg2) - return { someValue: true } - } - - throw new Error(`provider ${provider.name} is not supported`) -} - -export default function BrowserCommands(): Plugin { - return { - name: 'vitest:custom-commands', - config() { - return { - test: { - browser: { - commands: { - myCustomCommand, - } - } - } - } - } - } -} -``` - -然后,你可以通过从 `@vitest/brower/context` 导入它,在测试中调用它: - -```ts -import { commands } from '@vitest/browser/context' -import { expect, test } from 'vitest' - -test('custom command works correctly', async () => { - const result = await commands.myCustomCommand('test1', 'test2') - expect(result).toEqual({ someValue: true }) -}) - -// if you are using TypeScript, you can augment the module -declare module '@vitest/browser/context' { - interface BrowserCommands { - myCustomCommand: (arg1: string, arg2: string) => Promise<{ - someValue: true - }> - } -} -``` - -::: warning -如果自定义命令具有相同的名称,则它们将覆盖内置命令。 -::: - -### 自定义命令 `playwright` - -Vitest 在命令上下文中公开了几个`playwright`特定属性。 - -- `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)。 - -```ts -import { defineCommand } from '@vitest/browser' - -export const myCommand = defineCommand(async (ctx, arg1, arg2) => { - if (ctx.provider.name === 'playwright') { - const element = await ctx.iframe.findByRole('alert') - const screenshot = await element.screenshot() - // do something with the screenshot - return difference - } -}) -``` - -::: tip -如果您使用的是 TypeScript,请不要忘记将 `@vitest/browser/providers/playwright` 添加到您的 `tsconfig` "compilerOptions.types" 字段,以便在配置中以及 `userEvent` 和 `page` 选项中获得自动完成功能: - -```json -{ - "compilerOptions": { - "types": [ - "@vitest/browser/providers/playwright" - ] - } -} -``` -::: - -### 自定义命令 `webdriverio` - -Vitest 在上下文对象上公开了一些 `webdriverio` 特有属性。 - -- `browser` 是 `WebdriverIO.Browser` API. - -Vitest 通过在调用命令前调用 `browser.switchToFrame` 自动将 `webdriver` 上下文切换到测试 iframe,因此 `$` 和 `$` 方法将引用 iframe 内的元素,而不是 orchestrator 中的元素,但非 Webdriver API 仍将引用 parent frame 上下文。 - -::: tip -如果您使用的是 TypeScript,请不要忘记将 `@vitest/browser/providers/webdriverio` 添加到您的 `tsconfig` "compilerOptions.types" 字段,以获得自动完成功能: - -```json -{ - "compilerOptions": { - "types": [ - "@vitest/browser/providers/webdriverio" - ] - } -} -``` -::: diff --git a/guide/browser/context.md b/guide/browser/context.md deleted file mode 100644 index cd429990..00000000 --- a/guide/browser/context.md +++ /dev/null @@ -1,87 +0,0 @@ ---- -title: Context | Browser Mode ---- - -# 上下文 - -Vitest 通过 `@vitest/browser/context` 入口点公开上下文模块。从 2.0 开始,它公开了一小部分实用程序,这些实用程序可能在测试中对你有用。 - -```ts -export const server: { - /** - * Platform the Vitest server is running on. - * The same as calling `process.platform` on the server. - */ - platform: Platform - /** - * Runtime version of the Vitest server. - * The same as calling `process.version` on the server. - */ - version: string - /** - * Name of the browser provider. - */ - provider: string - /** - * Name of the current browser. - */ - browser: string - /** - * Available commands for the browser. - */ - commands: BrowserCommands -} - -/** - * Handler for user interactions. The support is implemented by the browser provider (`playwright` or `webdriverio`). - * If used with `preview` provider, fallbacks to simulated events via `@testing-library/user-event`. - * @experimental - */ -export const userEvent: { - setup: () => UserEvent - click: (element: Element, options?: UserEventClickOptions) => Promise - dblClick: (element: Element, options?: UserEventDoubleClickOptions) => Promise - tripleClick: (element: Element, options?: UserEventTripleClickOptions) => Promise - selectOptions: ( - element: Element, - values: HTMLElement | HTMLElement[] | string | string[], - options?: UserEventSelectOptions, - ) => Promise - keyboard: (text: string) => Promise - type: (element: Element, text: string, options?: UserEventTypeOptions) => Promise - clear: (element: Element) => Promise - tab: (options?: UserEventTabOptions) => Promise - hover: (element: Element, options?: UserEventHoverOptions) => Promise - unhover: (element: Element, options?: UserEventHoverOptions) => Promise - fill: (element: Element, text: string, options?: UserEventFillOptions) => Promise - dragAndDrop: (source: Element, target: Element, options?: UserEventDragAndDropOptions) => Promise -} - -/** - * Available commands for the browser. - * A shortcut to `server.commands`. - */ -export const commands: BrowserCommands - -export const page: { - /** - * Serialized test config. - */ - config: ResolvedConfig - /** - * Change the size of iframe's viewport. - */ - viewport(width: number, height: number): Promise - /** - * Make a screenshot of the test iframe or a specific element. - * @returns Path to the screenshot file or path and base64. - */ - screenshot(options: Omit & { base64: true }): Promise<{ - path: string - base64: string - }> - screenshot(options?: ScreenshotOptions): Promise -} - -export const cdp: () => CDPSession -``` diff --git a/guide/browser/examples.md b/guide/browser/examples.md deleted file mode 100644 index 7952764d..00000000 --- a/guide/browser/examples.md +++ /dev/null @@ -1,158 +0,0 @@ ---- -title: Examples | Browser Mode ---- - -# Examples - -浏览器模式与框架无关,因此不提供任何渲染组件的方法。不过,你应该可以使用框架的测试工具包。 - -我们建议根据您的框架使用 `testing-library` packages: - -- [`@testing-library/dom`](https://testing-library.com/docs/dom-testing-library/intro) if you don't use a framework -- [`@testing-library/vue`](https://testing-library.com/docs/vue-testing-library/intro) to render [vue](https://vuejs.org) components -- [`@testing-library/svelte`](https://testing-library.com/docs/svelte-testing-library/intro) to render [svelte](https://svelte.dev) components -- [`@testing-library/react`](https://testing-library.com/docs/react-testing-library/intro) to render [react](https://react.dev) components -- [`@testing-library/preact`](https://testing-library.com/docs/preact-testing-library/intro) to render [preact](https://preactjs.com) components -- [`solid-testing-library`](https://testing-library.com/docs/solid-testing-library/intro) to render [solid](https://www.solidjs.com) components -- [`@marko/testing-library`](https://testing-library.com/docs/marko-testing-library/intro) to render [marko](https://markojs.com) components - -::: warning -`testing-library` 提供了一个包`@testing-library/user-event`。我们不建议直接使用它,因为它会模拟事件而非实际触发事件--相反,请使用从 `@vitest/browser/context`导入的 [`userEvent`](#interactivity-api),它使用 Chrome DevTools 协议或 Webdriver(取决于provider)。 -::: - -::: code-group -```ts [vue] -// based on @testing-library/vue example -// https://testing-library.com/docs/vue-testing-library/examples - -import { userEvent } from '@vitest/browser/context' -import { render, screen } from '@testing-library/vue' -import Component from './Component.vue' - -test('properly handles v-model', async () => { - render(Component) - - // Asserts initial state. - expect(screen.getByText('Hi, my name is Alice')).toBeInTheDocument() - - // Get the input DOM node by querying the associated label. - const usernameInput = await screen.findByLabelText(/username/i) - - // Type the name into the input. This already validates that the input - // is filled correctly, no need to check the value manually. - await userEvent.fill(usernameInput, 'Bob') - - expect(screen.getByText('Hi, my name is Alice')).toBeInTheDocument() -}) -``` -```ts [svelte] -// based on @testing-library/svelte -// https://testing-library.com/docs/svelte-testing-library/example - -import { render, screen } from '@testing-library/svelte' -import { userEvent } from '@vitest/browser/context' -import { expect, test } from 'vitest' - -import Greeter from './greeter.svelte' - -test('greeting appears on click', async () => { - const user = userEvent.setup() - render(Greeter, { name: 'World' }) - - const button = screen.getByRole('button') - await user.click(button) - const greeting = await screen.findByText(/hello world/iu) - - expect(greeting).toBeInTheDocument() -}) -``` -```tsx [react] -// based on @testing-library/react example -// https://testing-library.com/docs/react-testing-library/example-intro - -import { userEvent } from '@vitest/browser/context' -import { render, screen } from '@testing-library/react' -import Fetch from './fetch' - -test('loads and displays greeting', async () => { - // Render a React element into the DOM - render() - - await userEvent.click(screen.getByText('Load Greeting')) - // wait before throwing an error if it cannot find an element - const heading = await screen.findByRole('heading') - - // assert that the alert message is correct - expect(heading).toHaveTextContent('hello there') - expect(screen.getByRole('button')).toBeDisabled() -}) -``` -```tsx [preact] -// based on @testing-library/preact example -// https://testing-library.com/docs/preact-testing-library/example - -import { h } from 'preact' -import { userEvent } from '@vitest/browser/context' -import { render } from '@testing-library/preact' - -import HiddenMessage from '../hidden-message' - -test('shows the children when the checkbox is checked', async () => { - const testMessage = 'Test Message' - - const { queryByText, getByLabelText, getByText } = render( - {testMessage}, - ) - - // query* functions will return the element or null if it cannot be found. - // get* functions will return the element or throw an error if it cannot be found. - expect(queryByText(testMessage)).not.toBeInTheDocument() - - // The queries can accept a regex to make your selectors more - // resilient to content tweaks and changes. - await userEvent.click(getByLabelText(/show/i)) - - expect(getByText(testMessage)).toBeInTheDocument() -}) -``` -```tsx [solid] -// baed on @testing-library/solid API -// https://testing-library.com/docs/solid-testing-library/api - -import { render } from '@testing-library/solid' - -it('uses params', async () => { - const App = () => ( - <> - ( -

- Id: - {useParams()?.id} -

- )} - /> -

Start

} /> - - ) - const { findByText } = render(() => , { location: 'ids/1234' }) - expect(await findByText('Id: 1234')).toBeInTheDocument() -}) -``` -```ts [marko] -// baed on @testing-library/marko API -// https://testing-library.com/docs/marko-testing-library/api - -import { render, screen } from '@marko/testing-library' -import Greeting from './greeting.marko' - -test('renders a message', async () => { - const { container } = await render(Greeting, { name: 'Marko' }) - expect(screen.getByText(/Marko/)).toBeInTheDocument() - expect(container.firstChild).toMatchInlineSnapshot(` -

Hello, Marko!

- `) -}) -``` -::: diff --git a/guide/browser/index.md b/guide/browser/index.md deleted file mode 100644 index 4496afc7..00000000 --- a/guide/browser/index.md +++ /dev/null @@ -1,308 +0,0 @@ ---- -title: Browser Mode | Guide -outline: deep ---- - -# 浏览器模式 实验性 {#browser-mode} - -此页面提供有关 Vitest API 中实验性浏览器模式功能的信息,该功能允许你在浏览器中本地运行测试,提供对窗口和文档等浏览器全局变量的访问。此功能目前正在开发中,API 未来可能会更改。 - -## 安装 - -为方便设置,可使用 `vitest init browser` 命令安装所需的依赖项并创建浏览器配置。 - -::: code-group -```bash [npm] -npx vitest init browser -``` -```bash [yarn] -yarn exec vitest init browser -``` -```bash [pnpm] -pnpx vitest init browser -``` -```bash [bun] -bunx vitest init browser -``` -::: - -### 手动安装 - -您也可以手动安装软件包。默认情况下,浏览器模式不需要任何额外的 E2E provider 就能在本地运行测试,因为它会复用你现有的浏览器。 - -::: code-group -```bash [npm] -npm install -D vitest @vitest/browser -``` -```bash [yarn] -yarn add -D vitest @vitest/browser -``` -```bash [pnpm] -pnpm add -D vitest @vitest/browser -``` -```bash [bun] -bun add -D vitest @vitest/browser -``` -::: - -::: warning -不过,要在 CI 中运行测试,您需要安装 [`playwright`](https://npmjs.com/package/playwright) 或 [`webdriverio`](https://www.npmjs.com/package/webdriverio) 。我们还建议在本地测试时切换到这两个选项中的一个,而不是使用默认的 `preview` 提供程序,因为它依赖于模拟事件而不是使用 Chrome DevTools 协议。 -::: - -### 使用 Playwright - -::: code-group -```bash [npm] -npm install -D vitest @vitest/browser playwright -``` -```bash [yarn] -yarn add -D vitest @vitest/browser playwright -``` -```bash [pnpm] -pnpm add -D vitest @vitest/browser playwright -``` -```bash [bun] -bun add -D vitest @vitest/browser playwright -``` -::: - -### Using Webdriverio - -::: code-group -```bash [npm] -npm install -D vitest @vitest/browser webdriverio -``` -```bash [yarn] -yarn add -D vitest @vitest/browser webdriverio -``` -```bash [pnpm] -pnpm add -D vitest @vitest/browser webdriverio -``` -```bash [bun] -bun add -D vitest @vitest/browser webdriverio -``` -::: - -## 配置 - -要在 Vitest 配置中激活浏览器模式,可以使用 `--browser` 标志,或在 Vitest 配置文件中将 `browser.enabled` 字段设为 `true`。下面是一个使用浏览器字段的配置示例: - -```ts -export default defineConfig({ - test: { - browser: { - provider: 'playwright', // or 'webdriverio' - enabled: true, - name: 'chrome', // browser name is required - }, - } -}) -``` - -如果之前未使用过 Vite,请确保已安装框架插件并在配置中指定。有些框架可能需要额外配置才能运行,请查看其 Vite 相关文档以确定。 - -::: code-group -```ts [vue] -import { defineConfig } from 'vitest/config' -import vue from '@vitejs/plugin-vue' - -export default defineConfig({ - plugins: [vue()], - test: { - browser: { - enabled: true, - provider: 'playwright', - name: 'chrome', - } - } -}) -``` -```ts [svelte] -import { defineConfig } from 'vitest/config' -import { svelte } from '@sveltejs/vite-plugin-svelte' - -export default defineConfig({ - plugins: [svelte()], - test: { - browser: { - enabled: true, - provider: 'playwright', - name: 'chrome', - } - } -}) -``` -```ts [solid] -import { defineConfig } from 'vitest/config' -import solidPlugin from 'vite-plugin-solid' - -export default defineConfig({ - plugins: [solidPlugin()], - test: { - browser: { - enabled: true, - provider: 'playwright', - name: 'chrome', - } - } -}) -``` -```ts [marko] -import { defineConfig } from 'vitest/config' -import marko from '@marko/vite' - -export default defineConfig({ - plugins: [marko()], - test: { - browser: { - enabled: true, - provider: 'playwright', - name: 'chrome', - } - } -}) -``` -::: - -::: tip -`react` 不需要插件就能工作,但 `preact` 需要 [extra configuration](https://preactjs.com/guide/v10/getting-started/#create-a-vite-powered-preact-app) 才能使用别名 -::: - -如果需要使用基于 Node 的运行程序运行某些测试,可以定义一个 [workspace](/guide/workspace) 文件,为不同的测试策略分别配置: - -```ts -// vitest.workspace.ts -import { defineWorkspace } from 'vitest/config' - -export default defineWorkspace([ - { - test: { - // an example of file based convention, - // you don't have to follow it - include: [ - 'tests/unit/**/*.{test,spec}.ts', - 'tests/**/*.unit.{test,spec}.ts', - ], - name: 'unit', - environment: 'node', - }, - }, - { - test: { - // an example of file based convention, - // you don't have to follow it - include: [ - 'tests/browser/**/*.{test,spec}.ts', - 'tests/**/*.browser.{test,spec}.ts', - ], - name: 'browser', - browser: { - enabled: true, - name: 'chrome', - }, - }, - }, -]) -``` - -## 浏览器选项类型 - -Vitest 中的浏览器选项取决于provider。如果在配置文件中传递 `--browser` 且未指定其名称,则 Vitest 将失败。可用选项: -- `webdriverio` 支持这些浏览器: - - `firefox` - - `chrome` - - `edge` - - `safari` -- `playwright` 支持这些浏览器: - - `firefox` - - `webkit` - - `chromium` - -## 浏览器兼容性 - -Vitest 使用 [Vite dev server](https://cn.vitejs.dev/guide/#browser-support) 来运行您的测试,因此我们只支持 [`esbuild.target`](https://cn.vitejs.dev/config/shared-options#esbuild)选项(默认为 `esnext`)中指定的功能。 - -默认情况下,Vite 的目标浏览器支持本地 [ES Modules](https://caniuse.com/es6-module)、本地 [ESM dynamic import](https://caniuse.com/es6-module-dynamic-import) 和 [`import.meta`](https://caniuse.com/mdn-javascript_operators_import_meta)。此外,我们还利用 [`BroadcastChannel`](https://caniuse.com/?search=BroadcastChannel)在 iframe 之间进行通信: - -- Chrome >=87 -- Firefox >=78 -- Safari >=15.4 -- Edge >=88 - -## 动机 - -我们开发了 Vitest 浏览器模式功能,以帮助改进测试工作流程并实现更准确、可靠的测试结果。这个实验性的测试 API 增加了在本地浏览器环境中运行测试的功能。在本节中,我们将探讨这个功能背后的动机以及它对测试的好处。 - - -### 不同的测试方式 - -有不同的方法来测试 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/) 的情况下提供一个浏览器引擎。 - -### 模拟警告 - -在模拟环境(如 jsdom 或 happy-dom)中测试 JavaScript 程序简化了测试设置并提供了易于使用的 API,使它们适用于许多项目并增加了对测试结果的信心。然而,需要牢记的是,这些工具仅模拟浏览器环境而不是实际浏览器,这可能导致模拟环境和真实环境之间存在一些差异。因此,测试结果可能会出现误报或漏报。 - -为了在测试中获得最高的水平,测试在真实浏览器环境中进行非常重要。这就是为什么我们开发了 Vitest 的浏览器模式功能,允许开发者在浏览器中本地运行测试,并获得更准确、可靠的测试结果。通过浏览器级别的测试,开发者可以更加自信地确保他们的应用在真实场景中能够按照预期工作。t that their application will work as intended in a real-world scenario. - -## 缺点 - -使用 Vitest 浏览器时,重要的是要考虑以下缺点: - -### 更长的初始化时间 - -Vitest 浏览器在初始化过程中需要启动提供程序和浏览器,这可能需要一些时间。与其他测试模式相比,这可能导致更长的初始化时间。 - -## 跨浏览器测试 - -当你在浏览器选项中指定浏览器名称时,Vitest 将默认尝试使用 [WebdriverIO](https://webdriver.io/) 运行指定的浏览器,然后在那里运行测试。此功能使跨浏览器测试易于在 CI 等环境中使用和配置。如果不想使用 WebdriverIO,可以使用 `browser.provider` 选项配置自定义浏览器提供程序。 - -要使用 CLI 指定浏览器,请使用 `--browser` 标志后跟浏览器名称,如下所示: - -```sh -npx vitest --browser=chrome -``` - -或者你可以使用点符号向 CLI 提供浏览器选项: - -```sh -npx vitest --browser.name=chrome --browser.headless -``` - -## Headless - -headless 模式是浏览器模式下可用的另一个选项。在 headless 模式下,浏览器在没有用户界面的情况下在后台运行,这对于运行自动化测试非常有用。Vitest 中的 headless 选项可以设置为布尔值以启用或禁用 headless 模式。 - -这是启用 headless 模式的示例配置: - - -```ts -export default defineConfig({ - test: { - browser: { - provider: 'playwright', - enabled: true, - headless: true, - }, - } -}) -``` - -你还可以在 CLI 中使用 `--browser.headless` 标志设置 headless 模式,如下所示: - -```sh -npx vitest --browser.name=chrome --browser.headless -``` - -在这种情况下,Vitest 将使用 Chrome 浏览器以 headless 模式运行。 - -::: warning -默认情况下Headless模式不可用。您需要使用 [`playwright`](https://npmjs.com/package/playwright) 或 [`webdriverio`](https://www.npmjs.com/package/webdriverio) 提供程序来启用此功能。 -::: - -## 限制 - -### 线程阻塞对话框 - -使用 Vitest 浏览器时,需要注意的是像 `alert` 或 `confirm` 这样的线程阻塞对话框不能在本地使用。这是因为它们阻塞了网页,这意味着 Vitest 无法继续与该页面通信,导致执行挂起。 - -在这种情况下,Vitest 为这些 API 提供默认模拟和默认返回值。这确保如果用户不小心使用了同步弹出式 Web API,执行不会挂起。但是,仍然建议用户模拟这些 Web API 以获得更好的体验。在 [Mocking](/guide/mocking) 中阅读更多内容。 diff --git a/guide/browser/interactivity-api.md b/guide/browser/interactivity-api.md deleted file mode 100644 index d1709c91..00000000 --- a/guide/browser/interactivity-api.md +++ /dev/null @@ -1,388 +0,0 @@ ---- -title: Interactivity API | Browser Mode ---- - -# Interactivity API - -Vitest 使用 [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) 或 [webdriver](https://www.w3.org/TR/webdriver/) API 实现了 [`@testing-library/user-event`](https://testing-library.com/docs/user-event)应用程序接口的子集,而不是伪造事件,这使得浏览器行为更加可靠和一致。 - -几乎每个 `userEvent` 方法都继承了其provider选项。要在集成开发环境中查看所有可用选项,请在 `tsconfig.json` 文件中添加 `webdriver` 或 `playwright` 类型: - -::: code-group -```json [playwright] -{ - "compilerOptions": { - "types": [ - "@vitest/browser/providers/playwright" - ] - } -} -``` -```json [webdriverio] -{ - "compilerOptions": { - "types": [ - "@vitest/browser/providers/webdriverio" - ] - } -} -``` -::: - -## userEvent.click - -- **Type:** `(element: Element, options?: UserEventClickOptions) => Promise` - -点击元素。继承 provider 的选项。有关此方法如何工作的详细说明,请参阅 provider 的文档。 - -```ts -import { userEvent } from '@vitest/browser/context' -import { screen } from '@testing-library/dom' - -test('clicks on an element', async () => { - const logo = screen.getByRole('img', { name: /logo/ }) - - await userEvent.click(logo) -}) -``` - -References: - -- [Playwright `locator.click` API](https://playwright.dev/docs/api/class-locator#locator-click) -- [WebdriverIO `element.click` API](https://webdriver.io/docs/api/element/click/) -- [testing-library `click` API](https://testing-library.com/docs/user-event/convenience/#click) - -## userEvent.dblClick - -- **Type:** `(element: Element, options?: UserEventDoubleClickOptions) => Promise` - -触发元素的双击事件 - -请参阅你的 provider 的文档以获取有关此方法如何工作的详细说明。 - -```ts -import { userEvent } from '@vitest/browser/context' -import { screen } from '@testing-library/dom' - -test('triggers a double click on an element', async () => { - const logo = screen.getByRole('img', { name: /logo/ }) - - await userEvent.dblClick(logo) -}) -``` - -References: - -- [Playwright `locator.dblclick` API](https://playwright.dev/docs/api/class-locator#locator-dblclick) -- [WebdriverIO `element.doubleClick` API](https://webdriver.io/docs/api/element/doubleClick/) -- [testing-library `dblClick` API](https://testing-library.com/docs/user-event/convenience/#dblClick) - -## userEvent.tripleClick - -- **Type:** `(element: Element, options?: UserEventTripleClickOptions) => Promise` - -Triggers a triple click event on an element. Since there is no `tripleclick` in browser api, this method will fire three click events in a row, and so you must check [click event detail](https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event#usage_notes) to filter the event: `evt.detail === 3`. - -Please refer to your provider's documentation for detailed explanation about how this method works. - -```ts -import { userEvent } from '@vitest/browser/context' -import { screen } from '@testing-library/dom' - -test('triggers a triple click on an element', async () => { - const logo = screen.getByRole('img', { name: /logo/ }) - let tripleClickFired = false - logo.addEventListener('click', (evt) => { - if (evt.detail === 3) { - tripleClickFired = true - } - }) - - await userEvent.tripleClick(logo) - expect(tripleClickFired).toBe(true) -}) -``` - -References: - -- [Playwright `locator.click` API](https://playwright.dev/docs/api/class-locator#locator-click): implemented via `click` with `clickCount: 3` . -- [WebdriverIO `browser.action` API](https://webdriver.io/docs/api/browser/action/): implemented via actions api with `move` plus three `down + up + pause` events in a row -- [testing-library `tripleClick` API](https://testing-library.com/docs/user-event/convenience/#tripleClick) - -## userEvent.fill - -- **Type:** `(element: Element, text: string) => Promise` - -用文本填充 input/textarea/conteneditable。这将在输入新值之前移除输入框中的任何现有文本。 - -```ts -import { userEvent } from '@vitest/browser/context' -import { screen } from '@testing-library/dom' - -test('update input', async () => { - const input = screen.getByRole('input') - - await userEvent.fill(input, 'foo') // input.value == foo - await userEvent.fill(input, '{{a[[') // input.value == {{a[[ - await userEvent.fill(input, '{Shift}') // input.value == {Shift} -}) -``` - -::: 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)。 -::: - -References: - -- [Playwright `locator.fill` API](https://playwright.dev/docs/api/class-locator#locator-fill) -- [WebdriverIO `element.setValue` API](https://webdriver.io/docs/api/element/setValue) -- [testing-library `type` API](https://testing-library.com/docs/user-event/utility/#type) - -## userEvent.keyboard - -- **Type:** `(text: string) => Promise` - -通过 `userEvent.keyboard` 可以触发键盘输入。如果任何输入有焦点,它就会在该输入中键入字符。否则,它将触发当前焦点元素(如果没有焦点元素,则为 `document.body`)上的键盘事件。 - -This API supports [user-event `keyboard` syntax](https://testing-library.com/docs/user-event/keyboard). - -```ts -import { userEvent } from '@vitest/browser/context' -import { screen } from '@testing-library/dom' - -test('trigger keystrokes', async () => { - await userEvent.keyboard('foo') // translates to: f, o, o - await userEvent.keyboard('{{a[[') // translates to: {, a, [ - await userEvent.keyboard('{Shift}{f}{o}{o}') // translates to: Shift, f, o, o - await userEvent.keyboard('{a>5}') // press a without releasing it and trigger 5 keydown - await userEvent.keyboard('{a>5/}') // press a for 5 keydown and then release it -}) -``` - -References: - -- [Playwright `locator.press` API](https://playwright.dev/docs/api/class-locator#locator-press) -- [WebdriverIO `action('key')` API](https://webdriver.io/docs/api/browser/action#key-input-source) -- [testing-library `type` API](https://testing-library.com/docs/user-event/utility/#type) - -## userEvent.tab - -- **Type:** `(options?: UserEventTabOptions) => Promise` - -发送一个 `Tab` 键事件。这是`userEvent.keyboard('{tab}')`的简写。 - -```ts -import { userEvent } from '@vitest/browser/context' -import { screen } from '@testing-library/dom' - -test('tab works', async () => { - const [input1, input2] = screen.getAllByRole('input') - - expect(input1).toHaveFocus() - - await userEvent.tab() - - expect(input2).toHaveFocus() - - await userEvent.tab({ shift: true }) - - expect(input1).toHaveFocus() -}) -``` - -References: - -- [Playwright `locator.press` API](https://playwright.dev/docs/api/class-locator#locator-press) -- [WebdriverIO `action('key')` API](https://webdriver.io/docs/api/browser/action#key-input-source) -- [testing-library `tab` API](https://testing-library.com/docs/user-event/convenience/#tab) - -## userEvent.type - -- **Type:** `(element: Element, text: string, options?: UserEventTypeOptions) => Promise` - -::: warning -如果不依赖 [special characters](https://testing-library.com/docs/user-event/keyboard)(例如,`{shift}` 或 `{selectall}`),建议使用 [`userEvent.fill`](#userevent-fill)。 -::: - -`type` 方法在 [`keyboard`](https://testing-library.com/docs/user-event/keyboard) API 的基础上实现了 `@testing-library/user-event` 的 [`type`](https://testing-library.com/docs/user-event/utility/#type) 工具。 - -该函数允许您在 input/textarea/conteneditable 中键入字符。它支持 [user-event `keyboard` syntax](https://testing-library.com/docs/user-event/keyboard)。 - -如果只需按下字符而无需输入,请使用 [`userEvent.keyboard`](#userevent-keyboard) API。 - -```ts -import { userEvent } from '@vitest/browser/context' -import { screen } from '@testing-library/dom' - -test('update input', async () => { - const input = screen.getByRole('input') - - await userEvent.type(input, 'foo') // input.value == foo - await userEvent.type(input, '{{a[[') // input.value == foo{a[ - await userEvent.type(input, '{Shift}') // input.value == foo{a[ -}) -``` - -References: - -- [Playwright `locator.press` API](https://playwright.dev/docs/api/class-locator#locator-press) -- [WebdriverIO `action('key')` API](https://webdriver.io/docs/api/browser/action#key-input-source) -- [testing-library `type` API](https://testing-library.com/docs/user-event/utility/#type) - -## userEvent.clear - -- **Type:** `(element: Element) => Promise` - -此方法会清除输入元素的内容。 - -```ts -import { userEvent } from '@vitest/browser/context' -import { screen } from '@testing-library/dom' - -test('clears input', async () => { - const input = screen.getByRole('input') - - await userEvent.fill(input, 'foo') - expect(input).toHaveValue('foo') - - await userEvent.clear(input) - expect(input).toHaveValue('') -}) -``` - -References: - -- [Playwright `locator.clear` API](https://playwright.dev/docs/api/class-locator#locator-clear) -- [WebdriverIO `element.clearValue` API](https://webdriver.io/docs/api/element/clearValue) -- [testing-library `clear` API](https://testing-library.com/docs/user-event/utility/#clear) - -## userEvent.selectOptions - -- **Type:** `(element: Element, values: HTMLElement | HTMLElement[] | string | string[], options?: UserEventSelectOptions) => Promise` - -The `userEvent.selectOptions` allows selecting a value in a `