Best practices

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

Prefer test ids Link to 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:

retest.config.ts
testIds: ['task-title', 'save-task', 'saved-task'],

One check per fact Link to 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.

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

No sleeps Link to 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.

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

Secrets through the config Link to 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 Link to 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 Link to 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 shows how.

Keep every run complete Link to 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 Link to 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.