Assertion
expect.element is the API for Locator assertions in Browser Mode. It accepts a Locator and returns a chainable assertion object. Unlike expect(value) which primarily compares JS values, expect.element targets the real element state on the page, suitable for verifying visibility, text, form values, count, and other user-observable results.
With the Playwright provider, all expect.element matchers automatically retry until the assertion passes or the timeout is reached. You do not need to write manual waitFor or polling logic — just assert the expected state and the framework handles the waiting.
When you import @rstest/browser in a Browser Mode test (e.g., importing page), expect automatically gains the element capability. This allows queries, interactions, and assertions to be organized around the same Locator, focusing failure messages on element state.
The following minimal example demonstrates common usage: first assert visibility, then verify a checked state change, and finally validate an element attribute.
Type signature
Auto-retry and timeout
Assertion behavior in expect.element depends on the provider. With the Playwright provider, expect.element matchers are web-first assertions: they continuously retry within the timeout duration until the assertion passes or times out. You do not need to write manual waitFor or polling logic.
Timeout priority
The assertion timeout is determined by the following rules:
- Per-assertion timeout: The
timeoutparameter passed directly to the matcher, highest priority. This includes0; with Playwright, an explicit0means no matcher-specific deadline when there is no finite active test or hook deadline. Inside a finite non-concurrent test or hook, it is capped by the remaining scope timeout. - Configured poll timeout: When no matcher timeout is provided, the assertion uses
expect.poll.timeout. Its default is1000msin Node Mode and5000msin Browser Mode. - Remaining test or hook timeout: Outside concurrent flows, an implicit assertion timeout is capped by the active test or hook deadline, so it cannot outlive that scope. Browser Mode tests default to
15000ms.
Outside concurrent flows, the effective timeout is capped by the remaining active test or hook timeout. With no per-assertion override, it is the smaller of expect.poll.timeout and that remaining time. With an explicit matcher timeout, it is the smaller of the requested timeout and that remaining time. The Browser Mode 5000ms default gives asynchronous UI updates more time without making a failed assertion wait for the full testTimeout. If there is no finite active test or hook deadline, an explicit timeout: 0 retains Playwright's no-deadline behavior.
For test.concurrent, use the test-scoped expect fixture (async ({ expect }) => ...) when the assertion must use that test's remaining deadline. The shared file-level expect uses the configured poll timeout because browsers cannot identify concurrent async flows reliably. The same limitation applies to hooks inside describe.concurrent; their assertions use the poll timeout and can still be ended by the enclosing hook timeout.
Assertion timeout is independent from the browser RPC transport timeout. During test execution, browser and snapshot RPC requests do not add another timeout. Framework setup and file-cleanup RPCs use a separate 30-second watchdog.
Failure output
When an assertion times out, the error message includes: the expected element state, the actual state, and the timeout duration. For example:
Use cases
Auto-retry is especially useful for handling async rendering scenarios — such as waiting for a loading indicator to disappear or UI changes after data finishes loading:
Input constraints
locatormust be aLocatorreturned by@rstest/browser. This allows the runtime to recognize and replay the complete query chain; hand-crafted objects or locators from other libraries cannot be parsed by this mechanism.expect.elementis not available outside of Browser Mode. It relies on Browser Mode's browser runtime and communication channel to execute element assertions; pure Node test environments only support regularexpect(value)assertions.
Screenshot matching
toMatchScreenshot
With the Playwright provider, toMatchScreenshot() captures either a Locator or the page and compares it with a PNG reference. The first run creates a reference when snapshot updates allow new snapshots. Later runs compare against that reference; use --update to replace a mismatched reference.
The matcher accepts an optional reference name and capture or comparison options.
Pass a Locator or the page object from @rstest/browser. The matcher is only available with the Playwright provider and does not support .not.
By default, the current test name identifies the reference. Pass a name to share a reference across assertions or give multiple screenshots in one test distinct names:
Unnamed assertions in the same test are assigned distinct references automatically. In test.concurrent, use the test-scoped expect fixture (async ({ expect }) => ...) to use the test name; if you use the shared file-level expect, provide a name explicitly.
The second argument controls comparison and capture. For example, allow up to 20 differing pixels, wait up to one second for a stable capture, and mask a timestamp that changes between runs:
threshold sets the per-pixel color distance from 0 (strict) to 1 (loose). maxDiffPixels sets an absolute limit on differing pixels; use maxDiffPixelRatio instead to set a fraction of the image. The defaults allow no differing pixels after applying the default threshold of 0.1. Set either maxDiffPixels or maxDiffPixelRatio, not both.
stabilityTimeout defaults to 2000 ms. Set it to 0 to skip stability checks; positive values must be at least 100 ms so Rstest can take a follow-up capture. Rstest disables animations during capture. Use mask for dynamic content, maskColor to change the overlay color, or style to inject CSS before capture. The remaining capture options (caret, scale, and omitBackground) are passed to Playwright's screenshot API. fullPage is available only for page assertions.
Element screenshots
Pass a Locator to capture only the matched element:
Page screenshots
Pass the page object from @rstest/browser to capture the current viewport. Set fullPage: true to capture the full page:
References are PNG files under __screenshots__, grouped by project and test file. Their names include the test name, browser, and operating system. On a mismatch, Rstest reports the reference, actual screenshot, and pixel diff; the actual and diff images are written to a sibling __failures__ directory. Screenshot capture uses the remaining active test timeout. With testTimeout: 0, there is no test deadline, but stabilityTimeout still applies.
Set project-wide matcher defaults and reference paths with browser.expect.toMatchScreenshot.
Element assertions
The matchers listed below represent the currently supported subset. Matchers not listed here are not yet available.
not
- Type:
BrowserElementExpect
Negates the subsequent assertion.
All the following matchers support an optional options?: { timeout?: number } parameter (unless the type signature states otherwise).
toBeVisible
- Type:
(options?: { timeout?: number }) => Promise<void>
The Locator resolves to a mounted and visible element within the timeout.
Visibility is determined by: the element has a non-empty bounding box and visibility is not hidden. For example, display: none or zero dimensions are not considered visible; opacity: 0 is still considered visible.
toBeHidden
- Type:
(options?: { timeout?: number }) => Promise<void>
The Locator meets any of the following conditions within the timeout: does not match any DOM node, or matches a non-visible node.
Think of it as the opposite of toBeVisible; for example, setting only opacity: 0 typically will not cause toBeHidden to pass.
toBeEnabled
- Type:
(options?: { timeout?: number }) => Promise<void>
The element is not in a disabled state.
Disabled determination: a native form control with the disabled attribute, within a disabled fieldset, or within an aria-disabled=true semantic context may all be considered disabled.
toBeDisabled
- Type:
(options?: { timeout?: number }) => Promise<void>
The element is determined to be disabled.
The determination rules are the same as toBeEnabled, just with the opposite result. Recommended for scenarios like button submission and preventing duplicate clicks.
toBeChecked
- Type:
(options?: { timeout?: number }) => Promise<void>
The checked state of a checkbox or radio is true.
Commonly used to verify user check actions or whether a default checked state has taken effect.
toBeUnchecked
- Type:
(options?: { timeout?: number }) => Promise<void>
The checked state of a checkbox or radio is false.
Suitable for use with check / uncheck to verify state changes before and after interaction.
toBeAttached
- Type:
(options?: { timeout?: number }) => Promise<void>
The node pointed to by the Locator is connected to a Document or ShadowRoot (equivalent to Node.isConnected === true).
This assertion only checks "whether it is in the DOM tree" and does not require the element to be visible.
toBeDetached
- Type:
(options?: { timeout?: number }) => Promise<void>
The Locator no longer points to a connected DOM node.
Common for verification after conditional rendering, async unmounting, or delete operations.
toBeEditable
- Type:
(options?: { timeout?: number }) => Promise<void>
The element is both enabled and not readonly.
Readonly determination includes both the native readonly attribute and aria-readonly=true semantics.
toBeFocused
- Type:
(options?: { timeout?: number }) => Promise<void>
The element is the current document's focus target (active element).
Suitable for verifying keyboard navigation, auto-focus, or form focus-switching behavior.
toBeEmpty
- Type:
(options?: { timeout?: number }) => Promise<void>
An editable element's content is empty, or a regular DOM node has no text content.
It checks "whether the content is empty", not "whether the element exists" or "whether it is visible".
toBeInViewport
- Type:
(options?: { timeout?: number; ratio?: number }) => Promise<void>
The element intersects with the viewport (based on Intersection Observer semantics).
ratio represents the minimum intersection ratio; for example, ratio: 0.5 means at least half of the area is within the viewport.
toHaveText
- Type:
(text: string | RegExp, options?: { timeout?: number }) => Promise<void>
The element's text fully matches the expected value (supports string / RegExp).
Text computation includes nested child element content. When the expected value is a string, whitespace and line breaks are normalized before matching.
toContainText
- Type:
(text: string | RegExp, options?: { timeout?: number }) => Promise<void>
The element's text contains the expected substring, or matches the given regex.
The difference from toHaveText is: toContainText does substring matching, while toHaveText does full matching.
toHaveValue
- Type:
(value: string | RegExp, options?: { timeout?: number }) => Promise<void>
The form control's current value matches the expected value (supports string / RegExp).
Applicable to input, textarea, select, and other elements with retrievable values.
toHaveId
- Type:
(value: string | RegExp, options?: { timeout?: number }) => Promise<void>
The element's id matches the expected value (supports string / RegExp).
Suitable for validating dynamically generated IDs or fixed IDs injected after component mounting.
toHaveClass
- Type:
(value: string | RegExp, options?: { timeout?: number }) => Promise<void>
The element's class attribute matches the expected value (supports string / RegExp).
When passing a string, it matches against the entire class string. If you only care about a specific class, consider using a more targeted regex.
toHaveAttribute
- Type:
(name: string, options?: { timeout?: number }) => Promise<void>(name: string, value: string | RegExp, options?: { timeout?: number }) => Promise<void>
When only name is passed, it asserts the attribute exists. When name + value is passed, it asserts the attribute value matches (value supports string / RegExp).
Common use cases include validating structural attributes like type, disabled, aria-*, etc.
toHaveCount
- Type:
(count: number, options?: { timeout?: number }) => Promise<void>
The number of elements resolved by the Locator exactly matches count.
Suitable for list rendering, filter results, pagination item counts, and similar scenarios.
toHaveCSS
- Type:
(name: string, value: string | RegExp, options?: { timeout?: number }) => Promise<void>
The specified CSS property value in the element's computed style matches the expected value.
name must be a non-empty string; value supports string / RegExp.
toHaveJSProperty
- Type:
(name: string, value: unknown, options?: { timeout?: number }) => Promise<void>
The JS property on the element matches the expected value.
name must be a non-empty string; value must be JSON-serializable (assertion parameters are transmitted through the Browser Mode channel).