Core concepts

Build a test from apps, locators, actions and checks, then understand its result.

This page connects the parts of your first test, from the apps in its config to the result in its run folder. Use it to understand what Retest prepares for you and what your test must check.

Apps and targets Link to Apps and targets

An app is a named website, native app or Electron app your test uses. A target says how to run it. For a website, the target chooses the browser and any viewport or device emulation.

retest.config.ts
import { app, chrome, chromium, defineConfig } from '@rehearsal-labs/retest'const config = defineConfig({  apps: {    web: app({      baseUrl: 'http://localhost:3000',      targets: {        chrome: chrome(),        chromium: chromium(),      },    }),  },})export default configdeclare module '@rehearsal-labs/retest' {  interface Register {    config: typeof config  }}

This config runs each test once in Chrome and once in Chromium. Chrome uses the installed browser; Chromium needs its executable path in RETEST_CHROMIUM or executablePath. Each pairing, such as web=chrome, is a variant with its own result. The registration block lets TypeScript check your app names.

With one app, the test receives it as page. With several apps, you declare the names the test uses and receive a fixture for each. Browser apps get separate contexts, so two participants have separate cookies and storage. Configuration explains how to pair their targets.

Tests and fixtures Link to Tests and fixtures

A test file ends in .retest.ts. Import test to declare a test and expect to check a result. The callback receives fixtures, the objects Retest prepares for that test:

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')})

Here, page is a fresh page in a new browser context for each test, and goto resolves / against the app's baseUrl. Await every action and asynchronous check so the next line runs after it settles. A missing await, unfinished work or no assertions fails the test.

If several tests need to sign in, a test.setup can save a named state that each test restores into its own context. See Signing in and secrets. Retest does not support custom fixtures or test.extend yet.

Locators Link to Locators

A locator describes how to find an element. It keeps a query, not a reference to one node, so Retest finds the element again whenever you act on it or read it. In the task example, getByTestId('save-task') finds the element whose data-testid is save-task.

You can also find elements by role, accessible name, label, text, placeholder or CSS. An action needs one match. If nothing matches, it waits within its budget; if several elements match, it fails as ambiguous. Locators explains how to make the query specific.

Actions and checks Link to Actions and checks

Actions change the app; checks read it. Keeping those jobs separate means Retest can wait for a save to finish without pressing Save again.

KindExampleWhat happens
Navigationpage.goto('/')Opens the page.
Actionpage.getByTestId('save-task').click()Waits until the element is ready, then sends input once.
Locator checkexpect(savedTask).toHaveText('Release checklist')Reads again until it passes or reaches its deadline.
Value checkexpect(title).toBe('Release checklist')Compares a value your test already holds, once.
AI checktest.evaluate({ requirement, evidence })Asks a configured judge to assess the evidence.

A click checks that the element is visible, stable, enabled and uncovered before sending mouse input. Once sent, the action is never repeated automatically. A successful click means input was sent; the following check establishes whether the app showed the result you wanted.

Locator checks have a five-second default budget. The saved-title check can see a loading message several times before the final title appears, without needing a sleep. Use Checks for exact conditions and AI checks when the requirement needs interpretation.

Results Link to Results

A test result says whether the test passed, failed or could not establish an answer. When something goes wrong, its failure class names the problem, such as not_found or outcome_unknown.

Test statusMeaning
passedThe test met its required checks and finished successfully.
failedA check, action or test-code rule failed, or the test ran out of time.
inconclusiveA required AI check could not decide from its evidence.
errorA problem prevented the test from completing its checks.
not_runThe test could not start, for example because its setup failed.
skippedThe test or its group was declared as skipped.

If the browser disappears before input is sent, the failure class is session_lost. When input was sent but Retest cannot confirm what it did, it is outcome_unknown: the browser may have gone, a frame may have covered the element, or the page may have moved to a new document first. Both give the test an error status. Check the app's state before repeating an action whose outcome is unknown.

The run also has an exit code for your shell or CI system. A failed test normally gives 1; setup problems, incomplete checks and undecided results normally give 2. Results and exit codes explains how Retest combines different results.

The run folder Link to The run folder

Each run writes a new folder under .retest/runs/. It keeps events as they happen in events.jsonl and writes result.json when the run ends, alongside logs and captured evidence. The terminal prints the folder so you can inspect it later:

Terminal
npx retest inspect .retest/runs/<time>npx retest inspect .retest/runs/<time> --test "tests/task.retest.ts > saves a task"

Replace <time> with the printed folder name. Inspection reads the saved run and opens no browser. If result.json is missing, Retest rebuilds what it can from the events and marks the run incomplete.

Next steps Link to Next steps