# Best practices

> Write tests that find the right element, wait the right way and give the same answer on every run.

Source: https://rehearsal.dev/retest/best-practices

## Prefer test ids

Find elements with `getByTestId` where you can, then by role and name. Text changes when the copy or the language does. A test id changes only when you change it.

List your ids in the config, and a misspelt id becomes a type error:

```ts filename="retest.config.ts"
testIds: ['task-title', 'save-task', 'saved-task'],
```

## One check per fact

Give each fact the user relies on its own `expect`. When it fails, the card names that one fact, with what the page showed. To see every broken fact in one run, make the checks soft.

```ts
await expect(page.getByTestId('saved-task')).toHaveText('Release checklist')
await expect(page.getByLabel('Title')).toHaveValue('Release checklist')
```

## No sleeps

Retest has no sleep, and a test does not need one. Checks look again until they pass, and actions wait for their element. For a value the page does not show, poll a function that only reads.

```ts
await expect.poll(savedTasks, { timeout: 5000 }).toBe(before + 1)
```

## Secrets through the config

- Never write a password in a test file. Name it with `secret()`, and give its source in the config.
- Read a value that changes, such as a one-time code, with a function source. Retest calls it each time the code is typed.
- In CI, give secrets as environment variables. Retest removes them from the test's environment.
- Keep run folders private. Screenshots are not redacted.

## Sign in once

Sign in with `test.setup` once and start other tests from its `state`. Keep one setup, and one state, for each kind of account. A test that checks the sign-in form itself starts without a state.

## Make your own data

- Retest gives each test a fresh browser, but it never resets your app's data. A test can find what an earlier run left.
- Make the data a test needs, with a value unique to the run, such as a random suffix on a title.
- Find a record by an id the app gave it, not by a title another record may share.
- When tests in different files share something outside the page, such as one inbox or one counter, hold a lock. [Run tests in parallel](https://rehearsal.dev/retest/scaling-up/parallel-runs.md) shows how.

## Keep every run complete

- Remove `test.only` before you push. When `CI` is set, a run with one stops before any test starts.
- Await every action and every check. A test fails when work it started is still running as it returns.
- Read exit code 2 as a run that could not check everything, never as a pass.

## What to give an AI check

- Give it what needs reading, such as whether a message says how to go on, or whether a reply follows a policy.
- Keep facts in `expect`: a total, a title, a ticked box, an address. An AI check does not replace them.
- A screenshot cannot show what reached your server. Check that with `expect.poll` on your API.
- Write the requirement before you look at the result, and name exact words when they matter, as in: The message under Save shows the title "Release checklist", exactly.
- Use `advisory` while you learn how a judge answers: a failed advisory check is a warning. Keep an `expect` beside it, since an advisory check is not an assertion.
