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/index.md b/config/index.md index 711d0c2f..5df89a19 100644 --- a/config/index.md +++ b/config/index.md @@ -501,7 +501,6 @@ Vitest 中的默认测试环境是一个 Node.js 环境。如果你正在构建 你可以通过在文件顶部添加包含 `@vitest-environment` 的文档块或注释,为某个测试文件中的所有测试指定环境: - 文档块格式: ```js @@ -1689,7 +1688,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 @@ -2532,4 +2531,3 @@ export interface SnapshotEnvironment { - **默认值:** `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 `