Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,10 @@ export default ({ mode }: { mode: string }) => {
text: '类型测试',
link: '/guide/testing-types',
},
{
text: 'Benchmarking',
link: '/guide/benchmarking',
},
{
text: '内联测试',
link: '/guide/in-source',
Expand Down
10 changes: 5 additions & 5 deletions .vitepress/sponsors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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/',
Expand All @@ -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/',
Expand Down
30 changes: 2 additions & 28 deletions api/advanced/vitest.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,9 @@ title: Vitest API

# Vitest

Vitest 实例需要当前的测试模式。它可以是以下之一:

- `test`:运行运行时测试时
- `benchmark`:运行基准测试时 <Experimental />

::: 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 <Experimental /> {#benchmark}
## mode <Deprecated /> {#mode}

基准测试模式调用 `bench` 函数,并在遇到 `test` 或 `it` 时抛出错误。此模式使用配置中的 `benchmark.include` 和 `benchmark.exclude` 选项来查找基准测试文件。
Since Vitest 5, this property is always `'test'`.

## config

Expand Down
43 changes: 34 additions & 9 deletions api/browser/assertions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!')
})
```

Expand All @@ -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!')
```
:::

Expand Down Expand Up @@ -696,7 +696,32 @@ await expect.element(button).not.toHaveStyle({

```ts
function toHaveTextContent(
text: string | RegExp,
text: string | number,
options?: { normalizeWhitespace: boolean }
): Promise<void>
```
<!-- TODO: translation -->
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
<span data-testid="text-content">Text Content</span>
```

```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<void>
```
Expand All @@ -707,7 +732,7 @@ function toHaveTextContent(

若要进行不区分大小写的匹配,可以使用带有 `/i` 修饰符的 `RegExp`。

如果你想匹配整段内容,可以使用 `RegExp` 来实现。
如果你想匹配整段内容,可以使用 `RegExp` 或 [`toHaveTextContent`](#tohavetextcontent) 来实现。

```html
<span data-testid="text-content">Text Content</span>
Expand All @@ -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
Expand Down
61 changes: 55 additions & 6 deletions api/browser/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@ outline: deep
在浏览器测试中,可借助 `readFile`、`writeFile` 与 `removeFile` 三个 API 完成文件操作。自 Vitest 3.2 起,所有路径均以 [project](/guide/projects) 根目录为基准解析(根目录默认为 `process.cwd()`,可手动重写);旧版本则以当前测试文件所在目录为基准。

默认情况下,Vitest 使用 `utf-8` 编码,但你可以使用选项覆盖它。

<!-- TODO: translation -->
::: 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.

<!-- TODO: translation -->
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
Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -125,7 +126,55 @@ declare module 'vitest/browser' {
::: warning
如果自定义命令具有相同的名称,则它们将覆盖内置命令。
:::
<!-- TODO: translation -->

::: 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.
Expand Down
2 changes: 2 additions & 0 deletions api/browser/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)文档。
<!-- TODO: translation -->
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
Expand Down
Loading
Loading