> ## Documentation Index
> Fetch the complete documentation index at: https://e2e.tester.army/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Core concepts

> Goals, assertions, and locators, and how to structure and run tests with them.

A test mixes three types of step:

| Step | API | Model call |
| --- | --- | --- |
| [Goal](#goals) | `agent.act` | Yes, unless the [cache](https://e2e.tester.army/docs/cache) replays it |
| [Assertion](#assertions) | `agent.assert`, `agent.waitFor`, `agent.extract` | Yes |
| [Locator](#locators) | `screen` and `expect` | No |

With a [custom executor](https://e2e.tester.army/docs/executors), `act` and `assert` need no model.

```ts title="tests/todos.e2e.ts"

test('todos survive a filter round-trip', async ({ app, agent, screen }) => {
  await app.open('/todos');

  await agent.act('add todos "Buy milk" and "Walk the dog"');
  await expect(screen.getByTestId('todo')).toHaveCount(2);

  await agent.act('complete "Buy milk"');
  await agent.assert('only one todo remains open');

  await screen.getByRole('tab', 'Done').tap();
  await expect(screen.getByTestId('todo')).toHaveCount(1);
});
```

Await each agent call, screen call, and `expect.poll`. If a test returns
while a step still runs, the test fails with `STEP_NOT_AWAITED`.

## Goals

`agent.act` takes one goal and does the actions it needs. It returns when
the goal is complete and throws when the goal fails.

```ts
await agent.act('complete checkout with the test card');
await agent.act('invite {email} as an editor', { params: { email: 'ada@example.test' } });
```

Keep each goal atomic: one outcome that one check confirms. When two parts
of an instruction each leave a state worth checking, they are two goals.
A test can have many goals.

```ts title="Bad: three goals in one call, no check between them"
await agent.act('open the Pro plan, set the seats to {seats}, and check out', {
  params: { seats: 3 },
});
```

```ts title="Good: one atomic goal per call, each checked before the next"
await agent.act('open the Pro plan');
await expect(screen.getByRole('heading', 'Pro plan')).toBeVisible();

await agent.act('set the seats to {seats}', { params: { seats: 3 } });
await expect(screen.getByRole('status', 'Seats')).toHaveText('3 seats');

await agent.act('check out');
await expect(screen.getByText('Order confirmed')).toBeVisible();
```

A failure then names the goal that broke. Each goal replays from its own
recording and gets its own model-call and action
[budgets](https://e2e.tester.army/docs/reference/agent#budgets). The actions toward one outcome are not
goals: opening the screen or menu that holds a control, scrolling to it,
closing a banner, or filling the fields of one form.

- You control the order of the goals. The agent picks the actions.
- Check each goal's result with an `expect`. The [replay cache](https://e2e.tester.army/docs/cache)
  records a goal only after a later check passes.
- Use the words that the screen shows.
- Give test data in `params`. For a password, give a `Secret`. The model
  never gets its value as text. See [Signing in](https://e2e.tester.army/docs/authentication).
- For a value that changes on each run, use `unique()` so the
  [cache can replay the step](https://e2e.tester.army/docs/cache#what-must-match):

```ts

const email = `ada+${Date.now()}@example.test`;
await agent.act('sign up with email {email}', { params: { email: unique(email) } });
```

`act` gets screenshots when it needs them. On some engines, it can tap and
type at points in the image.

If a goal fails, read the report and make the instruction more specific.
Put words that many goals use in the agent's [`context`](https://e2e.tester.army/docs/agents).

## Assertions

An assertion judges the screen. The model sees the statement, a redacted
text snapshot of the current screen, and the agent's context. It does not
see the earlier steps or the summaries of `act`.

- `agent.assert` examines the screen now. A false statement fails with
  `ASSERTION_FAILED`. When the evidence is not enough, it fails with
  `ASSERTION_INCONCLUSIVE`.
- `agent.waitFor` waits until a condition is true. It calls the model only
  when the screen changes.
- `agent.extract` reads data from the screen into a
  [Standard Schema](https://standardschema.dev) type, for example Zod.

```ts

await agent.assert('the dashboard shows a trial badge');
await agent.waitFor('a download link appears', { timeout: 120_000 });
await agent.assert('the chart trends upward', { vision: true });

const data = await agent.extract('every todo title', {
  schema: z.object({ titles: z.array(z.string()) }),
});
expect(data.titles).toContain('Buy milk');
```

For text that the model wrote, examine the meaning, not the full sentence.
When the exact text is important, use a [locator](#locators).

For a visual check, such as a chart or a layout, add `vision: true`. To
judge only the screenshot, use `vision: 'only'`. Images add input tokens.
See the [agent reference](https://e2e.tester.army/docs/reference/agent#vision).

## Locators

A locator finds a known control, with no model call. Use one when you know
the exact action or the exact value.

```ts
// Role and name first, label second, test ID last.
await screen.getByRole('button', 'Sign in').tap();
await screen.getByLabel('Email').fill('ada@example.test');

// Scope into a row.
const row = screen.getByRole('listitem').filter({ hasText: 'Design review' });
await row.getByRole('button', 'Archive').tap();

// Gestures and coordinates.
await screen.swipe({ direction: 'up' });
await screen.getByRole('image', 'Seat map').tap({ position: { x: 120, y: 48 } });

// Assertions retry until they pass or time out.
await expect(screen.getByRole('status')).toHaveText('2 remaining');
```

- An action, a read, or a single-node assertion fails when its query finds
  more than one node. Use `.filter()`, `.first()`, or `.nth()` to find one
  node.
- A direct read, such as `textContent()`, waits for its match, then reads once
  and does not retry for a value.

See the [screen reference](https://e2e.tester.army/docs/reference/screen) and the
[expect reference](https://e2e.tester.army/docs/reference/expect#locator-matchers). For routes, cookies,
dialogs, and downloads, see [Working with the browser](https://e2e.tester.army/docs/browser).

## Organize tests

```ts

describe('billing', { tags: ['billing'] }, () => {
  beforeEach(async ({ app }) => { await app.open('/billing'); });

  test('upgrades', { retries: 2, timeout: 60_000 }, async ({ agent }) => {
    await agent.act('upgrade to Pro');
  });
});

test('needs two organizations', async ({ app }) => {
  test.skip((await countOrganizations()) < 2, 'nothing to switch to');
  await app.open('/organizations');
});
```

- Each test runs in its own attempt. Start it near the screen it checks, so
  every run skips the clicks that lead there. Create its data in fixtures
  or hooks. In a browser, restore a [saved session](https://e2e.tester.army/docs/authentication) and
  open the screen by its path. On a device, open a
  [deep link](https://e2e.tester.army/docs/mobile#the-device-fixture).
- `test.skip(condition, reason)` stops the test as `skipped` when the
  condition is true.
- For a flow across many tests, set `{ serial: true }` on the group. The
  tests run in sequence on one worker and retry together.
- For setup and cleanup, use `test.extend`. See
  [Writing fixtures](https://e2e.tester.army/docs/fixtures).
- Import `test` from `@e2e-dev/web` or `@e2e-dev/mobile` to type the
  `browser` or `device` fixture.

The [test reference](https://e2e.tester.army/docs/reference/test) shows all options.

## Run one test

<CodeGroup>
```bash npm
npx e2e run tests/signup.e2e.ts:12   # the test at line 12
npx e2e run --grep checkout          # tests with a matching title
npx e2e run --target web             # one target
```

```bash pnpm
pnpm exec e2e run tests/signup.e2e.ts:12   # the test at line 12
pnpm exec e2e run --grep checkout          # tests with a matching title
pnpm exec e2e run --target web             # one target
```

```bash bun
bunx e2e run tests/signup.e2e.ts:12   # the test at line 12
bunx e2e run --grep checkout          # tests with a matching title
bunx e2e run --target web             # one target
```
</CodeGroup>

The [CLI reference](https://e2e.tester.army/docs/reference/cli#e2e-run) shows all flags.

## Next

<CardGroup cols={2}>
  <Card title="Caching" icon="box-archive" href="/cache">
    Replay passed goals with no model calls.
  </Card>
  <Card title="How agent steps work" icon="list-check" href="/agent-steps">
    Verdicts, exit codes, and what the model sees.
  </Card>
</CardGroup>
