# Test API

> Look up test declarations, page methods, locator actions and matchers.

Source: https://rehearsal.dev/retest/docs/reference/test-api

Use this reference to look up a test option, page method, locator action or matcher. Import these APIs from `@rehearsal-labs/retest`; [Writing tests](https://rehearsal.dev/retest/docs/essentials/writing-tests.md) introduces them with examples.

```ts filename="tests/task.retest.ts"
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')
})
```

The body receives the default app as `page`. Actions wait until they can act, then send input once. Locator checks keep reading until they pass or reach their deadline. Await every asynchronous call; each app accepts one command at a time.

## Test declarations

| Call | Purpose |
| --- | --- |
| `test(name, body)` | Declare a test. Names, including group names, must be unique in a file. |
| `test(name, options, body)` | Declare a test with the options below. |
| `test.skip(name, options?, body)` | Declare a skipped test. No body, pages or dependent setup runs for it. |
| `test.only(name, options?, body)` | Focus the run on marked tests. Refused in CI unless --allow-only is given. |
| `test.describe(name, options?, body)` | Declare a group synchronously. Its callback receives a test object typed with inherited apps. |
| `test.describe.skip(name, options?, body)` | Skip every test in a group. |
| `test.describe.only(name, options?, body)` | Focus on a group, subject to more specific focus inside it. |
| `test.beforeEach(body)` | Run before each test in the file or group, outermost first. |
| `test.afterEach(body)` | Run after each test, innermost first, including ordinary failures. A whole-test timeout skips teardown. |
| `test.for(rows)(name, options?, body)` | Declare one test per object row. $key in the name uses that row's value; body receives context, then row. |
| `test.setup(state, options?, body)` | Declare a top-level setup that saves browser sign-in state after it passes. |
| `test.step(name, body)` | Run a named step and return its callback's result. Await it. |
| `test.evaluate(check)` | Run an AI check using a configured judge. Await it. |

Declare tests, groups and hooks while the module loads. There are no `beforeAll`, `afterAll`, custom fixtures or `test.extend`. `test.skip` declares a test; it has no conditional form inside a running body.

| Test option | Default | Meaning |
| --- | --- | --- |
| `apps` | Inherited apps, or the default app | Non-empty array of configured app names. Context exposes handles under those names instead of page. |
| `tags` | None added | Tags used by --tag. |
| `state` | Fresh browser state | Saved state name, or a map from app names to state names. |
| `locks` | None added | Declared locks held while the test runs. Shared locks prevent overlapping tests within one run. |
| `timeout` | Test budget | This test's budget in positive whole milliseconds. |

Groups pass down `apps`, `tags`, `state` and `locks`; they take no `timeout`. Setup options are only `apps`, containing exactly one app, and `timeout`. A successful setup saves cookies and visited origins' local storage, but no session storage. A failed setup leaves dependent tests `not_run`.

Every test needs an assertion. Catching a Retest failure does not erase it, and teardown failures remain beside the original failure. Use [Signing in and secrets](https://rehearsal.dev/retest/docs/guides/signing-in.md) for a complete setup example.

## Finders and locator selection

Browser pages, frames and web locators share these finders. A finder on a locator searches inside its matches, excluding the matched elements themselves. A locator stores a recipe and finds elements again for every action or check.

| Method | Matching rule |
| --- | --- |
| `getByTestId(id)` | Exact data-testid value. |
| `getByRole(role, { name?, exact? }?)` | ARIA role and optional accessible name. |
| `getByLabel(text, { exact? }?)` | Form field's accessible name. |
| `getByText(text, { exact? }?)` | Innermost element with matching text. |
| `getByPlaceholder(text, { exact? }?)` | Placeholder text. |
| `locator(selector)` | CSS selector. XPath and Playwright selector engines are refused. |
| `first()` | Keep the first match. Locator only. |
| `last()` | Keep the last match. Locator only. |
| `nth(index)` | Keep a match by zero-based index; negative indexes count from the end. Locator only. |

Text and names accept strings or regular expressions. Strings match the whole text with case preserved by default; `exact: false` selects a case-insensitive substring. Regular expressions use their own flags and take no `exact` option. See [Locators](https://rehearsal.dev/retest/docs/essentials/locators.md) for scoping and strictness.

## Page methods

Navigation and actions accept an optional `{ timeout }` in milliseconds. It can shorten the configured navigation or action budget, never lengthen it. The test's time left also limits each call.

| Method | Behaviour |
| --- | --- |
| `goto(url, options?)` | Open an address or inbox link and wait for load. Relative addresses use the app's base URL. |
| `reload(options?)` | Reload and wait for load. |
| `goBack(options?)` | Move back through history and wait for load; fail if no entry exists. |
| `goForward(options?)` | Move forward through history and wait for load; fail if no entry exists. |
| `url()` | Read the recorded address as origin and path, without query or fragment. |
| `title()` | Read the trimmed title, limited to 300 code units. |
| `frame(locator)` | Create a frame handle from an iframe or frame element. Nested frames expose frame(locator) too. |
| `keyboard.press(key, options?)` | Press and release one key or shortcut on the current keyboard focus. |
| `scroll({ x?, y? }, options?)` | Send one wheel event at the viewport centre, in CSS pixels. |
| `dialog(action, options?)` | Arm a dialog wait, run the callback, then return its dialog for an answer. |
| `popup(action, options?)` | Arm a popup wait, run the callback, then return the new page after its first document opens. |
| `close(options?)` | Close a popup returned by popup(). |
| `fileChooser(action, options?)` | Catch the callback's file picker without showing the operating system picker. |
| `download(action, options?)` | Catch the callback's download and return a download handle. |
| `route(pattern, answer)` | Register a fixed mocked response or refusal. Later routes take precedence. |
| `unroute(pattern)` | Remove routes registered with this exact pattern. Fail when none match. |

Frames expose finders and nested `frame`, without navigation methods. Electron exposes the first window's page methods except `goto`, `popup`, `route` and `unroute`. Browser feature limits are listed below.

## Locator actions

An action needs exactly one element. No match waits within the action budget; several matches fail immediately. Retest checks actionability before sending input and does not repeat input after dispatch.

| Method | Behaviour |
| --- | --- |
| `fill(value, options?)` | Replace a field's value with text, a secret handle or an inbox code. |
| `click(options?)` | Click the centre of a visible, stable, enabled, uncovered element. Touch targets receive a tap. |
| `tap(options?)` | Tap an app whose every target has a touch screen. |
| `hover(options?)` | Move the mouse to a visible, stable, uncovered element. Disabled elements may be hovered. |
| `press(key, options?)` | Focus a visible, enabled element, then press and release one key or shortcut. |
| `select(choice, options?)` | Choose by label string or { value }; an array chooses a select element's multiple options. |
| `check(options?)` | Click a checkbox, radio or switch unless already checked; verify the resulting state. |
| `uncheck(options?)` | Click a checkbox or switch unless already unchecked. Choose another radio to clear a radio. |
| `scroll({ x?, y? }, options?)` | Send one wheel event over the element. Omitted axes are zero. |
| `setInputFiles(files, options?)` | Replace a file input's files. An empty array clears it. |
| `dragTo(target, options?)` | Drag between two visible elements in view together on the same page. options takes timeout and steps, a whole number from 1 to 100, defaulting to 5. Frames and touch emulation are unsupported. |

Use `fill` to replace text, or `press` once per character when the app needs each key event. Retest has no key-down state across actions. See [Actions](https://rehearsal.dev/retest/docs/essentials/actions.md) for actionability details.

## Keys and modifiers

| Kind | Accepted names |
| --- | --- |
| Named keys | `Enter`, `Tab`, `Escape`, `Backspace`, `Delete`, `Space`, `ArrowUp`, `ArrowDown`, `ArrowLeft`, `ArrowRight`, `Home`, `End`, `PageUp`, `PageDown`. |
| Modifiers | `Shift`, `Control`, `Alt`, `Meta`, `ControlOrMeta`. |

`press` also accepts one character or modifiers joined to a key with `+`, such as `Shift+Tab` or `ControlOrMeta+A`. Names are case-sensitive. `ControlOrMeta` means Meta on macOS and Control elsewhere. For an uppercase letter alone, use `A` rather than `Shift+A`.

## Dialogs, files and route arguments

| Object or argument | Members and values |
| --- | --- |
| Dialog | `type`, `message`, `defaultValue`, `accept(promptText?)` and `dismiss()`. Answer once; the answer also waits for the opening action to finish. |
| File chooser | `multiple` and `setFiles(files, options?)`. |
| Input files | A path, a payload, or an array of either. Relative paths start from the calling file's folder. |
| File payload | `{ name, content, mimeType? }`, where content is a string or Uint8Array. |
| Download | `suggestedFilename`, `path(options?)` and `saveAs(path, options?)`. Both methods wait for completion; saveAs keeps the run's original copy. |
| Route pattern | Whole-address glob including query, or RegExp. `**` crosses slashes; `*` does not. |
| Route response | `{ status?, headers?, contentType?, body? }`. Default status is 200; body is text or Uint8Array. |
| Route refusal | `{ abort: reason }`, using a supported network error name such as connectionrefused. Cannot accompany response fields. |

A dialog blocks its opening action until you answer it, so `await page.dialog(...)` comes before `await dialog.accept()` or `dismiss()`. The file, dialog and popup methods arm their waits before the callback runs. See [Uploads and downloads](https://rehearsal.dev/retest/docs/guides/files.md), [Dialogs and popups](https://rehearsal.dev/retest/docs/guides/dialogs-and-popups.md) and [Mocking requests](https://rehearsal.dev/retest/docs/guides/mocking-requests.md).

## Native app handles

Native pages and locators have `getByTestId`, `getByRole`, `getByLabel` and `getByText`. Native test ids are accessibility identifiers. Their `locator(step)` takes data instead of CSS.

| Native member | Availability and behaviour |
| --- | --- |
| `locator(step)` | A step with by: testId and value; by: role with role, name? and exact?; or by: label or text with text and exact?. All may include pick: first, last or an index. |
| `first()`, `last()`, `nth(index)` | Select native locator matches using the web indexing rules. |
| `fill`, `press`, `scroll` | Native locator actions; page scroll and keyboard.press are also available. |
| `tap(options?)` | iOS simulator locators. |
| `click(options?)` | macOS locators. |
| `swipe(direction, options?)` | iOS page or locator; up, down, left or right. |
| `keyboard.wait(options?)` | iOS only; wait for the software keyboard. |
| `keyboard.dismiss(options?)` | iOS only; press return once and wait for the keyboard to close. |
| `keyboard.dismissFirstRunCard(options?)` | iOS only; dismiss the sliding-to-type introduction if present. |
| `alert.accept(button, options?)` | Press the native alert button whose label matches exactly. |
| `alert.dismiss(button, options?)` | Press the named native alert button through the dismiss route. |

Native handles have no navigation, CSS, placeholder finder, web select, check or uncheck. Use their tap or click to change a control. Native locators add `toBeSelected()` to the ordinary locator matchers, but have no layout or screenshot comparison matchers.

## Expect and polling

| Call | Behaviour |
| --- | --- |
| `expect(locator)` | Return locator matchers that read again until they pass. |
| `expect(page)` | Return page matchers that read again until they pass. |
| `expect(value)` | Return synchronous value matchers. Await promises before passing their values. |
| `expect.soft(actual)` | Record a failed check and continue. The test still fails at the end. |
| `expect.poll(read, { timeout?, intervals? }?)` | Repeat a read-only callback until its value passes a value matcher. Await the matcher. The final interval repeats. |

Locator and page matchers accept `{ timeout }` to shorten the assertion budget, which defaults to 5000 milliseconds. `.not` is available once on these matchers, apart from screenshot comparisons. Value matchers have no `.not`.

## Locator matchers

| Matcher | Pass condition |
| --- | --- |
| `toBeVisible(options?)` | Exactly one match is visible. Negated, a hidden or absent match passes. |
| `toBeHidden(options?)` | No matches, or none visible. Its negation needs exactly one visible match. |
| `toBeChecked(options?)` | Exactly one checkable match is checked. Mixed is not checked. |
| `toBeEnabled(options?)` | Exactly one match is enabled. |
| `toBeDisabled(options?)` | Exactly one match is disabled, including its inherited aria-disabled state. |
| `toHaveText(text, options?)` | Exactly one match has the whole string, or matches the RegExp. |
| `toHaveText(texts, options?)` | All matches have the array's strings or RegExps, in order. Hidden matches count. |
| `toContainText(text, options?)` | Exactly one match contains the case-sensitive string, or matches the RegExp. |
| `toHaveCount(count, options?)` | Exactly this many elements match, visible or hidden. |
| `toHaveValue(value, options?)` | Exactly one field has the whole string, or matches the RegExp, without whitespace normalisation. |
| `toBeSelected(options?)` | Exactly one native match is selected. Native locators only. |

Text matchers trim both ends and collapse runs of whitespace before comparison. A missing element passes only `.not.toBeVisible()`, `.not.toHaveCount()` and `.not.toHaveText([...])`; other negations need an element. More than 100 matches prevents `toBeHidden()` and the array form of `toHaveText()` from passing.

## Page and layout matchers

| Matcher | Pass condition |
| --- | --- |
| `expect(page).toHaveURL(url, options?)` | Whole address, including query and fragment, equals a string or matches a RegExp. Relative strings resolve against baseUrl. |
| `expect(page).toHaveTitle(title, options?)` | Title equals a whitespace-normalised string or matches a RegExp. |
| `expect(locator).toOverlap(other, options?)` | Visible boxes share more than 4 CSS pixels across and down. Use two separate elements on the same page. |
| `expect(locator).toOverflowViewport(options?)` | Visible box extends more than 1 CSS pixel past the viewport's right edge. |
| `expect(locator).toHaveClippedText(options?)` | Hidden or clipped overflow cuts more than 4 CSS pixels from words. Intentional ellipsis and line clamps do not count. |
| `expect(page).toScrollHorizontally(options?)` | Document extends more than 1 CSS pixel beyond the viewport and does not hide that overflow. |

Layout matchers name defects, so use `.not` to check that the layout is correct. Missing or hidden elements pass neither form. [Layout checks](https://rehearsal.dev/retest/docs/guides/layout-checks.md) explains the measurements and supported engines.

## Screenshot comparisons

`expect(page).toHaveScreenshot(name, options?)` compares the viewport; the same matcher on a locator compares one visible element inside it. Retest waits for unchanged pixels for at least 100 milliseconds before comparing with the saved baseline. Writing a baseline with `--update-screenshots` needs one second of unchanged pixels. A missing baseline fails until you run with `--update-screenshots`.

| Option | Default | Meaning |
| --- | --- | --- |
| `threshold` | 0.2 | Colour distance from 0 to 1 above which a pixel differs. |
| `maxDiffPixels` | No differing pixels allowed | Maximum differing pixels. |
| `maxDiffPixelRatio` | No differing pixels allowed | Maximum differing fraction from 0 to 1. Both limits must hold when both are set. |
| `timeout` | Assertion budget | Shorter comparison budget in milliseconds. |

Screenshot comparisons have no negation, full-page capture or masking option. Use [Screenshot comparisons](https://rehearsal.dev/retest/docs/guides/screenshots.md) to create baselines and read a difference image.

## Value matchers

| Matcher | Comparison |
| --- | --- |
| `toBe(expected)` | Object.is equality. |
| `toEqual(expected)` | Deep comparison of plain objects, arrays, dates, maps and sets; other objects require identity. |
| `toContain(item)` | Case-sensitive substring for strings, or SameValueZero membership for arrays. |
| `toMatch(pattern)` | RegExp match in a string. |

These checks run immediately unless used after `expect.poll`. Polling callbacks must only read because Retest calls them repeatedly; an attempted Retest action inside one fails the test.

## Secrets and inbox handles

| Call or member | Behaviour |
| --- | --- |
| `secret(name)` | Reference a configured secret for fill(). The test never receives its value. |
| `secret(name).name` | Declared secret name, without its value. |
| `inbox(name)` | Reference a configured mailbox. |
| `inbox(name).name` | Mailbox name as declared in the config. |
| `inbox(name).latest({ subject?, timeout? }?)` | Wait for the newest message received since the test started or its last returned message. A subject string matches a case-insensitive substring; RegExp uses its flags. |
| `message.subject`, `from`, `text`, `receivedAt` | Redacted message content and its received time as a Date. |
| `message.code` | First code as a secret handle for fill(). |
| `message.links` | Redacted text, origin, path and token flag for each link. |
| `message.link()` | First token-bearing link, or the first link, as a handle for goto(). |
| `message.link(number)` | Link numbered from 1. |
| `message.link({ path })` | First link whose path equals or lies under the given path. |
| `handle.toString()`, `handle.toJSON()` | Secret, code and link handles return their placeholder when converted to text or JSON. |

Codes can only be filled, and link handles can only be opened through `goto`. The mailbox reader and allowed origins belong in the [config](https://rehearsal.dev/retest/docs/reference/config.md).

## AI check arguments

| Key | Default | Meaning |
| --- | --- | --- |
| `requirement` | Required | Non-empty sentence, or map of criterion ids to sentences or criterion objects. |
| `evidence` | Required | One evidence selector or a non-empty array of selectors. |
| `judge` | Configured default | Judge name; required when there is no default. |
| `context` | None | Reference text given to the judge. |
| `mode` | `required` | required affects the test; advisory only records a warning. |
| `timeoutMs` | Configured evaluation budget | Check budget, bounded by the test's time left. |

Evidence selectors are `{ capture: 'screenshot', app? }`, `{ text, label? }`, `{ recording: { step }, app? }`, `{ recording: { lastMs }, app? }` and `{ diagnostics: 'console' | 'network' | ['console', 'network'], app? }`. Omitted apps use the test's first app.

A criterion object is `{ requirement, kind }`, where kind is `state`, `seen` or `never`. The older `{ requirement, absence: true }` form is also accepted. Required checks remain recorded even when the test catches an error; see [AI checks](https://rehearsal.dev/retest/docs/guides/ai-checks.md) for verdicts and the limits of sampled frames.

## Platform support

Firefox and WebKit do not support uploads, file pickers, downloads, JavaScript dialogs, popups, drag and drop or mocked responses. Frames work on Chrome and Chromium, with no file upload or element screenshot inside them; Electron windows refuse frame locators. Closed shadow roots are inaccessible.

Firefox refuses `select` requests for several options before lookup, even if they are already selected. It also refuses keyboard changes to a multiple select; requesting the one option already selected alone succeeds without input.

Layout matchers are for Chrome and Chromium pages. Screenshot comparisons cover browser viewports, with element comparisons on Chromium. Electron supports uploads and file pickers, but cannot capture downloads through `page.download()` or compare screenshots. Native app screenshots are not compared. The [platform pages](https://rehearsal.dev/retest/docs/platforms/browsers.md) describe further engine limits.

## Next steps

- [Writing tests](https://rehearsal.dev/retest/docs/essentials/writing-tests.md): Build a test from its first action to its final check.
- [Checks](https://rehearsal.dev/retest/docs/essentials/checks.md): Understand waiting, negation and soft failures.
- [Results and exit codes](https://rehearsal.dev/retest/docs/reference/results.md): Read the outcome of each test and run.
