断言 API
Vitest 开箱即用地提供了大量 DOM 断言,这些断言源自 @testing-library/jest-dom
库,并增强了对定位器和内置重试功能的支持。
TypeScript 支持
如果你正在使用 TypeScript 或希望在 expect
中获得正确的类型提示,请确保你已在某处引用 @vitest/browser/context
。如果你从未从那里导入,可以在 tsconfig.json
覆盖的任何文件中添加 reference
注释:
/// <reference types="@vitest/browser/context" />
浏览器中的测试由于其异步特性,可能会出现不一致的失败。因此,即使条件因超时、网络请求或动画等原因延迟,也需要有方法确保断言能够成功。为此,Vitest 通过 expect.poll
和 expect.element
API 开箱即用地提供了可重试的断言:
import { expect, test } from 'vitest';
import { page } from '@vitest/browser/context';
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).toHaveTextContent('Error!');
});
我们建议在使用 page.getBy*
定位器时始终使用 expect.element
,以降低测试的不稳定性。请注意,expect.element
接受第二个参数:
interface ExpectPollOptions {
// 断言重试的间隔,单位为毫秒。
// 默认为 "expect.poll.interval" 配置选项。
interval?: number;
// 断言重试的超时时间,单位为毫秒。
// 默认为 "expect.poll.timeout" 配置选项。
timeout?: number;
// 断言失败时打印的消息。
message?: string;
}
TIP
expect.element
是 expect.poll(() => element)
的简写,工作方式完全相同。
toHaveTextContent
和所有其他断言仍然可以在常规 expect
上使用,但没有内置的重试机制:
// 如果 .textContent 不等于 'Error!',则会立即失败。
expect(banner).toHaveTextContent('Error!');
toBeDisabled
function toBeDisabled(): Promise<void>;
允许你检查元素在用户看来是否被禁用。
如果元素是表单控件,并且其上指定了 disabled
属性,或者元素是具有 disabled
属性的表单元素的后代,则匹配。
请注意,只有 HTML button
、input
、select
、textarea
、option
、optgroup
等原生控件元素可以通过设置 disabled
属性来禁用。disabled
属性在其他元素上会被忽略,除非它是自定义元素。
<button data-testid="button" type="submit" disabled>submit</button>
await expect.element(getByTestId('button')).toBeDisabled(); // ✅
await expect.element(getByTestId('button')).not.toBeDisabled(); // ❌
toBeEnabled
function toBeEnabled(): Promise<void>;
允许你检查元素是否未从用户的角度被禁用。
功能类似于 not.toBeDisabled()
。使用此匹配器可避免测试中的双重否定表达。
<button data-testid="button" type="submit" disabled>submit</button>
await expect.element(getByTestId('button')).toBeEnabled(); // ✅
await expect.element(getByTestId('button')).not.toBeEnabled(); // ❌
toBeEmptyDOMElement
function toBeEmptyDOMElement(): Promise<void>;
这允许你断言元素对用户来说没有可见内容。它会忽略注释,但若元素包含空格则会失败。
<span data-testid="not-empty"><span data-testid="empty"></span></span>
<span data-testid="with-whitespace"> </span>
<span data-testid="with-comment"><!-- comment --></span>
await expect.element(getByTestId('empty')).toBeEmptyDOMElement();
await expect.element(getByTestId('not-empty')).not.toBeEmptyDOMElement();
await expect.element(getByTestId('with-whitespace')).not.toBeEmptyDOMElement();
toBeInTheDocument
function toBeInTheDocument(): Promise<void>;
断言元素是否存在于文档中。
<svg data-testid="svg-element"></svg>
await expect.element(getByTestId('svg-element')).toBeInTheDocument();
await expect.element(getByTestId('does-not-exist')).not.toBeInTheDocument();
WARNING
此匹配器不会查找分离的元素。元素必须添加到文档中才能被 toBeInTheDocument
找到。如果你希望在分离的元素中搜索,请使用 toContainElement
。
toBeInvalid
function toBeInvalid(): Promise<void>;
这允许你检查元素当前是否处于无效状态。
如果元素具有没有值或值为 "true"
的 aria-invalid
属性,或者 checkValidity()
的结果为 false
,则该元素无效。
<input data-testid="no-aria-invalid" />
<input data-testid="aria-invalid" aria-invalid />
<input data-testid="aria-invalid-value" aria-invalid="true" />
<input data-testid="aria-invalid-false" aria-invalid="false" />
<form data-testid="valid-form">
<input />
</form>
<form data-testid="invalid-form">
<input required />
</form>
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
function toBeRequired(): Promise<void>;
这允许你检查表单元素当前是否为必填。
如果元素具有 required
或 aria-required="true"
属性,则为必填。
<input data-testid="required-input" required />
<input data-testid="aria-required-input" aria-required="true" />
<input data-testid="conflicted-input" required aria-required="false" />
<input data-testid="aria-not-required-input" aria-required="false" />
<input data-testid="optional-input" />
<input data-testid="unsupported-type" type="image" required />
<select data-testid="select" required></select>
<textarea data-testid="textarea" required></textarea>
<div data-testid="supported-role" role="tree" required></div>
<div data-testid="supported-role-aria" role="tree" aria-required="true"></div>
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
function toBeValid(): Promise<void>;
这允许你检查元素的值当前是否处于有效状态。
如果元素没有 aria-invalid
属性 或属性值为 "false",则该元素有效。如果它是表单元素,checkValidity()
的结果也必须是 true
。
<input data-testid="no-aria-invalid" />
<input data-testid="aria-invalid" aria-invalid />
<input data-testid="aria-invalid-value" aria-invalid="true" />
<input data-testid="aria-invalid-false" aria-invalid="false" />
<form data-testid="valid-form">
<input />
</form>
<form data-testid="invalid-form">
<input required />
</form>
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
function toBeVisible(): Promise<void>;
这允许你检查元素当前是否对用户可见。
当元素具有非空边界框且没有 visibility:hidden
计算样式时,它被视为可见。
注意,根据此定义:
- 零大小的元素不被视为可见。
- 具有
display:none
的元素不被视为可见。 - 具有
opacity:0
的元素被视为可见。
要检查列表中至少有一个元素是可见的,请使用 locator.first()
。
// 特定元素可见。
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();
toContainElement
function toContainElement(
element: HTMLElement | SVGElement | null
): Promise<void>;
这允许你断言元素是否包含另一个元素作为其子孙元素。
<span data-testid="ancestor"><span data-testid="descendant"></span></span>
const ancestor = getByTestId('ancestor');
const descendant = getByTestId('descendant');
const nonExistantElement = getByTestId('does-not-exist');
await expect.element(ancestor).toContainElement(descendant);
await expect.element(descendant).not.toContainElement(ancestor);
await expect.element(ancestor).not.toContainElement(nonExistantElement);
toContainHTML
function toContainHTML(htmlText: string): Promise<void>;
断言一个表示 HTML 元素的字符串是否包含在另一个元素中。字符串应包含完整有效的 HTML,而不是任何不完整的 HTML。
<span data-testid="parent"><span data-testid="child"></span></span>
// 这些是有效用法
await expect
.element(getByTestId('parent'))
.toContainHTML('<span data-testid="child"></span>');
await expect
.element(getByTestId('parent'))
.toContainHTML('<span data-testid="child" />');
await expect.element(getByTestId('parent')).not.toContainHTML('<br />');
// 这些将不起作用
await expect
.element(getByTestId('parent'))
.toContainHTML('data-testid="child"');
await expect.element(getByTestId('parent')).toContainHTML('data-testid');
await expect.element(getByTestId('parent')).toContainHTML('</span>');
WARNING
通常不需要使用此匹配器。我们鼓励从用户在浏览器中感知应用程序的方式进行测试。因此,不建议针对特定的 DOM 结构进行测试。
它可能在被测试的代码渲染从外部源获取的 HTML,并且你希望验证该 HTML 代码是否按预期使用的情况下很有用。
它不应用于检查你控制的 DOM 结构。请改用 toContainElement
。
toHaveAccessibleDescription
function toHaveAccessibleDescription(
description?: string | RegExp
): Promise<void>;
这允许你断言元素拥有预期的可访问描述。
你可以传入预期可访问描述的精确字符串,也可以通过传入正则表达式,或使用 expect.stringContaining
或 expect.stringMatching
来进行部分匹配。
<a
data-testid="link"
href="/"
aria-label="Home page"
title="A link to start over"
>Start</a
>
<a data-testid="extra-link" href="/about" aria-label="About page">About</a>
<img src="avatar.jpg" data-testid="avatar" alt="User profile pic" />
<img
src="logo.jpg"
data-testid="logo"
alt="Company logo"
aria-describedby="t1"
/>
<span id="t1" role="presentation">The logo of Our Company</span>
<img
src="logo.jpg"
data-testid="logo2"
alt="Company logo"
aria-description="The logo of Our Company"
/>
await expect.element(getByTestId('link')).toHaveAccessibleDescription();
await expect
.element(getByTestId('link'))
.toHaveAccessibleDescription('A link to start over');
await expect
.element(getByTestId('link'))
.not.toHaveAccessibleDescription('Home page');
await expect
.element(getByTestId('extra-link'))
.not.toHaveAccessibleDescription();
await expect.element(getByTestId('avatar')).not.toHaveAccessibleDescription();
await expect
.element(getByTestId('logo'))
.not.toHaveAccessibleDescription('Company logo');
await expect
.element(getByTestId('logo'))
.toHaveAccessibleDescription('The logo of Our Company');
await expect
.element(getByTestId('logo2'))
.toHaveAccessibleDescription('The logo of Our Company');
toHaveAccessibleErrorMessage
function toHaveAccessibleErrorMessage(message?: string | RegExp): Promise<void>;
这允许你断言元素拥有预期的可访问错误消息。
你可以传入预期可访问错误消息的精确字符串。 或者,你可以通过传入正则表达式,或使用 expect.stringContaining
或 expect.stringMatching
来执行部分匹配。
<input
aria-label="Has Error"
aria-invalid="true"
aria-errormessage="error-message"
/>
<div id="error-message" role="alert">This field is invalid</div>
<input aria-label="No Error Attributes" />
<input
aria-label="Not Invalid"
aria-invalid="false"
aria-errormessage="error-message"
/>
// 具有有效错误消息的输入
await expect
.element(getByRole('textbox', { name: 'Has Error' }))
.toHaveAccessibleErrorMessage();
await expect
.element(getByRole('textbox', { name: 'Has Error' }))
.toHaveAccessibleErrorMessage('This field is invalid');
await expect
.element(getByRole('textbox', { name: 'Has Error' }))
.toHaveAccessibleErrorMessage(/invalid/i);
await expect
.element(getByRole('textbox', { name: 'Has Error' }))
.not.toHaveAccessibleErrorMessage('This field is absolutely correct!');
// 没有有效错误消息的输入
await expect
.element(getByRole('textbox', { name: 'No Error Attributes' }))
.not.toHaveAccessibleErrorMessage();
await expect
.element(getByRole('textbox', { name: 'Not Invalid' }))
.not.toHaveAccessibleErrorMessage();
toHaveAccessibleName
function toHaveAccessibleName(name?: string | RegExp): Promise<void>;
这允许你断言元素拥有预期的可访问名称。例如,这对于断言表单元素和按钮是否正确标记很有用。
你可以传入预期可访问名称的精确字符串,也可以通过传入正则表达式,或使用expect.stringContaining
或 expect.stringMatching
来进行部分匹配。
<img data-testid="img-alt" src="" alt="Test alt" />
<img data-testid="img-empty-alt" src="" alt="" />
<svg data-testid="svg-title"><title>Test title</title></svg>
<button data-testid="button-img-alt"><img src="" alt="Test" /></button>
<p><img data-testid="img-paragraph" src="" alt="" /> Test content</p>
<button data-testid="svg-button"><svg><title>Test</title></svg></p>
<div><svg data-testid="svg-without-title"></svg></div>
<input data-testid="input-title" title="test" />
await expect.element(getByTestId('img-alt')).toHaveAccessibleName('Test alt');
await expect.element(getByTestId('img-empty-alt')).not.toHaveAccessibleName();
await expect
.element(getByTestId('svg-title'))
.toHaveAccessibleName('Test title');
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
function toHaveAttribute(attribute: string, value?: unknown): Promise<void>;
这允许你检查给定元素是否具有某个属性。你还可以选择检查该属性是否具有特定的预期值,或使用 expect.stringContaining
或 expect.stringMatching
来进行部分匹配。
<button data-testid="ok-button" type="submit" disabled>ok</button>
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
function toHaveClass(
...classNames: string[],
options?: { exact: boolean }
): Promise<void>;
function toHaveClass(...classNames: (string | RegExp)[]): Promise<void>;
这允许你检查给定元素在其 class
属性中是否具有某些类。你必须至少提供一个类,除非你断言元素不包含任何类。
类名列表可以包含字符串和正则表达式。正则表达式会针对目标元素中的每个单独类进行匹配,而不是针对其完整的 class
属性值进行整体匹配。
WARNING
请注意,当只提供正则表达式时,不能使用 exact: true
选项。
<button data-testid="delete-button" class="btn extra btn-danger">
Delete item
</button>
<button data-testid="no-classes">No Classes</button>
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/);
// ⚠️ 正则表达式匹配单个类,而不是整个 classList
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
function toHaveFocus(): Promise<void>;
这允许你断言元素是否拥有焦点。
<div><input type="text" data-testid="element-to-focus" /></div>
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
function toHaveFormValues(
expectedValues: Record<string, unknown>
): Promise<void>;
这允许你检查表单或字段集是否包含每个给定名称的表单控件,并拥有指定的值。
TIP
需要强调的是,此匹配器只能在 form 或 fieldset 元素上调用。
这使得它能够利用 form
和 fieldset
中的 .elements
属性来可靠地获取其中的所有表单控件。
这也避免了用户提供包含多个 form
的容器的可能性,从而导致混合了不相关的表单控件,甚至可能相互冲突。
此匹配器抽象了根据表单控件类型获取表单控件值的具体细节。例如,<input>
元素拥有 value
属性,但 <select>
元素没有。以下是涵盖的所有情况列表:
<input type="number">
元素返回的值为数字,而不是字符串。<input type="checkbox">
元素:- 如果只有一个具有给定
name
属性的复选框,则将其视为布尔值:如果复选框被选中,则返回true
;如果未选中,则返回false
。 - 如果有多个具有相同
name
属性的复选框,则它们都被集体视为单个表单控件,该控件将值作为数组返回,其中包含集合中所有选定复选框的值。
- 如果只有一个具有给定
<input type="radio">
元素都按name
属性分组,并且此类组被视为单个表单控件。此表单控件将值作为字符串返回,对应于组中选定单选按钮的value
属性。<input type="text">
元素将值作为字符串返回。这也适用于具有任何其他可能未在上述不同规则中明确涵盖的type
属性的<input>
元素(例如search
、email
、date
、password
、hidden
等)。- 不带
multiple
属性的<select>
元素将值作为字符串返回,对应于选定option
的value
属性,如果没有选定选项,则返回undefined
。 <select multiple>
元素将值作为数组返回,其中包含所有 选定选项 的值。<textarea>
元素将其值作为字符串返回。该值对应于它们的节点内容。
上述规则使得,例如,从使用单个选择控件切换到使用一组单选按钮变得容易。或者从多选控件切换到使用一组复选框。此匹配器用于比较的最终表单值集将是相同的。
<form data-testid="login-form">
<input type="text" name="username" value="jane.doe" />
<input type="password" name="password" value="12345678" />
<input type="checkbox" name="rememberMe" checked />
<button type="submit">Sign in</button>
</form>
await expect.element(getByTestId('login-form')).toHaveFormValues({
username: 'jane.doe',
rememberMe: true,
});
toHaveStyle
function toHaveStyle(css: string | Partial<CSSStyleDeclaration>): Promise<void>;
这允许你检查某个元素是否设置了某些特定的 CSS 属性和特定值。它仅在元素设置了所有预期属性时才匹配,而不仅仅是其中一些。
<button
data-testid="delete-button"
style="display: none; background-color: red"
>
Delete item
</button>
const button = getByTestId('delete-button');
await expect.element(button).toHaveStyle('display: none');
await expect.element(button).toHaveStyle({ display: 'none' });
await expect.element(button).toHaveStyle(`
background-color: red;
display: none;
`);
await expect.element(button).toHaveStyle({
backgroundColor: 'red',
display: 'none',
});
await expect.element(button).not.toHaveStyle(`
background-color: blue;
display: none;
`);
await expect.element(button).not.toHaveStyle({
backgroundColor: 'blue',
display: 'none',
});
这也适用于通过类名应用于元素的规则,这些规则在文档中当前活动的样式表中定义了规则。CSS 优先级的一般规则适用。
toHaveTextContent
function toHaveTextContent(
text: string | RegExp,
options?: { normalizeWhitespace: boolean }
): Promise<void>;
这允许你检查给定节点是否拥有文本内容。这支持元素,也支持文本节点和片段。
当传入 string
参数时,它将对节点内容进行部分区分大小写的匹配。
要进行不区分大小写的匹配,你可以使用带有 /i
修饰符的 RegExp
。
如果你想匹配整个内容,你可以使用 RegExp
实现。
<span data-testid="text-content">Text Content</span>
const element = getByTestId('text-content');
await expect.element(element).toHaveTextContent('Content');
// 匹配整个内容
await expect.element(element).toHaveTextContent(/^Text Content$/);
// 使用不区分大小写的匹配
await expect.element(element).toHaveTextContent(/content$/i);
await expect.element(element).not.toHaveTextContent('content');
toHaveValue
function toHaveValue(value: string | string[] | number | null): Promise<void>;
这允许你检查给定表单元素是否拥有指定值。 它接受 <input>
、<select>
和 <textarea>
元素,但 <input type="checkbox">
和 <input type="radio">
除外,它们只能使用 toBeChecked
或 toHaveFormValues
进行有意义的匹配。
它还接受角色为 meter
、progressbar
、slider
或 spinbutton
的元素,并检查其 aria-valuenow
属性(作为数字)。
对于所有其他表单元素,值使用与 toHaveFormValues
相同的算法匹配。
<input type="text" value="text" data-testid="input-text" />
<input type="number" value="5" data-testid="input-number" />
<input type="text" data-testid="input-empty" />
<select multiple data-testid="select-number">
<option value="first">First Value</option>
<option value="second" selected>Second Value</option>
<option value="third" selected>Third Value</option>
</select>
const textInput = getByTestId('input-text');
const numberInput = getByTestId('input-number');
const emptyInput = getByTestId('input-empty');
const selectInput = getByTestId('select-number');
await expect.element(textInput).toHaveValue('text');
await expect.element(numberInput).toHaveValue(5);
await expect.element(emptyInput).not.toHaveValue();
await expect.element(selectInput).toHaveValue(['second', 'third']);
toHaveDisplayValue
function toHaveDisplayValue(
value: string | RegExp | (string | RegExp)[]
): Promise<void>;
这允许你检查给定表单元素是否拥有指定的显示值(最终用户将看到的值)。它接受 <input>
、<select>
和 <textarea>
元素,但 <input type="checkbox">
和 <input type="radio">
除外,它们只能使用 toBeChecked
或 toHaveFormValues
进行有意义的匹配。
<label for="input-example">First name</label>
<input type="text" id="input-example" value="Luca" />
<label for="textarea-example">Description</label>
<textarea id="textarea-example">An example description here.</textarea>
<label for="single-select-example">Fruit</label>
<select id="single-select-example">
<option value="">Select a fruit...</option>
<option value="banana">Banana</option>
<option value="ananas">Ananas</option>
<option value="avocado">Avocado</option>
</select>
<label for="multiple-select-example">Fruits</label>
<select id="multiple-select-example" multiple>
<option value="">Select a fruit...</option>
<option value="banana" selected>Banana</option>
<option value="ananas">Ananas</option>
<option value="avocado" selected>Avocado</option>
</select>
const input = page.getByLabelText('First name');
const textarea = page.getByLabelText('Description');
const selectSingle = page.getByLabelText('Fruit');
const selectMultiple = page.getByLabelText('Fruits');
await expect.element(input).toHaveDisplayValue('Luca');
await expect.element(input).toHaveDisplayValue(/Luc/);
await expect
.element(textarea)
.toHaveDisplayValue('An example description here.');
await expect.element(textarea).toHaveDisplayValue(/example/);
await expect.element(selectSingle).toHaveDisplayValue('Select a fruit...');
await expect.element(selectSingle).toHaveDisplayValue(/Select/);
await expect.element(selectMultiple).toHaveDisplayValue([/Avocado/, 'Banana']);
toBeChecked
function toBeChecked(): Promise<void>;
这允许你检查给定元素是否处于选中状态。它接受类型为 checkbox
或 radio
的 input
元素,以及 role
为 checkbox
、radio
或 switch
且 aria-checked
属性为 "true"
或 "false"
的元素。
<input type="checkbox" checked data-testid="input-checkbox-checked" />
<input type="checkbox" data-testid="input-checkbox-unchecked" />
<div role="checkbox" aria-checked="true" data-testid="aria-checkbox-checked" />
<div
role="checkbox"
aria-checked="false"
data-testid="aria-checkbox-unchecked"
/>
<input type="radio" checked value="foo" data-testid="input-radio-checked" />
<input type="radio" value="foo" data-testid="input-radio-unchecked" />
<div role="radio" aria-checked="true" data-testid="aria-radio-checked" />
<div role="radio" aria-checked="false" data-testid="aria-radio-unchecked" />
<div role="switch" aria-checked="true" data-testid="aria-switch-checked" />
<div role="switch" aria-checked="false" data-testid="aria-switch-unchecked" />
const inputCheckboxChecked = getByTestId('input-checkbox-checked');
const inputCheckboxUnchecked = getByTestId('input-checkbox-unchecked');
const ariaCheckboxChecked = getByTestId('aria-checkbox-checked');
const ariaCheckboxUnchecked = getByTestId('aria-checkbox-unchecked');
await expect.element(inputCheckboxChecked).toBeChecked();
await expect.element(inputCheckboxUnchecked).not.toBeChecked();
await expect.element(ariaCheckboxChecked).toBeChecked();
await expect.element(ariaCheckboxUnchecked).not.toBeChecked();
const inputRadioChecked = getByTestId('input-radio-checked');
const inputRadioUnchecked = getByTestId('input-radio-unchecked');
const ariaRadioChecked = getByTestId('aria-radio-checked');
const ariaRadioUnchecked = getByTestId('aria-radio-unchecked');
await expect.element(inputRadioChecked).toBeChecked();
await expect.element(inputRadioUnchecked).not.toBeChecked();
await expect.element(ariaRadioChecked).toBeChecked();
await expect.element(ariaRadioUnchecked).not.toBeChecked();
const ariaSwitchChecked = getByTestId('aria-switch-checked');
const ariaSwitchUnchecked = getByTestId('aria-switch-unchecked');
await expect.element(ariaSwitchChecked).toBeChecked();
await expect.element(ariaSwitchUnchecked).not.toBeChecked();
toBePartiallyChecked
function toBePartiallyChecked(): Promise<void>;
这允许你检查给定元素是否处于部分选中状态。它接受类型为 checkbox
的 input
元素和 role
为 checkbox
且 aria-checked="mixed"
的元素,或者 indeterminate
设置为 true
的类型为 checkbox
的 input
元素。
<input type="checkbox" aria-checked="mixed" data-testid="aria-checkbox-mixed" />
<input type="checkbox" checked data-testid="input-checkbox-checked" />
<input type="checkbox" data-testid="input-checkbox-unchecked" />
<div role="checkbox" aria-checked="true" data-testid="aria-checkbox-checked" />
<div
role="checkbox"
aria-checked="false"
data-testid="aria-checkbox-unchecked"
/>
<input type="checkbox" data-testid="input-checkbox-indeterminate" />
const ariaCheckboxMixed = getByTestId('aria-checkbox-mixed');
const inputCheckboxChecked = getByTestId('input-checkbox-checked');
const inputCheckboxUnchecked = getByTestId('input-checkbox-unchecked');
const ariaCheckboxChecked = getByTestId('aria-checkbox-checked');
const ariaCheckboxUnchecked = getByTestId('aria-checkbox-unchecked');
const inputCheckboxIndeterminate = getByTestId('input-checkbox-indeterminate');
await expect.element(ariaCheckboxMixed).toBePartiallyChecked();
await expect.element(inputCheckboxChecked).not.toBePartiallyChecked();
await expect.element(inputCheckboxUnchecked).not.toBePartiallyChecked();
await expect.element(ariaCheckboxChecked).not.toBePartiallyChecked();
await expect.element(ariaCheckboxUnchecked).not.toBePartiallyChecked();
inputCheckboxIndeterminate.element().indeterminate = true;
await expect.element(inputCheckboxIndeterminate).toBePartiallyChecked();
toHaveRole
function toHaveRole(role: ARIARole): Promise<void>;
这允许你断言元素拥有预期的 角色。
这在已通过角色之外的其他查询访问元素时,并且想要对其可访问性进行额外断言的情况下很有用。
该角色可以与显式角色(通过 role
属性)匹配,也可以通过 隐式 ARIA 语义 匹配隐式角色。
<button data-testid="button">Continue</button>
<div role="button" data-testid="button-explicit">Continue</button>
<button role="switch button" data-testid="button-explicit-multiple">Continue</button>
<a href="/about" data-testid="link">About</a>
<a data-testid="link-invalid">Invalid link<a/>
await expect.element(getByTestId('button')).toHaveRole('button');
await expect.element(getByTestId('button-explicit')).toHaveRole('button');
await expect
.element(getByTestId('button-explicit-multiple'))
.toHaveRole('button');
await expect
.element(getByTestId('button-explicit-multiple'))
.toHaveRole('switch');
await expect.element(getByTestId('link')).toHaveRole('link');
await expect.element(getByTestId('link-invalid')).not.toHaveRole('link');
await expect.element(getByTestId('link-invalid')).toHaveRole('generic');
WARNING
角色通过字符串相等进行字面匹配,不会继承 ARIA 角色层次结构。因此,查询 checkbox
等超类角色将不包括 switch
等子类角色。
另请注意,与 testing-library
不同,Vitest 会忽略所有自定义角色,仅保留第一个有效角色,遵循 Playwright 的行为:
<div data-testid="switch" role="switch alert"></div>;
await expect.element(getByTestId('switch')).toHaveRole('switch'); // ✅
await expect.element(getByTestId('switch')).toHaveRole('alert'); // ❌
toHaveSelection
function toHaveSelection(selection?: string): Promise<void>;
这允许断言元素拥有 文本选择。
这对于检查元素中是否存在选中的文本或部分文本很有用。该元素可以是文本类型的输入框、文本区域,或任何其他包含文本的元素,例如段落、span、div 等。
WARNING
预期选择是字符串形式,不支持检查选择范围索引。
<div>
<input type="text" value="text selected text" data-testid="text" />
<textarea data-testid="textarea">text selected text</textarea>
<p data-testid="prev">prev</p>
<p data-testid="parent">
text <span data-testid="child">selected</span> text
</p>
<p data-testid="next">next</p>
</div>
getByTestId('text').element().setSelectionRange(5, 13);
await expect.element(getByTestId('text')).toHaveSelection('selected');
getByTestId('textarea').element().setSelectionRange(0, 5);
await expect.element('textarea').toHaveSelection('text ');
const selection = document.getSelection();
const range = document.createRange();
selection.removeAllRanges();
selection.empty();
selection.addRange(range);
// 子元素的选中状态也适用于父元素
range.selectNodeContents(getByTestId('child').element());
await expect.element(getByTestId('child')).toHaveSelection('selected');
await expect.element(getByTestId('parent')).toHaveSelection('selected');
// 选中范围从 prev 开始,涵盖父元素中子元素之前的文本,以及子元素的一部分。
range.setStart(getByTestId('prev').element(), 0);
range.setEnd(getByTestId('child').element().childNodes[0], 3);
await expect.element(queryByTestId('prev')).toHaveSelection('prev');
await expect.element(queryByTestId('child')).toHaveSelection('sel');
await expect.element(queryByTestId('parent')).toHaveSelection('text sel');
await expect.element(queryByTestId('next')).not.toHaveSelection();
// 选中范围从子元素的一部分开始,涵盖父元素中子元素之后的文本,以及 next 的一部分。
range.setStart(getByTestId('child').element().childNodes[0], 3);
range.setEnd(getByTestId('next').element().childNodes[0], 2);
await expect.element(queryByTestId('child')).toHaveSelection('ected');
await expect.element(queryByTestId('parent')).toHaveSelection('ected text');
await expect.element(queryByTestId('prev')).not.toHaveSelection();
await expect.element(queryByTestId('next')).toHaveSelection('ne');