Checks
Check an app outcome with retrying assertions, compare values and collect several failures in one test.
After an action, check the outcome you care about, such as a task appearing with the title you saved. This page covers checks that wait for the app, immediate value comparisons and ways to collect several failures.
Check an outcome Link to Check an outcome
Pass a locator to expect, then choose a matcher for its expected state. Locator checks read the page again until they pass or their time runs out, so you do not need a fixed sleep for the save to finish.
import { expect, test } from '@rehearsal-labs/retest'test('saves a task', async ({ page }) => { await page.goto('/') await page.getByTestId('task-title').fill('Release checklist') await page.getByTestId('save-task').click() await expect(page.getByTestId('saved-task')).toHaveText('Release checklist') await expect(page.getByTestId('task-title')).toHaveValue('Release checklist')})The first check waits for the saved task's text. The second checks the title still held by the field. Both are asynchronous, so await them; a plain value check such as expect(2).toBe(2) runs immediately.
Choose a matcher Link to Choose a matcher
Choose a matcher for the fact your test needs to prove. Most locator matchers require exactly one element, while count and list checks can describe several matches.
| Matcher | What it checks |
|---|---|
toBeVisible() | One element matches and is visible. |
toBeHidden() | No element matches, or none of the matches is visible. |
toHaveText(text) | One element has the expected whole text. |
toContainText(text) | One element contains the expected text. |
toHaveValue(value) | One field holds the expected value. |
toHaveCount(number) | The locator has this many matches, hidden ones included. |
toBeChecked() | One checkable control is checked. |
toBeEnabled() or toBeDisabled() | One control has the expected enabled or disabled state. |
toHaveText compares the whole text, while toContainText checks for a substring. Both respect case, trim the ends and collapse runs of spaces or line breaks. toHaveValue compares a field's value without changing its whitespace.
await expect(page.getByTestId('saved-task')).toContainText('checklist')await expect(page.getByTestId('saved-task')).toHaveText(/^Release /)await expect(page.getByRole('listitem')).toHaveText([ 'Release checklist', 'Groceries',])A regular expression searches the normalised text using its own flags. The list form of toHaveText checks every matched element in document order, with exactly as many matches as entries. Use the last example on a task list containing those two items.
Retest reads text and visibility for at most 100 matches. Above that limit, toBeHidden() and toHaveText([...]) cannot pass, so narrow the locator to a smaller group first.
Check the page Link to Check the page
Pass the page itself to expect when you need to check navigation or the document title. These matchers retry too, so they can wait for a redirect or a title update.
await expect(page).toHaveURL('/')await expect(page).toHaveTitle('Tasks')A relative expected URL resolves against the app's base URL. toHaveURL compares the complete address, including its query and fragment, while toHaveTitle compares the whole title with whitespace normalised. Both also accept a regular expression.
Check the opposite Link to Check the opposite
Use .not when an element must no longer have a state or value. Retest needs a look that proves the opposite, so a missing element does not automatically make every negated matcher pass.
await expect(page.getByText('Could not save')).not.toBeVisible()await expect(page.getByTestId('saved-task')).not.toHaveText('Draft')The first check passes if the error is hidden or absent, but two or more matches fail with ambiguous. Use toBeHidden() when every match must be hidden. The second check still needs one matching saved task whose text differs from Draft. If that task never appears, it fails with not_found rather than treating absence as proof of the text.
Set the wait Link to Set the wait
Locator and page checks have a five-second assertion budget by default. A matcher's timeout can shorten that budget, but a larger value cannot extend it or the time left in the test.
await expect(page.getByTestId('saved-task')).toHaveText( 'Release checklist', { timeout: 2000 },)To give checks more time, change timeouts.assertion in your config or override it for a run. This command allows up to ten seconds per assertion, within the test's remaining time.
npx retest run --timeouts assertion=10000When a check fails, the report shows what it expected, what it read and how long it waited. Increasing the budget helps only if the app can reach the expected state; otherwise use Debugging a failure to investigate the mismatch.
Compare values Link to Compare values
Use value matchers for data already in your test, such as an object returned by a helper. They compare immediately because Retest has no page state to reread.
const task = { title: 'Release checklist', tags: ['smoke', 'release'] }expect(task).toEqual({ title: 'Release checklist', tags: ['smoke', 'release'] })expect(task.tags).toContain('release')expect(task.title).toMatch(/^Release/)toEqual compares the object's contents. toBe compares with Object.is, toContain looks in a string or array, and toMatch checks a string with a regular expression. Value matchers have no .not.
Poll a value Link to Poll a value
If a helper reads something that changes over time, pass the function to expect.poll. For example, a server-side task count can confirm that one click caused one save, as in Retest's task examples.
const before = await savedTasks()await page.getByTestId('task-title').fill('Release checklist')await page.getByTestId('save-task').click()await expect.poll(savedTasks, { timeout: 5000 }).toBe(before + 1)Here savedTasks is your helper that reads the count from the app's API. Keep other tests from changing that counter while this test reads it, for example with a lock. The polling function must only read; Retest refuses page actions inside it because polling would repeat them.
Unlike a locator matcher's timeout, expect.poll's timeout sets its own polling budget, still limited by the test's remaining time. Without it, polling uses the assertion budget.
Keep checking after a failure Link to Keep checking after a failure
Use expect.soft for independent checks when seeing several mismatches would help you diagnose the page. A state mismatch records a failure and allows the body to continue, but the test still fails at the end. A problem that prevents reading the page, such as a lost browser, still stops the test.
await expect.soft(page.getByTestId('saved-task')).toHaveText('Release checklist')await expect.soft(page.getByTestId('task-title')).toHaveValue('Release checklist')The report keeps the first failure and lists later ones beside it. Use an ordinary check for a prerequisite, because continuing after a failed sign-in or missing page can obscure the cause.