---
url: /api/browser/assertions.md
---
# 断言 API
Vitest 开箱即用提供了广泛的 DOM 断言,这些断言源自 [`@testing-library/jest-dom`](https://github.com/testing-library/jest-dom) 库,并增加了对定位器(locators)的支持和内置的重试能力。
::: tip TypeScript 支持
如果你正在使用 [TypeScript](/guide/browser/#typescript) 或者希望在 `expect` 中获得正确的类型提示,请确保你在某处引用了 `vitest/browser`。如果你从未从那里导入过,你可以在任何被 `tsconfig.json` 覆盖的文件中添加一个 `reference` 注释:
```ts
///
```
:::
浏览器中的测试可能会因其异步性质而不一致地失败。因此,重要的是要有一种方法来保证即使条件延迟(例如由于超时、网络请求或动画),断言也能成功。为此,Vitest 通过 [`expect.poll`](/api/expect#poll) 和 `expect.element` API 开箱即用地提供了可重试的断言:
```ts
import { expect, test } from 'vitest'
import { page } from 'vitest/browser'
test('error banner is rendered', async () => {
triggerError()
// 这将创建一个定位器,当调用其任何方法时,它将尝试查找元素
// 此调用本身不会检查元素是否存在。
const banner = page.getByRole('alert', {
name: /error/i,
})
// Vitest 提供了具有内置重试能力的 `expect.element`
// 它会反复检查该元素是否存在于 DOM 中,以及
// `element.textContent` 的内容是否等于 "Error!"
// 直到所有条件都满足为止
await expect.element(banner).toMatchTextContent('Error!')
})
```
我们建议在使用 `page.getBy*` 定位器时始终使用 `expect.element` 以减少测试的不稳定性。注意 `expect.element` 接受第二个选项:
```ts
interface ExpectPollOptions {
// 重试断言的间隔时间(毫秒)
// 默认为 "expect.poll.interval" 配置选项
interval?: number
// 重试断言的时间(毫秒)
// 默认为 "expect.poll.timeout" 配置选项
timeout?: number
// 断言失败时打印的消息
message?: string
}
```
::: tip
与 [`expect.poll`](/api/expect#poll) 类似,`expect.element` 会重试 DOM 断言,直到它们通过或达到超时时间。当它接收到一个定位器时,Vitest 会在执行 DOM 断言之前,先使用 [`locator.findElement()`](/api/browser/locators#findelement) 解析它。`timeout` 选项适用于整个重试操作。`interval` 选项控制失败的 DOM 断言多久重试一次,但定位器解析使用的是 `findElement` 自身逐渐增加的重试间隔。
`toMatchTextContent` 和所有其他断言在普通的 `expect` 中仍然可用,但不具备内置的重试机制:
```ts
// 如果 .textContent 不是 `'Error!'`,会立即失败
expect(banner).toMatchTextContent('Error!')
```
:::
## toBeDisabled
```ts
function toBeDisabled(): Promise
```
允许你检查元素是否从用户的角度被禁用。
如果元素是表单控件且在此元素上指定了 `disabled` 属性,或者该元素是具有 `disabled` 属性的表单元素的后代,则匹配。
注意,只有原生控件元素(如 HTML `button`、`input`、`select`、`textarea`、`option`、`optgroup`)可以通过设置 "disabled" 属性来禁用。除非是自定义元素,否则其他元素上的 "disabled" 属性会被忽略。
```html
```
```ts
await expect.element(getByTestId('button')).toBeDisabled() // ✅
await expect.element(getByTestId('button')).not.toBeDisabled() // ❌
```
## toBeEnabled
```ts
function toBeEnabled(): Promise
```
允许你检查元素是否未从用户的角度被禁用。
工作原理类似于 [`not.toBeDisabled()`](#tobedisabled)。使用此匹配器可以避免测试中的双重否定。
```html
```
```ts
await expect.element(getByTestId('button')).toBeEnabled() // ✅
await expect.element(getByTestId('button')).not.toBeEnabled() // ❌
```
## toBeEmptyDOMElement
```ts
function toBeEmptyDOMElement(): Promise
```
这允许你断言元素是否没有对用户可见的内容。它会忽略注释,但如果元素包含空白字符则会失败。
```html
```
```ts
await expect.element(getByTestId('empty')).toBeEmptyDOMElement()
await expect.element(getByTestId('not-empty')).not.toBeEmptyDOMElement()
await expect.element(
getByTestId('with-whitespace')
).not.toBeEmptyDOMElement()
```
## toBeInTheDocument
```ts
function toBeInTheDocument(): Promise
```
断言元素是否存在于文档中。
```html
```
```ts
await expect.element(getByTestId('svg-element')).toBeInTheDocument()
await expect.element(getByTestId('does-not-exist')).not.toBeInTheDocument()
```
::: warning
此匹配器不会查找分离的元素。元素必须添加到文档中才能被 `toBeInTheDocument` 找到。如果你希望在分离的元素中搜索,请使用:[`toContainElement`](#tocontainelement)。
:::
## toBeInvalid
```ts
function toBeInvalid(): Promise
```
这允许你检查元素当前是否无效。
如果元素具有 [`aria-invalid` 属性](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-invalid) 且无值或值为 `"true"`,或者 [`checkValidity()`](https://developer.mozilla.org/en-US/docs/Web/HTML/Constraint_validation) 的结果为 `false`,则元素无效。
```html
```
```ts
await expect.element(getByTestId('no-aria-invalid')).not.toBeInvalid()
await expect.element(getByTestId('aria-invalid')).toBeInvalid()
await expect.element(getByTestId('aria-invalid-value')).toBeInvalid()
await expect.element(getByTestId('aria-invalid-false')).not.toBeInvalid()
await expect.element(getByTestId('valid-form')).not.toBeInvalid()
await expect.element(getByTestId('invalid-form')).toBeInvalid()
```
## toBeRequired
```ts
function toBeRequired(): Promise
```
这允许你检查表单元素当前是否是必需的。
如果元素具有 `required` 或 `aria-required="true"` 属性,则该元素是必需的。
```html
```
```ts
await expect.element(getByTestId('required-input')).toBeRequired()
await expect.element(getByTestId('aria-required-input')).toBeRequired()
await expect.element(getByTestId('conflicted-input')).toBeRequired()
await expect.element(getByTestId('aria-not-required-input')).not.toBeRequired()
await expect.element(getByTestId('optional-input')).not.toBeRequired()
await expect.element(getByTestId('unsupported-type')).not.toBeRequired()
await expect.element(getByTestId('select')).toBeRequired()
await expect.element(getByTestId('textarea')).toBeRequired()
await expect.element(getByTestId('supported-role')).not.toBeRequired()
await expect.element(getByTestId('supported-role-aria')).toBeRequired()
```
## toBeValid
```ts
function toBeValid(): Promise
```
这允许你检查元素的值当前是否有效。
如果元素没有 [`aria-invalid` 属性](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-invalid) 或属性值为 "false",则元素有效。如果是表单元素,[`checkValidity()`](https://developer.mozilla.org/en-US/docs/Web/HTML/Constraint_validation) 的结果也必须为 `true`。
```html
```
```ts
await expect.element(getByTestId('no-aria-invalid')).toBeValid()
await expect.element(getByTestId('aria-invalid')).not.toBeValid()
await expect.element(getByTestId('aria-invalid-value')).not.toBeValid()
await expect.element(getByTestId('aria-invalid-false')).toBeValid()
await expect.element(getByTestId('valid-form')).toBeValid()
await expect.element(getByTestId('invalid-form')).not.toBeValid()
```
## toBeVisible
```ts
function toBeVisible(): Promise
```
这允许你检查元素当前是否对用户可见。
当元素具有非空的边界框且没有 `visibility:hidden` 计算样式时,被视为可见。
注意根据此定义:
* 大小为零的元素**不**被视为可见。
* 具有 `display:none` 的元素**不**被视为可见。
* 具有 `opacity:0` 的元素**被**视为可见。
要检查列表中至少有一个元素可见,请使用 `locator.first()`。
```ts
// 特定元素可见。
await expect.element(page.getByText('Welcome')).toBeVisible()
// 列表中至少有一项可见。
await expect.element(page.getByTestId('todo-item').first()).toBeVisible()
// 两个元素中至少有一个可见,也可能两个都可见。
await expect.element(
page.getByRole('button', { name: 'Sign in' })
.or(page.getByRole('button', { name: 'Sign up' }))
.first()
).toBeVisible()
```
## toBeInViewport 4.0.0 {#tobeinviewport}
```ts
function toBeInViewport(options: { ratio?: number }): Promise
```
这允许你使用 [IntersectionObserver API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) 检查元素当前是否在视口内。
你可以传递 `ratio` 参数作为选项,这意味着元素在视口内的最小比例。`ratio` 应该在 0~1 之间。
```ts
// 特定元素在视口内。
await expect.element(page.getByText('Welcome')).toBeInViewport()
// 特定元素的 50% 应该在视口内
await expect.element(page.getByText('To')).toBeInViewport({ ratio: 0.5 })
// 特定元素的全部应该在视口内
await expect.element(page.getByText('Vitest')).toBeInViewport({ ratio: 1 })
```
## toContainElement
```ts
function toContainElement(element: HTMLElement | SVGElement | Locator | null): Promise
```
这允许你断言一个元素是否包含另一个元素作为后代。
```html
```
```ts
const ancestor = getByTestId('ancestor')
const descendant = getByTestId('descendant')
const nonExistingElement = getByTestId('does-not-exist')
await expect.element(ancestor).toContainElement(descendant)
await expect.element(descendant).not.toContainElement(ancestor)
await expect.element(ancestor).not.toContainElement(nonExistingElement)
```
## toContainHTML
```ts
function toContainHTML(htmlText: string): Promise
```
断言表示 HTML 元素的字符串是否包含在另一个元素中。该字符串应包含有效的 html,而不是任何不完整的 html。
```html
```
```ts
// 这些是有效的用法
await expect.element(getByTestId('parent')).toContainHTML('')
await expect.element(getByTestId('parent')).toContainHTML('')
await expect.element(getByTestId('parent')).not.toContainHTML('
')
// 这些不起作用
await expect.element(getByTestId('parent')).toContainHTML('data-testid="child"')
await expect.element(getByTestId('parent')).toContainHTML('data-testid')
await expect.element(getByTestId('parent')).toContainHTML('')
```
::: warning
你大概率不需要使用这个匹配器。我们鼓励从用户在浏览器中感知应用程序的角度进行测试。这就是为什么不建议针对特定的 DOM 结构进行测试。
当被测试的代码渲染了从外部来源获取的 html,并且你想验证该 html 代码是否按预期使用时,它可能会很有用。
它不应用于检查你控制的 DOM 结构。请使用 [`toContainElement`](#tocontainelement) 代替。
:::
::: warning
很可能你不需要使用这个匹配器。我们鼓励从用户在浏览器中感知应用程序的角度进行测试。这就是为什么不建议针对特定的 DOM 结构进行测试。
在被测试的代码渲染从外部源获取的 html,并且你想验证该 html 代码是否按预期使用时,它可能会很有用。
它不应用于检查你控制的 DOM 结构。请使用 [`toContainElement`](#tocontainelement) 代替。
:::
## toHaveAccessibleDescription
```ts
function toHaveAccessibleDescription(description?: string | RegExp): Promise
```
这允许你断言元素具有预期的
[可访问描述](https://w3c.github.io/accname/)。
你可以传递预期的可访问描述的确切字符串,或者你也可以通过传递正则表达式,或使用
[`expect.stringContaining`](/api/expect#expect-stringcontaining) 或 [`expect.stringMatching`](/api/expect#expect-stringmatching) 进行部分匹配。
```html
开始
关于
我们公司的标志
```
```ts
await expect.element(getByTestId('link')).toHaveAccessibleDescription()
await expect.element(getByTestId('link')).toHaveAccessibleDescription('返回起点的链接')
await expect.element(getByTestId('link')).not.toHaveAccessibleDescription('主页')
await expect.element(getByTestId('extra-link')).not.toHaveAccessibleDescription()
await expect.element(getByTestId('avatar')).not.toHaveAccessibleDescription()
await expect.element(getByTestId('logo')).not.toHaveAccessibleDescription('公司标志')
await expect.element(getByTestId('logo')).toHaveAccessibleDescription(
'我们公司的标志',
)
await expect.element(getByTestId('logo2')).toHaveAccessibleDescription(
'我们公司的标志',
)
```
## toHaveAccessibleErrorMessage
```ts
function toHaveAccessibleErrorMessage(message?: string | RegExp): Promise
```
这允许你断言元素具有预期的
[可访问错误消息](https://w3c.github.io/aria/#aria-errormessage)。
你可以传递预期的可访问错误消息的确切字符串。
或者,你可以通过传递正则表达式
或使用
[`expect.stringContaining`](/api/expect#expect-stringcontaining) 或 [`expect.stringMatching`](/api/expect#expect-stringmatching) 进行部分匹配。
```html
此字段无效
```
```ts
// 带有有效错误消息的输入框
await expect.element(getByRole('textbox', { name: '有错误' })).toHaveAccessibleErrorMessage()
await expect.element(getByRole('textbox', { name: '有错误' })).toHaveAccessibleErrorMessage(
'此字段无效',
)
await expect.element(getByRole('textbox', { name: '有错误' })).toHaveAccessibleErrorMessage(
/invalid/i,
)
await expect.element(
getByRole('textbox', { name: '有错误' }),
).not.toHaveAccessibleErrorMessage('This field is absolutely correct!')
// 没有有效错误消息的输入框
await expect.element(
getByRole('textbox', { name: '没有错误属性' }),
).not.toHaveAccessibleErrorMessage()
await expect.element(
getByRole('textbox', { name: '未失效' }),
).not.toHaveAccessibleErrorMessage()
```
## toHaveAccessibleName
```ts
function toHaveAccessibleName(name?: string | RegExp): Promise
```
这允许你断言元素具有预期的
[可访问名称](https://w3c.github.io/accname/)。例如,这对于断言表单元素和按钮是否有适当的标签很有用。
你可以传递预期的可访问名称的确切字符串,或者你也可以通过传递正则表达式,或使用
[`expect.stringContaining`](/api/expect#expect-stringcontaining) 或 [`expect.stringMatching`](/api/expect#expect-stringmatching) 进行部分匹配。
```html
测试内容
```
```javascript
await expect.element(getByTestId('img-alt')).toHaveAccessibleName('测试替代文本')
await expect.element(getByTestId('img-empty-alt')).not.toHaveAccessibleName()
await expect.element(getByTestId('svg-title')).toHaveAccessibleName('测试标题')
await expect.element(getByTestId('button-img-alt')).toHaveAccessibleName()
await expect.element(getByTestId('img-paragraph')).not.toHaveAccessibleName()
await expect.element(getByTestId('svg-button')).toHaveAccessibleName()
await expect.element(getByTestId('svg-without-title')).not.toHaveAccessibleName()
await expect.element(getByTestId('input-title')).toHaveAccessibleName()
```
## toHaveAttribute
```ts
function toHaveAttribute(attribute: string, value?: unknown): Promise
```
这允许你检查给定元素是否具有属性。你也可以选择检查属性是否具有特定的预期值或使用 [`expect.stringContaining`](/api/expect#expect-stringcontaining) 或 [`expect.stringMatching`](/api/expect#expect-stringmatching) 进行部分匹配。
```html
```
```ts
const button = getByTestId('ok-button')
await expect.element(button).toHaveAttribute('disabled')
await expect.element(button).toHaveAttribute('type', 'submit')
await expect.element(button).not.toHaveAttribute('type', 'button')
await expect.element(button).toHaveAttribute(
'type',
expect.stringContaining('sub')
)
await expect.element(button).toHaveAttribute(
'type',
expect.not.stringContaining('but')
)
```
## toHaveClass
```ts
function toHaveClass(...classNames: string[], options?: { exact: boolean }): Promise
function toHaveClass(...classNames: (string | RegExp)[]): Promise
```
这允许你检查给定元素在其 `class` 属性中是否具有某些类。你必须提供至少一个类,除非你断言元素没有任何类。
类名列表可以包括字符串和正则表达式。正则表达式与目标元素中的每个单独类进行匹配,而不是与其完整的 `class` 属性值整体匹配。
::: warning
注意,当只提供正则表达式时,你不能使用 `exact: true` 选项。
:::
```html
```
```ts
const deleteButton = getByTestId('delete-button')
const noClasses = getByTestId('no-classes')
await expect.element(deleteButton).toHaveClass('extra')
await expect.element(deleteButton).toHaveClass('btn-danger btn')
await expect.element(deleteButton).toHaveClass(/danger/, 'btn')
await expect.element(deleteButton).toHaveClass('btn-danger', 'btn')
await expect.element(deleteButton).not.toHaveClass('btn-link')
await expect.element(deleteButton).not.toHaveClass(/link/)
// ⚠️ 正则表达式匹配单个类,而不是整个类列表
await expect.element(deleteButton).not.toHaveClass(/btn extra/)
// 元素确切拥有一组类(顺序任意)
await expect.element(deleteButton).toHaveClass('btn-danger extra btn', {
exact: true
})
// 如果它拥有的类多于预期,将会失败
await expect.element(deleteButton).not.toHaveClass('btn-danger extra', {
exact: true
})
await expect.element(noClasses).not.toHaveClass()
```
## toHaveFocus
```ts
function toHaveFocus(): Promise
```
这允许你断言一个元素是否具有焦点。
```html
```
```ts
const input = page.getByTestId('element-to-focus')
input.element().focus()
await expect.element(input).toHaveFocus()
input.element().blur()
await expect.element(input).not.toHaveFocus()
```
## toHaveFormValues
```ts
function toHaveFormValues(expectedValues: Record): Promise
```
这允许你检查表单或字段集是否包含每个给定名称的表单控件,并具有指定的值。
::: tip
重要的是要强调,此匹配器只能在 [form](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement) 或 [fieldset](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFieldSetElement) 元素上调用。
这使它能够利用 `form` 和 `fieldset` 中的 [`.elements`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/elements) 属性来可靠地获取其中的所有表单控件。
这也避免了用户提供一个包含多个 `form` 的容器,从而混合不相关甚至可能相互冲突的表单控件的可能性。
:::
此匹配器抽象了根据表单控件类型获取表单控件值的特殊性。例如,`` 元素有一个 `value` 属性,但 `