diff --git a/.vitepress/config.ts b/.vitepress/config.ts
index 8cedcbf1..61e0fdcf 100644
--- a/.vitepress/config.ts
+++ b/.vitepress/config.ts
@@ -902,6 +902,10 @@ export default ({ mode }: { mode: string }) => {
text: '类型测试',
link: '/guide/testing-types',
},
+ {
+ text: 'Benchmarking',
+ link: '/guide/benchmarking',
+ },
{
text: '内联测试',
link: '/guide/in-source',
diff --git a/.vitepress/sponsors.ts b/.vitepress/sponsors.ts
index 720845f7..f9b2a058 100644
--- a/.vitepress/sponsors.ts
+++ b/.vitepress/sponsors.ts
@@ -43,11 +43,6 @@ export const sponsors: SponsorTier[] = [
url: 'https://vital.io/',
img: '/vital.svg',
},
- {
- name: 'OOMOL',
- url: 'https://oomol.com/',
- img: '/oomol.svg',
- },
{
name: 'Mailmeteor',
url: 'https://mailmeteor.com/',
@@ -58,6 +53,11 @@ export const sponsors: SponsorTier[] = [
url: 'https://www.liminity.se/',
img: '/liminity.svg',
},
+ {
+ name: 'Kraken Tech',
+ url: 'https://kraken.tech/',
+ img: '/kraken.svg',
+ },
{
name: 'Aerius Ventilation',
url: 'https://aerius.se/',
diff --git a/api/advanced/vitest.md b/api/advanced/vitest.md
index bd75a509..4f456a71 100644
--- a/api/advanced/vitest.md
+++ b/api/advanced/vitest.md
@@ -5,35 +5,9 @@ title: Vitest API
# Vitest
-Vitest 实例需要当前的测试模式。它可以是以下之一:
-
-- `test`:运行运行时测试时
-- `benchmark`:运行基准测试时
-
-::: details New in Vitest 4
-Vitest 4 新增了多个 API(它们都标记有 "4.0.0+" 徽章),并移除了已弃用的 API:
-
-- `invalidates`
-- `changedTests`(使用 [`onFilterWatchedSpecification`](#onfilterwatchedspecification) 代替)
-- `server`(使用 [`vite`](#vite) 代替)
-- `getProjectsByTestFile`(使用 [`getModuleSpecifications`](#getmodulespecifications) 代替)
-- `getFileWorkspaceSpecs`(使用 [`getModuleSpecifications`](#getmodulespecifications) 代替)
-- `getModuleProjects`(自行通过 [`this.projects`](#projects) 过滤)
-- `updateLastChanged`(重命名为 [`invalidateFile`](#invalidatefile))
-- `globTestSpecs`(使用 [`globTestSpecifications`](#globtestspecifications) 代替)
-- `globTestFiles`(使用 [`globTestSpecifications`](#globtestspecifications) 代替)
-- `listFile`(使用 [`getRelevantTestSpecifications`](#getrelevanttestspecifications) 代替)
-:::
-
-## mode
-
-### test
-
-测试模式只会调用 `test` 或 `it` 中的函数,并在遇到 `bench` 时抛出错误。此模式使用配置中的 `include` 和 `exclude` 选项来查找测试文件。
-
-### benchmark {#benchmark}
+## mode {#mode}
-基准测试模式调用 `bench` 函数,并在遇到 `test` 或 `it` 时抛出错误。此模式使用配置中的 `benchmark.include` 和 `benchmark.exclude` 选项来查找基准测试文件。
+Since Vitest 5, this property is always `'test'`.
## config
diff --git a/api/browser/assertions.md b/api/browser/assertions.md
index f31eb6af..689a80b1 100644
--- a/api/browser/assertions.md
+++ b/api/browser/assertions.md
@@ -34,7 +34,7 @@ test('error banner is rendered', async () => {
// 它会反复检查该元素是否存在于 DOM 中,并且
// `element.textContent` 的内容等于 "Error!"
// 直到所有条件都满足为止
- await expect.element(banner).toHaveTextContent('Error!')
+ await expect.element(banner).toMatchTextContent('Error!')
})
```
@@ -56,11 +56,11 @@ interface ExpectPollOptions {
::: tip
Like [`expect.poll`](/api/expect#poll), `expect.element` retries DOM assertions until they pass or the timeout is reached. When it receives a locator, Vitest resolves it with [`locator.findElement()`](/api/browser/locators#findelement) before running the DOM assertion. The `timeout` option applies to the whole retry operation. The `interval` option controls how often failed DOM assertions are retried, but locator resolution uses `findElement`'s own increasing retry intervals.
-`toHaveTextContent` 以及其他所有断言在常规的 `expect` 中仍然可用,但没有内置的重试机制:
+`toMatchTextContent` 以及其他所有断言在常规的 `expect` 中仍然可用,但没有内置的重试机制:
```ts
// 如果 .textContent 不是 `'Error!'`,则会立即失败。
-expect(banner).toHaveTextContent('Error!')
+expect(banner).toMatchTextContent('Error!')
```
:::
@@ -696,7 +696,32 @@ await expect.element(button).not.toHaveStyle({
```ts
function toHaveTextContent(
- text: string | RegExp,
+ text: string | number,
+ options?: { normalizeWhitespace: boolean }
+): Promise
+```
+
+This matcher allows you to validate that an element's text matches provided string exactly. This
+supports elements, but also text nodes and fragments.
+
+If you wish to perform a partial check or do a case-sensitive match, use [`toMatchTextContent`](#tomatchtextcontent) instead.
+
+```html
+Text Content
+```
+
+```ts
+const element = getByTestId('text-content')
+
+await expect.element(element).toHaveTextContent('Text Content')
+await expect.element(element).not.toHaveTextContent('Content')
+```
+
+## toMatchTextContent
+
+```ts
+function toMatchTextContent(
+ text: string | number | RegExp,
options?: { normalizeWhitespace: boolean }
): Promise
```
@@ -707,7 +732,7 @@ function toHaveTextContent(
若要进行不区分大小写的匹配,可以使用带有 `/i` 修饰符的 `RegExp`。
-如果你想匹配整段内容,可以使用 `RegExp` 来实现。
+如果你想匹配整段内容,可以使用 `RegExp` 或 [`toHaveTextContent`](#tohavetextcontent) 来实现。
```html
Text Content
@@ -716,12 +741,12 @@ function toHaveTextContent(
```ts
const element = getByTestId('text-content')
-await expect.element(element).toHaveTextContent('Content')
+await expect.element(element).toMatchTextContent('Content')
// 匹配整段内容
-await expect.element(element).toHaveTextContent(/^Text Content$/)
+await expect.element(element).toMatchTextContent(/^Text Content$/)
// 不区分大小写匹配
-await expect.element(element).toHaveTextContent(/content$/i)
-await expect.element(element).not.toHaveTextContent('content')
+await expect.element(element).toMatchTextContent(/content$/i)
+await expect.element(element).not.toMatchTextContent('content')
```
## toHaveValue
diff --git a/api/browser/commands.md b/api/browser/commands.md
index 45c16838..284e4b04 100644
--- a/api/browser/commands.md
+++ b/api/browser/commands.md
@@ -14,12 +14,11 @@ outline: deep
在浏览器测试中,可借助 `readFile`、`writeFile` 与 `removeFile` 三个 API 完成文件操作。自 Vitest 3.2 起,所有路径均以 [project](/guide/projects) 根目录为基准解析(根目录默认为 `process.cwd()`,可手动重写);旧版本则以当前测试文件所在目录为基准。
默认情况下,Vitest 使用 `utf-8` 编码,但你可以使用选项覆盖它。
-
+
::: tip
-此 API 遵循 [`server.fs`](https://vitejs.dev/config/server-options.html#server-fs-allow) 出于安全原因的限制。
+The built-in file commands follow Vite's [`server.fs`](https://vitejs.dev/config/server-options.html#server-fs-allow) restrictions for security reasons.
-
-If [`browser.api.allowWrite`](/config/browser/api) or [`api.allowWrite`](/config/api#api-allowwrite) are disabled, `writeFile` and `removeFile` functions won't do anything.
+`writeFile` and `removeFile` also require write access through [`browser.api.allowWrite`](/config/browser/api) and [`api.allowWrite`](/config/api#api-allowwrite).
:::
```ts
@@ -59,7 +58,9 @@ expect(input).toHaveValue('a')
```
::: warning
-CDP session仅适用于 `playwright` provider,并且仅在使用 `chromium` 浏览器时有效。有关详细信息,请参阅 playwright 的 [`CDPSession`](https://playwright.dev/docs/api/class-cdpsession)文档。
+CDP session 仅适用于 `playwright` provider,并且仅在使用 `chromium` 浏览器时有效。有关详细信息,请参阅 playwright 的 [`CDPSession`](https://playwright.dev/docs/api/class-cdpsession) 文档。
+
+CDP is a privileged debugging API. It is available only when browser API write and exec operations are enabled through [`browser.api.allowWrite`](/config/browser/api#api-allowwrite), [`browser.api.allowExec`](/config/browser/api#api-allowexec), [`api.allowWrite`](/config/api#api-allowwrite), and [`api.allowExec`](/config/api#api-allowexec).
:::
## 自定义命令 {#custom-commands}
@@ -125,7 +126,55 @@ declare module 'vitest/browser' {
::: warning
如果自定义命令具有相同的名称,则它们将覆盖内置命令。
:::
-
+
+::: warning Security
+Custom commands run in the Vitest Node process and are callable from browser test code through Vitest's browser RPC connection. They can access local files, environment variables, network services, databases, shell commands, and other Node APIs.
+
+Vitest's built-in file commands validate paths against Vite's [`server.fs`](https://vite.dev/config/server-options#server-fs-allow) restrictions and separately check whether writes are allowed. Custom commands do not automatically inherit these protections. If a custom command accepts browser-provided input and uses it to read, write, delete, execute, or expose local resources, validate that input before using it.
+
+For file reads or fixture loading, use `isFileLoadingAllowed` from `vitest/node` or an explicit allowlist. For writes and deletes, also require an explicit mutation policy, such as [`browser.api.allowWrite`](/config/browser/api#api-allowwrite), [`api.allowWrite`](/config/api#api-allowwrite), and a command-specific allowed directory. For commands that execute code, shell commands, or project scripts, also check [`browser.api.allowExec`](/config/browser/api#api-allowexec) and [`api.allowExec`](/config/api#api-allowexec).
+
+For example, if you create your own file-writing command instead of using Vitest's built-in `writeFile`, apply the same checks:
+
+```ts
+import { mkdir, writeFile } from 'node:fs/promises'
+import { dirname, resolve } from 'node:path'
+import { normalizePath } from 'vite'
+import { isFileLoadingAllowed } from 'vitest/node'
+import type { BrowserCommand } from 'vitest/node'
+
+function assertFileAccess(path: string, project: any) {
+ if (
+ !isFileLoadingAllowed(project.vite.config, path)
+ && !isFileLoadingAllowed(project.vitest.vite.config, path)
+ ) {
+ throw new Error(`Access denied to "${path}".`)
+ }
+}
+
+function assertWrite(project: any) {
+ if (!project.config.browser.api.allowWrite || !project.vitest.config.api.allowWrite) {
+ throw new Error('Writing files is disabled.')
+ }
+}
+
+export const myWriteFileCommand: BrowserCommand<[path: string, content: string]> = async (
+ { project },
+ path,
+ content,
+) => {
+ assertWrite(project)
+
+ const file = resolve(project.config.root, path)
+ assertFileAccess(normalizePath(file), project)
+
+ await mkdir(dirname(file), { recursive: true })
+ await writeFile(file, content)
+}
+```
+
+:::
+
### Recording trace markers
Custom commands can record [trace markers](/api/browser/context#mark) for the test that triggered them through `context.mark`. This is the server-side equivalent of `page.mark` and helps annotate the [trace view](/guide/browser/trace-view) with custom actions performed inside a command.
diff --git a/api/browser/context.md b/api/browser/context.md
index bf1f9a02..534508b3 100644
--- a/api/browser/context.md
+++ b/api/browser/context.md
@@ -206,6 +206,8 @@ At the moment, the `frameLocator` method is only supported by the `playwright` p
::: warning
CDP 会话仅适用于 `playwright` provider,并且仅在使用 `chromium` 浏览器时有效。有关详细信息,请参阅 playwright 的 [`CDPSession`](https://playwright.dev/docs/api/class-cdpsession)文档。
+
+CDP is a privileged debugging API. It is available only when browser API write and exec operations are enabled through [`browser.api.allowWrite`](/config/browser/api#api-allowwrite), [`browser.api.allowExec`](/config/browser/api#api-allowexec), [`api.allowWrite`](/config/api#api-allowwrite), and [`api.allowExec`](/config/api#api-allowexec).
:::
```ts
diff --git a/api/test.md b/api/test.md
index 722591a2..38fb60c0 100644
--- a/api/test.md
+++ b/api/test.md
@@ -666,236 +666,9 @@ test.concurrent.for([
挂载在 test.extend 实例上的 `aroundAll` 钩子,继承 [`test.extend`](#test-extend) 的类型。更多内容请参阅 [aroundAll](/api/hooks#aroundall)。
## bench {#bench}
+
+::: warning Updated in Vitest 5
+The benchmarking API has been rewritten. `bench` is no longer a top-level import from `vitest`, and the `bench.skip` / `bench.only` / `bench.todo` helpers have been removed. `bench` is now a [test-context fixture](/guide/test-context#bench) accessed from inside a `test()`.
-- **类型:** `(name: string | Function, fn: BenchFunction, options?: BenchOptions) => void`
-
-::: danger
-基准测试是实验性功能,不遵循 SemVer。
+See the [Benchmarking guide](/guide/benchmarking) for the new API.
:::
-
-`bench` 用于定义基准测试。在 Vitest 中,基准测试是一个定义了一系列操作的函数,Vitest 会多次运行该函数以展示不同的性能指标。
-
- Vitest 底层使用 [`tinybench`](https://github.com/tinylibs/tinybench) 库,所有选项均可作为第三个参数传入。
-
-```ts
-import { bench } from 'vitest'
-
-bench('normal sorting', () => {
- const x = [1, 5, 4, 2, 3]
- x.sort((a, b) => {
- return a - b
- })
-}, { time: 1000 })
-```
-
-```ts
-export interface Options {
- /**
- * 运行基准任务所需时间(毫秒)
- * @default 500
- */
- time?: number
-
- /**
- * 如果连时间选项都已完成,任务应运行的次数
- * @default 10
- */
- iterations?: number
-
- /**
- * 函数以毫秒为单位获取当前时间戳
- */
- now?: () => number
-
- /**
- * 用于中止基准测试的中止信号
- */
- signal?: AbortSignal
-
- /**
- * 任务失败时抛出(如果为 true,事件将不起作用)
- */
- throws?: boolean
-
- /**
- * 预热时间(毫秒)
- * @default 100ms
- */
- warmupTime?: number
-
- /**
- * 热身迭代
- * @default 5
- */
- warmupIterations?: number
-
- /**
- * 在每个基准任务(周期)之前运行的设置函数
- */
- setup?: Hook
-
- /**
- * 在每个基准任务(周期)之后运行的拆机函数
- */
- teardown?: Hook
-}
-```
-测试用例运行后,输出结构信息如下:
-
-```
- name hz min max mean p75 p99 p995 p999 rme samples
-· normal sorting 6,526,368.12 0.0001 0.3638 0.0002 0.0002 0.0002 0.0002 0.0004 ±1.41% 652638
-```
-```ts
-export interface TaskResult {
- /*
- * 运行任务时发生的最后一次错误
- */
- error?: unknown
-
- /**
- * 以毫秒为单位的基准任务运行时间(周期)。
- */
- totalTime: number
-
- /**
- * 样本中的最小值
- */
- min: number
- /**
- * 样本中的最大值
- */
- max: number
-
- /**
- * 每秒的操作次数
- */
- hz: number
-
- /**
- * 每个操作需要多长时间(毫秒)
- */
- period: number
-
- /**
- * 每个任务的任务样本迭代时间(毫秒)
- */
- samples: number[]
-
- /**
- * 样本平均数/平均值(总体平均数的估计值)
- */
- mean: number
-
- /**
- * 样本方差(总体方差的估计值)
- */
- variance: number
-
- /**
- * 样本标准差(总体标准差的估计值)
- */
- sd: number
-
- /**
- * 平均值的标准误差(又称样本平均值的抽样分布标准差)
- */
- sem: number
-
- /**
- * 自由度
- */
- df: number
-
- /**
- * 样本临界值
- */
- critical: number
-
- /**
- * 误差率
- */
- moe: number
-
- /**
- * 相对误差
- */
- rme: number
-
- /**
- * 中位绝对偏差
- */
- mad: number
-
- /**
- * P50/中位百分位数
- */
- p50: number
-
- /**
- * p75 百分位数
- */
- p75: number
-
- /**
- * p99 百分位数
- */
- p99: number
-
- /**
- * p995 百分位数
- */
- p995: number
-
- /**
- * p999 百分位数
- */
- p999: number
-}
-```
-
-### bench.skip
-
-- **类型:** `(name: string | Function, fn: BenchFunction, options?: BenchOptions) => void`
-
-使用 `bench.skip` 跳过某些基准测试的运行。
-
-```ts
-import { bench } from 'vitest'
-
-bench.skip('normal sorting', () => {
- const x = [1, 5, 4, 2, 3]
- x.sort((a, b) => {
- return a - b
- })
-})
-```
-
-### bench.only
-
-- **类型:** `(name: string | Function, fn: BenchFunction, options?: BenchOptions) => void`
-
-使用 `bench.only` 在指定套件中只运行某些基准测试。适用于调试场景。
-
-```ts
-import { bench } from 'vitest'
-
-bench.only('normal sorting', () => {
- const x = [1, 5, 4, 2, 3]
- x.sort((a, b) => {
- return a - b
- })
-})
-```
-
-### bench.todo
-
-- **类型:** `(name: string | Function) => void`
-
-使用 `bench.todo` 为待实现的基准测试占位。
-
-```ts
-import { bench } from 'vitest'
-
-bench.todo('unimplemented test')
-```
diff --git a/config/benchmark.md b/config/benchmark.md
index 755dfa0e..d40775ad 100644
--- a/config/benchmark.md
+++ b/config/benchmark.md
@@ -8,6 +8,13 @@ outline: deep
- **类型:** `{ include?, exclude?, ... }`
运行 `vitest bench` 时使用的选项。
+
+## benchmark.enabled
+
+- **Type:** `boolean`
+- **Default:** `false`
+
+Enables the benchmark project. When set, Vitest creates a dedicated benchmark project alongside your regular test project, runs files matching [`benchmark.include`](#benchmark-include) in it, and exposes the [`bench` fixture](/guide/test-context#bench) to those files. Running `vitest bench` enables this automatically.
## benchmark.include
@@ -32,39 +39,18 @@ outline: deep
定义后,Vitest 将运行所有匹配的文件,其中包含 `import.meta.vitest`。
-## benchmark.reporters
-
-- **类型:** `Arrayable`
-- **默认值:** `'default'`
-
-用于定义输出的自定义报告器。它可以包含一个或多个内置报告名称、报告实例和(或)自定义报告的路径。
-
-## benchmark.outputFile
-
-已弃用,建议使用 `benchmark.outputJson`。
-
-## benchmark.outputJson {#benchmark-outputJson}
-
-- **类型:** `string | undefined`
-- **默认值:** `undefined`
+## benchmark.retainSamples
-存储基准测试结果的文件路径,可用于稍后的 `--compare` 选项。
+- **Type:** `boolean`
+- **Default:** `false`
-例如:
+Include the `samples` array of per-iteration timings on every benchmark result. Disabled by default to reduce memory usage; enable when a custom reporter or API consumer needs the raw samples.
-```sh
-# 保存主分支的结果
-git checkout main
-vitest bench --outputJson main.json
-# 切换到另一个分支并与主分支进行比较
-git checkout feature
-vitest bench --compare main.json
-```
+## benchmark.suppressExportGetterWarnings
-## benchmark.compare {#benchmark-compare}
+- **Type:** `boolean`
+- **Default:** `false`
-- **类型:** `string | undefined`
-- **默认值:** `undefined`
+Suppress the warning printed when a benchmark accesses module export getters too many times. Vitest tracks getter access during benchmark runs because Vite's module runner wraps every export in a getter, and excessive access can dominate the measurement (see [Module Runner Overhead](/guide/benchmarking#module-runner-overhead)). Enable this when you've intentionally accepted the overhead, or when the warning is noisy for benchmarks where the getter cost is negligible.
-用于与当前运行结果进行比较的先前基准测试结果的文件路径。
diff --git a/config/browser/api.md b/config/browser/api.md
index 75d62938..99931666 100644
--- a/config/browser/api.md
+++ b/config/browser/api.md
@@ -15,8 +15,8 @@ outline: deep
- **类型:** `boolean`
- **默认值:** 未暴露公网时默认为 `true`,否则为 `false`
-
-Vitest 通过接收来自浏览器的 WebSocket 连接来保存 [测试注解](/guide/test-annotations)、[测试产物](/api/advanced/artifacts) 和 [快照](/guide/snapshot)。这意味着任何能连接到该 API 的人都可在你机器的项目根目录(由 [`fs.allow`](https://cn.vite.dev/config/server-options#server-fs-allow) 配置)内执行任意代码。
+
+Vitest 通过接收来自浏览器的 WebSocket 连接来保存 [测试注解](/guide/test-annotations)、[测试产物](/api/advanced/artifacts) 和 [快照](/guide/snapshot)。这意味着任何能连接到该 API 的人都可在你机器的项目根目录(由 [`fs.allow`](https://cn.vite.dev/config/server-options#server-fs-allow) 配置)内执行任意代码。 This option also gates privileged browser APIs that can write files indirectly, such as raw Chrome DevTools Protocol access through [`cdp()`](/api/browser/context#cdp).
当浏览器服务器未暴露至互联网(主机为 `localhost`)时,默认值设为 `true` 不会构成安全隐患。若你修改了主机配置,Vitest 将默认将 `allowWrite` 设为 `false` 以防止潜在的恶意写入风险。
@@ -25,4 +25,4 @@ Vitest 通过接收来自浏览器的 WebSocket 连接来保存 [测试注解](/
- **类型:** `boolean`
- **默认值:** 未暴露至公网时默认为 `true`,否则为 `false`
-允许通过 [UI 模式](/guide/ui) 运行任意测试文件。此配置仅作用于界面交互元素(及其背后的服务端代码)的可执行权限。如果 UI 模式被禁用,则该配置不生效。更多信息请参阅 [`api.allowExec`](/config/api#api-allowexec)。
+允许通过 [UI 模式](/guide/ui) 运行任意测试文件。此配置仅作用于界面交互元素(及其背后的服务端代码)的可执行权限。如果 UI 模式被禁用,则该配置不生效。This option also gates privileged browser APIs that can execute code indirectly, such as raw Chrome DevTools Protocol access through [`cdp()`](/api/browser/context#cdp). 更多信息请参阅 [`api.allowExec`](/config/api#api-allowexec)。
diff --git a/config/browser/commands.md b/config/browser/commands.md
index 69362fbc..a6b30a9b 100644
--- a/config/browser/commands.md
+++ b/config/browser/commands.md
@@ -9,3 +9,9 @@ outline: deep
- **默认值:** `{ readFile, writeFile, ... }`
可在浏览器测试中通过 `vitest/browser` 导入 [自定义命令](/api/browser/commands)。
+
+::: warning Security
+Commands run in the Vitest Node process. If a command exposes filesystem, process, network, database, or shell access based on browser-provided input, validate and restrict that input inside the command. Built-in file commands apply Vite `server.fs` checks and write-access checks, but custom commands are responsible for their own protections.
+
+See [Custom Commands security notes](/api/browser/commands#custom-commands).
+:::
diff --git a/config/coverage.md b/config/coverage.md
index 95795ff4..68ac8fea 100644
--- a/config/coverage.md
+++ b/config/coverage.md
@@ -249,8 +249,8 @@ npx vitest --coverage.enabled --coverage.provider=istanbul
当实际覆盖率超过配置阈值时,自动将 `lines`、`functions`、`branches` 和 `statements` 的阈值更新到配置文件中。
此选项适用于覆盖率提高时保持阈值不变。
-
-你也可以通过传入函数自定义阈值更新值的格式:
+
+你也可以通过传入函数自定义阈值更新值的格式,The function receives the new threshold as the first argument and the previous threshold as the second:
```ts
@@ -260,6 +260,12 @@ npx vitest --coverage.enabled --coverage.provider=istanbul
// 更新阈值为整数
autoUpdate: (newThreshold) => Math.floor(newThreshold),
+ // Log the change and update without decimals
+ autoUpdate: (newThreshold, previousThreshold) => {
+ console.log(`Updated threshold from ${previousThreshold} to ${newThreshold}`)
+ return Math.floor(newThreshold)
+ },
+
// 95.85 -> 95
functions: 95,
}
diff --git a/config/index.md b/config/index.md
index d9fbbcd9..0fa77688 100644
--- a/config/index.md
+++ b/config/index.md
@@ -8,7 +8,7 @@ outline: deep
- 创建 `vitest.config.ts`,它将具有更高的优先级,并且会**覆盖** `vite.config.ts` 中的配置(Vitest 支持所有传统的 JS 和 TS 文件扩展名,但不支持 `json`) - 这意味着我们在 `vite.config` 中的所有选项将被**忽略**。
- 向 CLI 传递 `--config` 选项,例如 `vitest --config ./path/to/vitest.config.ts`。
-- 使用 `process.env.VITEST` 或在 `defineConfig` 上的 `mode` 属性(如果没有用 `--mode` 覆盖,默认设置为 `test`/`benchmark`)来在 `vite.config.ts` 中有条件地应用不同的配置。请注意,像任何其他环境变量一样,`VITEST` 也会在测试中的 `import.meta.env` 上暴露出来。
+- 使用 `process.env.VITEST` 或在 `defineConfig` 上的 `mode` 属性(如果没有用 `--mode` 覆盖,默认设置为 `test`)也可以在 `vite.config.ts` 中有条件地应用不同的配置。请注意,像任何其他环境变量一样,`VITEST` 也会在测试中的 `import.meta.env` 上暴露出来。
要配置 Vitest 本身,请在我们的 Vite 配置中添加 `test` 属性。如果我们是从 `vite` 本身导入 `defineConfig`,我们还需要在配置文件顶部使用 [三斜杠指令](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-types-) 添加对 Vitest 类型引用。
diff --git a/config/runner.md b/config/runner.md
index aacbd269..df99bc4f 100644
--- a/config/runner.md
+++ b/config/runner.md
@@ -6,6 +6,5 @@ outline: deep
# runner
- **类型**: `VitestRunnerConstructor`
-- **默认值**: 运行测试时为 `node`,运行基准测试时为 `benchmark`
自定义测试运行器的路径。这是一项高级功能,应与自定义库运行器一起使用。你可以在 [文档](/api/advanced/runner) 中阅读更多相关信息。
diff --git a/eslint.config.js b/eslint.config.js
index 2ee0d214..d59eba32 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -76,6 +76,7 @@ export default antfu(
'import/no-mutable-exports': 'off',
'no-throw-literal': 'off',
'markdown/no-missing-link-fragments': 'off',
+ 'import/no-duplicates': 'off',
},
},
createSimplePlugin({
diff --git a/guide/advanced/index.md b/guide/advanced/index.md
index f5b40893..bb79add5 100644
--- a/guide/advanced/index.md
+++ b/guide/advanced/index.md
@@ -14,7 +14,6 @@ title: Advanced API
```ts
function startVitest(
- mode: VitestRunMode,
cliFilters: string[] = [],
options: CliOptions = {},
viteOverrides?: ViteUserConfig,
@@ -27,7 +26,7 @@ function startVitest(
```js
import { startVitest } from 'vitest/node'
-const vitest = await startVitest('test')
+const vitest = await startVitest()
await vitest.close()
```
@@ -47,7 +46,7 @@ await vitest.close()
```ts
import type { TestModule } from 'vitest/node'
-const vitest = await startVitest('test')
+const vitest = await startVitest()
console.log(vitest.state.getTestModules()) // [TestModule]
```
@@ -60,7 +59,6 @@ console.log(vitest.state.getTestModules()) // [TestModule]
```ts
function createVitest(
- mode: VitestRunMode,
options: CliOptions,
viteOverrides: ViteUserConfig = {},
vitestOptions: VitestOptions = {},
diff --git a/guide/benchmarking.md b/guide/benchmarking.md
new file mode 100644
index 00000000..31a0a104
--- /dev/null
+++ b/guide/benchmarking.md
@@ -0,0 +1,480 @@
+---
+title: Benchmarking | Guide
+---
+
+# Benchmarking
+
+Vitest lets you write benchmarks alongside your tests using the `bench` fixture from the [test context](/guide/test-context). Benchmarks are powered by [Tinybench](https://github.com/tinylibs/tinybench) and are defined inside regular `test()` calls, giving you access to the full power of Vitest's test runner: retries, lifecycle hooks, filtering, and assertions.
+
+## Defining a Benchmark
+
+Use the `bench` fixture to define a benchmark. Call `.run()` to execute it:
+
+```ts
+import { expect, test } from 'vitest'
+
+test('parsing performance', async ({ bench }) => {
+ const result = await bench('parse', () => {
+ JSON.parse('{"key":"value"}')
+ }).run()
+})
+```
+
+The `bench()` function registers a benchmark without executing it. Calling `.run()` runs the benchmark and returns the result. After the test completes, Vitest prints a single-row version of the [comparison table](#comparing-benchmarks) (ops/sec, mean time, percentiles, etc.), so you get the same output for a one-off benchmark as you do for `bench.compare()`.
+
+::: warning
+The `bench` fixture is only available in files matched by [`benchmark.include`](/config/#benchmark-include) (default: `**/*.{bench,benchmark}.?(c|m)[jt]s?(x)`). Using `{ bench }` inside a regular test file will throw an error.
+
+Whether a file participates in the benchmark run is decided by the filename, not by whether the test uses the `bench` fixture. Renaming `parser.test.ts` to `parser.bench.ts` (or adjusting `benchmark.include`) is what moves it into the benchmark project.
+:::
+
+## Running Benchmarks
+
+Benchmark files are matched by [`benchmark.include`](/config/#benchmark-include) (default: `**/*.{bench,benchmark}.?(c|m)[jt]s?(x)`) and run in their own project, separate from your regular tests. There are three ways to run them, depending on whether you want to skip them, run them alongside tests, or run them on their own.
+
+### `vitest` (default)
+
+Without [`benchmark.enabled`](/config/#benchmark-enabled), the `vitest` command only runs regular tests. Benchmark files are ignored entirely. This is the default and the right choice for day-to-day development, since benchmarks are slow and noisy and shouldn't run on every save.
+
+### `vitest` with `benchmark.enabled`
+
+Set `benchmark.enabled: true` in your config to run benchmarks together with regular tests:
+
+```ts [vitest.config.ts]
+import { defineConfig } from 'vitest/config'
+
+export default defineConfig({
+ test: {
+ benchmark: {
+ enabled: true,
+ },
+ },
+})
+```
+
+With this config, `vitest` runs your regular tests first, then runs the benchmarks in a separate isolated group (so benchmark execution never overlaps with test execution and adds noise to results). Useful in CI when you want a single command to validate correctness and performance.
+
+### `vitest bench`
+
+The `bench` subcommand runs only benchmarks and skips regular tests:
+
+```bash
+vitest bench
+```
+
+This implicitly enables `benchmark.enabled` for the run, so you don't need to set it in the config. Like the `vitest` command, it accepts filename filters and `-t`/`--testNamePattern` to narrow the run:
+
+```bash
+# only benchmarks in files matching "parser"
+vitest bench parser
+
+# only benchmarks whose test name matches "JSON"
+vitest bench -t JSON
+```
+
+## Comparing Benchmarks
+
+Use `bench.compare()` to compare multiple benchmarks against each other:
+
+```ts
+import { expect, test } from 'vitest'
+
+test('compare JSON libraries', async ({ bench }) => {
+ const input = '{"key":"value","nested":{"a":1}}'
+
+ const result = await bench.compare(
+ bench('JSON.parse', () => {
+ JSON.parse(input)
+ }),
+ bench('custom parser', () => {
+ customParse(input)
+ }),
+ )
+})
+```
+
+When comparing benchmarks, Vitest runs them using interleaved iterations to reduce environmental bias (CPU throttling, GC pressure, etc.) and prints a comparison table after the test completes:
+
+<<< ./snippets/benchmark-table.ansi
+
+### Options
+
+You can pass [options](https://tinylibs.github.io/tinybench/interfaces/BenchOptions.html) as the last argument to `bench.compare()`:
+
+```ts
+test('compare with options', async ({ bench }) => {
+ const result = await bench.compare(
+ bench('lib1', () => { lib1() }),
+ bench('lib2', () => { lib2() }),
+ {
+ iterations: 100,
+ time: 1000,
+ },
+ )
+})
+```
+
+You can also pass per-benchmark [options](https://tinylibs.github.io/tinybench/interfaces/FnOptions.html) as the second argument, matching how `test()` accepts options:
+
+```ts
+test('benchmarks with setup', async ({ bench }) => {
+ const result = await bench.compare(
+ bench('with-cache', () => {
+ readFromCache()
+ }),
+ bench(
+ 'without-cache',
+ { beforeEach: () => clearCache() },
+ () => { readFromDisk() },
+ ),
+ )
+})
+```
+
+## Comparing Across Projects
+
+When your workspace defines multiple projects (e.g., different browsers or runtimes), pass `perProject: true` in the bench options to compare how the same benchmark performs across all of them. Vitest still prints the result inline for the current project, and additionally collects per-project results into a single comparison table at the end of the test run.
+
+```ts
+import { test } from 'vitest'
+
+test('simple example', async ({ bench }) => {
+ await bench('1 + 1', { perProject: true }, () => {
+ 1 + 1
+ }).run()
+})
+```
+
+The same test file runs in each project (chromium, firefox, webkit, etc.), and Vitest groups the results:
+
+<<< ./snippets/benchmark-per-project.ansi
+
+You can also mix `perProject` benchmarks with regular ones inside `bench.compare()`:
+
+```ts
+test('compare implementations across browsers', async ({ bench }) => {
+ await bench.compare(
+ bench('JSON.parse', { perProject: true }, () => {
+ JSON.parse('{"key":"value"}')
+ }),
+ bench('custom parser', () => {
+ customParse('{"key":"value"}')
+ }),
+ )
+})
+```
+
+In this case, `custom parser` appears in the normal inline comparison table per project, while `JSON.parse` is additionally collected into the cross-project comparison table at the end.
+
+## Asserting Performance
+
+Use `toBeFasterThan()` and `toBeSlowerThan()` matchers to assert relative performance between benchmarks:
+
+```ts
+import { expect, test } from 'vitest'
+
+test('lib1 is faster than lib2', async ({ bench }) => {
+ const result = await bench.compare(
+ bench('lib1', () => { lib1() }),
+ bench('lib2', () => { lib2() }),
+ )
+
+ expect(result.get('lib1')).toBeFasterThan(result.get('lib2'))
+})
+```
+
+The `delta` option specifies the minimum relative difference required for the assertion to pass. This helps avoid flaky tests caused by benchmark noise:
+
+```ts
+// lib1 must be at least 10% faster than lib2
+expect(result.get('lib1')).toBeFasterThan(result.get('lib2'), {
+ delta: 0.1,
+})
+
+// lib2 must be at least 20% slower than lib1
+expect(result.get('lib2')).toBeSlowerThan(result.get('lib1'), {
+ delta: 0.2,
+})
+```
+
+You can also assert absolute performance using standard matchers:
+
+```ts
+test('parsing is fast enough', async ({ bench }) => {
+ const result = await bench('parse', () => {
+ parse(largeInput)
+ }).run()
+
+ expect(result.throughput.mean).toBeGreaterThan(10_000)
+})
+```
+
+## Retries
+
+Since benchmarks can be noisy, use the `retry` option to automatically retry failing benchmark tests:
+
+```ts
+test('performance comparison', { retry: 3 }, async ({ bench }) => {
+ const result = await bench.compare(
+ bench('lib1', () => { lib1() }),
+ bench('lib2', () => { lib2() }),
+ )
+
+ expect(result.get('lib1')).toBeFasterThan(result.get('lib2'))
+})
+```
+
+## Storing and Replaying Results
+
+Two primitives let you persist benchmark results to disk and compare against them in future runs: the `writeResult` option saves a result, and `bench.from()` reads one back.
+
+### `writeResult`
+
+Pass `writeResult` as a per-bench option to write the result to a JSON file every time the benchmark runs. The path is resolved against the project root:
+
+```ts
+test('parse', async ({ bench }) => {
+ await bench(
+ 'parse',
+ { writeResult: './benchmarks/parse.json' },
+ () => parse(largeInput),
+ ).run()
+})
+```
+
+- The benchmark always runs. There is no skip-when-cached behaviour and no CLI flag, the file is overwritten on every successful run.
+- If the function throws, the file is not written.
+- Commit these files alongside your code so reviewers and CI share the same reference points.
+
+::: warning
+If you commit these files, keep in mind that benchmark results vary significantly between environments (developer machines, CI runners, different OSes). Designate a single environment (typically CI) to generate the file, and avoid regenerating it locally.
+:::
+
+### `bench.from()`
+
+`bench.from(name, source)` is a registration that doesn't execute a function. It reads a previously stored result and feeds it into `bench.compare()` (or returns it directly when you call `.run()`).
+
+The source can be a path (relative to the project root) or a function that returns the result data, including a Promise:
+
+```ts
+test('compare against the stored baseline', async ({ bench }) => {
+ const result = await bench.compare(
+ bench(
+ 'current',
+ { writeResult: './benchmarks/parse.json' },
+ () => parse(largeInput),
+ ),
+ bench.from('previous', './benchmarks/parse.json'),
+ bench.from('remote', () => fetch('https://path/to/external/file.json').then(r => r.json())),
+ )
+
+ expect(result.get('current')).toBeFasterThan(result.get('previous'))
+})
+```
+
+You can keep historical artifacts for older versions and compare them against the current implementation. Because `bench.from()` never invokes the function that produced the file, the original benchmark code can be deleted once the artifact is committed:
+
+```ts
+test('compare parser versions', async ({ bench }) => {
+ const input = '{"key":"value"}'
+
+ await bench.compare(
+ bench.from('v1', './benchmarks/parse.v1.json'),
+ bench.from('v2', './benchmarks/parse.v2.json'),
+ bench(
+ 'current',
+ { writeResult: './benchmarks/parse.current.json' },
+ () => customParser(input),
+ ),
+ )
+})
+```
+
+To produce a new historical artifact, point a fresh `bench()` at that version's implementation, set `writeResult` to a versioned path (`./benchmarks/parse.v3.json`), run it once, then replace the call with `bench.from('v3', './benchmarks/parse.v3.json')`.
+
+To regenerate the baseline on demand, gate the write behind an environment variable so the same test either refreshes the artifact or compares against it:
+
+```ts
+test('compare parser versions', async ({ bench }) => {
+ if (import.meta.env.VITE_WRITE_BENCH) {
+ const baseline = bench('baseline', { writeResult: './my-bench.json' }, () => fn())
+ await baseline.run()
+ }
+ else {
+ const baseline = bench.from('baseline', './my-bench.json')
+ await bench.compare(bench('current', () => fn()), baseline)
+ }
+})
+```
+
+Run `VITE_WRITE_BENCH=1 vitest bench` to refresh the stored result, and `vitest bench` to compare the current implementation against it.
+
+### Per-project artifacts
+
+In a multi-project workspace (different browsers, different runtimes), share one benchmark file across projects by including `${projectName}` in the path. The placeholder is substituted with the current project name at write time:
+
+```ts
+test('cross-project baseline', async ({ bench }) => {
+ await bench(
+ 'parse',
+ // eslint-disable-next-line no-template-curly-in-string
+ { perProject: true, writeResult: './benchmarks/parse.${projectName}.json' },
+ () => parse(largeInput),
+ ).run()
+})
+```
+
+Use the same template in `bench.from()` so each project reads its own artifact.
+
+## Stability
+
+Benchmarks are inherently flaky: CPU load, thermal throttling, GC pressure, and background processes all affect results. Vitest takes several steps to minimize this noise:
+
+- **Separate project**: Benchmark files are grouped into their own project based on the [`benchmark.include`](/config/#benchmark-include) pattern. The `bench` fixture is only exposed in files matched by that pattern. Using it inside a regular test file will throw an error.
+- **No concurrency**: Tests within a benchmark file always run sequentially. Benchmark files themselves also run one at a time, never in parallel. This prevents benchmarks from interfering with each other.
+
+To further improve stability:
+
+- Use the [`retry`](#retries) option to automatically rerun flaky benchmark assertions.
+- Use the [`delta`](#asserting-performance) option in `toBeFasterThan` / `toBeSlowerThan` to allow for acceptable variance.
+- Avoid running benchmarks alongside CPU-intensive processes.
+- Close browsers, IDEs, and other applications that compete for CPU time.
+
+### Dead Code Elimination
+
+JavaScript engines can optimize away code that has no observable side effects. If your benchmark function doesn't use its result, the engine may skip the computation entirely, producing misleadingly fast numbers:
+
+```ts
+test('parsing', async ({ bench }) => {
+ // BAD: the engine may eliminate the work
+ await bench('parse', () => {
+ JSON.parse(input)
+ }).run()
+
+ // GOOD: the result is consumed
+ await bench('parse', () => {
+ const result = JSON.parse(input)
+ doSomething(result)
+ }).run()
+})
+```
+
+This applies to all engines (V8, JavaScriptCore, SpiderMonkey) but is especially aggressive in V8's TurboFan and JavaScriptCore's FTL compiler tiers.
+
+### Module Runner Overhead
+
+By default, Vitest runs tests in Node.js using Vite's module runner (configured by [`experimental.viteModuleRunner`](/config/experimental#experimental-vitemodulerunner)). This transforms all module exports into getters, so every access to an imported binding goes through something like `__vite_ssr_module__.value`. In regular tests this overhead is negligible, but in benchmarks where a function is called millions of times, the getter call itself can dominate the measurement.
+
+Vitest will print a warning if it detects excessive getter calls (which you can silence via [`benchmark.suppressExportGetterWarnings`](/config/benchmark#benchmark-suppressexportgetterwarnings)), but you should be aware of this when benchmarking imported functions:
+
+```ts
+import { parse } from './parser.js'
+
+const _parse = parse
+
+test('parsing', async ({ bench }) => {
+ // BAD: every call to `parse` goes through a getter
+ await bench('parse', () => {
+ parse(input)
+ }).run()
+
+ // GOOD: store the reference locally to bypass the getter
+ await bench('parse', () => {
+ _parse(input)
+ }).run()
+})
+```
+
+If you are the library author, the same overhead applies inside the library you are benchmarking: every cross-module call within its source goes through the same getter wrapper. If you are benchmarking your own library, you have two ways to remove this:
+
+**Benchmark the pre-built artifact.** Import the library through its package name (which resolves to its built output) instead of reaching into its source. The built file has already collapsed internal imports into direct references, so Vite's module runner sees a single module with no internal getters:
+
+```ts
+// BAD: every internal call inside the library goes through a getter
+import { parse } from '../src/index.ts'
+
+// GOOD: the published entry has no internal getters
+import { parse } from 'my-library'
+```
+
+If you compare your library against other packages, benchmark the same kind of artifact for every implementation. For workspace packages, make sure the package name resolves to the built output instead of source, for example by externalizing the package in Vite or by importing from `dist`.
+
+**Disable the module runner for the benchmark.** If the benchmark does not need Vite transforms, mocks, or Vitest module interception, disable [`experimental.viteModuleRunner`](/config/experimental#experimental-vitemodulerunner) for the benchmark project so Node runs native ESM directly.
+
+This only affects Node.js mode. Browser mode uses native ESM imports and does not have this overhead.
+
+### Engine-Specific Considerations
+
+#### V8 (Node.js, Chrome)
+
+- **JIT tiering**: V8 compiles functions through multiple optimization tiers (Sparkplug → Maglev → TurboFan). A function may run at different speeds during warmup vs. steady-state. Tinybench handles warmup automatically, but very short benchmark runs may not reach the highest optimization tier.
+- **Deoptimization**: V8 can "bail out" of optimized code mid-benchmark if it encounters unexpected types or shapes. Keep the types consistent in your benchmark function:
+
+ ```ts
+ test('process items', async ({ bench }) => {
+ // BAD: mixed shapes cause deoptimization
+ await bench('process', () => {
+ for (const item of items) {
+ // some items have { name: string }, others have { name: string, id: number }
+ process(item)
+ }
+ }).run()
+
+ // GOOD: consistent object shapes
+ await bench('process', () => {
+ for (const item of items) {
+ // all items have the same shape { name: string, id: number }
+ process(item)
+ }
+ }).run()
+ })
+ ```
+
+- **Garbage collection**: Large allocations inside the benchmark loop add GC noise. If you're measuring computation, pre-allocate data in a `setup` hook rather than inside the benchmarked function:
+
+ ```ts
+ test('sorting', async ({ bench }) => {
+ const original = Array.from({ length: 10000 }).fill(Math.random())
+ let data: number[]
+
+ // BAD: allocates a new array every iteration, GC adds noise
+ await bench('sort', () => {
+ const data = Array.from({ length: 10000 }).fill(Math.random())
+ data.sort()
+ }).run()
+
+ // GOOD: pre-allocate, copy in beforeEach
+ await bench(
+ 'sort',
+ () => { data.sort() },
+ {
+ beforeEach() {
+ data = [...original]
+ },
+ },
+ ).run()
+ })
+ ```
+
+#### JavaScriptCore (Bun, Safari)
+
+- **Different optimization thresholds**: JSC uses its own JIT tiers (LLInt → Baseline → DFG → FTL) with different inlining and optimization heuristics. A benchmark that is fast on V8 may behave very differently on JSC.
+- **Async benchmarks**: Bun's event loop implementation differs from Node.js. If your benchmark involves async operations or timers, results may not be directly comparable across runtimes.
+
+#### Browser
+
+- **Timer resolution**: Browsers may reduce `performance.now()` precision (e.g., to 100μs or even 1ms) as a security mechanism. This makes very fast operations difficult to measure accurately, so increase iterations to compensate:
+
+ ```ts
+ test('fast operations', async ({ bench }) => {
+ await bench.compare(
+ bench('fast-op', () => { fastOp() }),
+ bench('other-op', () => { otherOp() }),
+ {
+ // more iterations help overcome low timer resolution
+ iterations: 1000,
+ },
+ )
+ })
+ ```
+- **Cross-browser differences**: V8 (Chrome), SpiderMonkey (Firefox), and JSC (Safari) optimize different patterns differently. A benchmark that shows one library winning in Chrome may show the opposite in Firefox.
diff --git a/guide/cli-generated.md b/guide/cli-generated.md
index b3690888..c99b18cd 100644
--- a/guide/cli-generated.md
+++ b/guide/cli-generated.md
@@ -311,7 +311,7 @@ UI 模式和 HTML 报告器中提供的 HTML 覆盖率输出目录。
- **命令行终端:** `--mode `
- **配置:** [mode](/config/mode)
-覆盖 Vite 模式 (默认值: `test` 或 `benchmark`)
+覆盖 Vite 模式 (默认值: `test`)
### isolate
diff --git a/guide/migration.md b/guide/migration.md
index cea5dae6..f2283d1c 100644
--- a/guide/migration.md
+++ b/guide/migration.md
@@ -13,6 +13,36 @@ outline: deep
Vitest 5.0 目前处于 beta 阶段。本章节跟踪已合并的重大变更,在稳定版发布前可能还会发生变化。
:::
+### Benchmarking API Rewrite
+
+The benchmarking API has been rewritten. `bench` is no longer a top-level import from `vitest`; it is a [test-context fixture](/guide/test-context#bench) accessed from inside a regular `test()`. See the [Benchmarking guide](/guide/benchmarking) for the new API.
+
+Removed, with replacements where applicable:
+
+- **`bench(name, fn)` at module scope**: destructure `bench` from the test context instead.
+
+```ts
+// v4
+import { bench } from 'vitest' // [!code --]
+
+bench('sort', () => { // [!code --]
+ [3, 1, 2].sort() // [!code --]
+}) // [!code --]
+
+// v5
+import { test } from 'vitest' // [!code ++]
+
+test('sort', async ({ bench }) => { // [!code ++]
+ await bench('sort', () => { [3, 1, 2].sort() }).run() // [!code ++]
+}) // [!code ++]
+```
+
+- **`bench.skip`, `bench.only`, `bench.todo`** are removed. Use the regular `test.skip`, `test.only`, `test.todo` on the surrounding `test()` instead.
+- **`benchmark.reporters` / `benchmark.outputFile`** are removed. Benchmark output is now part of the default reporter and the `json` reporter; configure those at the top level via `test.reporters` instead.
+- **`benchmark.compare` config and the `--compare` CLI flag** are removed. Pass [`writeResult`](/guide/benchmarking#storing-and-replaying-results) as a per-bench option to persist a result, and read it back with [`bench.from()`](/guide/benchmarking#bench-from) inside `bench.compare()`.
+- **`benchmark.outputJson` config and the `--outputJson` CLI flag** are removed. Use `--reporter=json --outputFile=` to capture benchmark results; the JSON reporter now includes a `benchmarks` field on each test case.
+- **`Vitest` instance `mode` property** is now always `'test'`. The previous `'benchmark'` value is no longer used; benchmarks run inside a dedicated project of the same `Vitest` instance.
+
### 移除 `test.sequential`, `describe.sequential`, 和 `sequential` 选项 {##removed-test-sequential-describe-sequential-and-sequential-options}
Vitest 5.0 移除了已弃用的 `test.sequential`、`describe.sequential` 和 `sequential` 选项。当你需要让某个测试或测试套件不再沿用继承来的并发设置,或退出全局配置的并发时,请使用 `concurrent: false`。
@@ -79,6 +109,21 @@ await locator.click()
- `vitest/suite`:改用 `vitest` 中 `TestRunner` 的静态方法(例如,`TestRunner.getCurrentTest()`)
- `vitest/mocker` 已移除,请直接使用 `@vitest/mocker` 包(这个包曾意外发布过一次且从未被移除)
- `vitest/internal/module-runner` 已移除
+
+### `toHaveTextContent` Now Performs Strict Equality
+
+The browser-mode [`toHaveTextContent`](/api/browser/assertions#tohavetextcontent) matcher now validates that an element's text content is exactly equal to the expected string instead of performing a partial, case-sensitive match. Regular expressions are no longer accepted. The previous behaviour, including `RegExp` support, has moved to the new [`toMatchTextContent`](/api/browser/assertions#tomatchtextcontent) matcher.
+
+```ts
+// Partial or regex matches:
+await expect.element(banner).toHaveTextContent('Error') // [!code --]
+await expect.element(banner).toHaveTextContent(/error/i) // [!code --]
+await expect.element(banner).toMatchTextContent('Error') // [!code ++]
+await expect.element(banner).toMatchTextContent(/error/i) // [!code ++]
+
+// Exact matches stay on `toHaveTextContent`:
+await expect.element(banner).toHaveTextContent('Error!')
+```
## 迁移至 Vitest 4.0 {#vitest-4}
diff --git a/guide/snippets/benchmark-per-project.ansi b/guide/snippets/benchmark-per-project.ansi
new file mode 100644
index 00000000..2eb283f3
--- /dev/null
+++ b/guide/snippets/benchmark-per-project.ansi
@@ -0,0 +1,7 @@
+[1m Cross-Project Benchmark Comparison[22m
+
+ [2mtest/parser.bench.ts > regular parsing > parsing[22m
+ [1mname [22m [1m hz[22m [1m min[22m [1m max[22m [1m mean[22m [1m p75[22m [1m p99[22m [1m p995[22m [1m p999[22m [1m rme[22m [1msamples[22m
+ webkit (bench) [34m6,730,808.15[39m [36m0.0000[39m [36m1.0000[39m [36m0.0001[39m [36m0.0000[39m [36m0.0000[39m [36m0.0000[39m [36m0.0000[39m [2m±6.20%[22m [2m6731808[22m[1m[32m fastest[39m[22m
+ chromium (bench) [34m3,841,599.95[39m [36m0.0000[39m [36m0.2000[39m [36m0.0003[39m [36m0.0000[39m [36m0.0000[39m [36m0.0000[39m [36m0.1000[39m [2m±1.96%[22m [2m3851571[22m
+ firefox (bench) [34m3,546,066.28[39m [36m0.0000[39m [36m5.0000[39m [36m0.0003[39m [36m0.0000[39m [36m0.0000[39m [36m0.0000[39m [36m0.0000[39m [2m±6.26%[22m [2m3547062[22m[1m[90m slowest[39m[22m
diff --git a/guide/snippets/benchmark-table.ansi b/guide/snippets/benchmark-table.ansi
new file mode 100644
index 00000000..8afa0770
--- /dev/null
+++ b/guide/snippets/benchmark-table.ansi
@@ -0,0 +1,9 @@
+ [32m✓[39m [30m[41m bench [49m[39m test/basic.bench.ts [2m([22m[2m1 test[22m[2m)[22m[33m 3446[2mms[22m[39m
+ [33m[2m✓[22m[39m different libraries[33m 3445[2mms[22m[39m
+ [1mname [22m [1m hz[22m [1m min[22m [1m max[22m [1m mean[22m [1m p75[22m [1m p99[22m [1m p995[22m [1m p999[22m [1m rme[22m [1msamples[22m
+ lib 1 [34m40,678,348.31[39m [36m0.0000[39m [36m0.0002[39m [36m0.0000[39m [36m0.0000[39m [36m0.0000[39m [36m0.0000[39m [36m0.0000[39m [2m±0.95%[22m [2m 53989[22m[1m[32m fastest[39m[22m
+ lib 3 [34m39,152,132.23[39m [36m0.0000[39m [36m0.0002[39m [36m0.0000[39m [36m0.0000[39m [36m0.0000[39m [36m0.0000[39m [36m0.0000[39m [2m±0.93%[22m [2m 52080[22m
+ lib 2 [34m38,088,138.97[39m [36m0.0000[39m [36m0.0066[39m [36m0.0000[39m [36m0.0000[39m [36m0.0000[39m [36m0.0000[39m [36m0.0000[39m [2m±1.58%[22m [2m 50618[22m[1m[90m slowest[39m[22m
+
+[2m Test Files [22m [1m[32m1 passed[39m[22m[90m (1)[39m
+[2m Tests [22m [1m[32m1 passed[39m[22m[90m (1)[39m
diff --git a/guide/test-context.md b/guide/test-context.md
index 1a954a3d..1cde36b4 100644
--- a/guide/test-context.md
+++ b/guide/test-context.md
@@ -116,6 +116,29 @@ it('stop request when test times out', async ({ signal }) => {
await fetch('/resource', { signal })
}, 2000)
```
+
+### `bench` 5.0.0 {#bench}
+
+The `bench` fixture lets you define and run benchmarks inside regular tests. You can measure throughput, compare implementations, and assert relative performance:
+
+```ts
+import { expect, test } from 'vitest'
+
+test('compare parsers', async ({ bench }) => {
+ const result = await bench.compare(
+ bench('JSON.parse', () => {
+ JSON.parse('{"key":"value"}')
+ }),
+ bench('custom parser', () => {
+ customParse('{"key":"value"}')
+ }),
+ )
+
+ expect(result.get('JSON.parse')).toBeFasterThan(result.get('custom parser'))
+})
+```
+
+See the [Benchmarks guide](/guide/benchmarking) for full documentation on comparisons, baselines, and assertion matchers.
### `onTestFailed`
diff --git a/guide/test-tags.md b/guide/test-tags.md
index d5ed8351..df3e312c 100644
--- a/guide/test-tags.md
+++ b/guide/test-tags.md
@@ -6,7 +6,7 @@ outline: deep
# 测试标签 4.1.0 {#test-tags}
允许你在测试用例上添加 [`标签`](/config/tags),在必要时可以使用标签进行过滤测试,或覆盖测试配置。
-
+
## Why tags
Tags become useful once a suite has groups of tests that share runner options, like a longer timeout for database queries or retries for integration tests on CI. Repeating those options on every relevant test by hand is brittle, and the categories often don't line up with file paths anyway, so splitting them out by file isn't an option. Flaky tests in particular tend to accumulate wherever the bugs landed, not in a `flaky/` folder.
@@ -234,7 +234,7 @@ vitest --tags-filter="frontend and backend"
```ts
import { startVitest } from 'vitest/node'
-await startVitest('test', [], {
+await startVitest([], {
tagsFilter: ['frontend and backend'],
})
```
diff --git a/public/kraken.svg b/public/kraken.svg
new file mode 100644
index 00000000..52ecee1f
--- /dev/null
+++ b/public/kraken.svg
@@ -0,0 +1,66 @@
+
+
\ No newline at end of file