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

# Quickstart

> Scaffold a project, run a browser test, then add an agent step.

The first run needs no model key. With your app's dev server up, three
commands get a browser test green against it. The agent step comes after.

<Note>
  Node.js 22.12 or newer. On Windows, run everything inside WSL.
</Note>

## Run your first test

<Steps>
  <Step title="Scaffold">
    In your app's directory, with the dev server on `http://localhost:3000`:

    <CodeGroup>
      ```bash npm theme={"theme":"catppuccin-mocha"}
      npx @e2edev/e2e@beta init
      ```

      ```bash pnpm theme={"theme":"catppuccin-mocha"}
      pnpm dlx @e2edev/e2e@beta init
      ```

      ```bash bun theme={"theme":"catppuccin-mocha"}
      bunx @e2edev/e2e@beta init
      ```
    </CodeGroup>

    Accept the defaults: Web (Playwright), the Vercel AI Gateway, and the
    coding-agent skill. `init` adds `@e2edev/e2e`, `@e2edev/playwright`,
    `playwright`, and `ai` to `devDependencies`, a `test:e2e` script, and
    `.gitignore` entries, and writes the config, an example test, and the skill
    files. It never overwrites a file that exists.

    ```ts title="e2e.config.ts" theme={"theme":"catppuccin-mocha"}
    import type { E2EConfig } from '@e2edev/e2e';
    import { createAgent } from '@e2edev/e2e/agent';
    import { playwright } from '@e2edev/playwright';
    import { gateway } from 'ai';

    export default {
      // One model for every agent.* call, checked once when the first test acquires the agent fixture.
      // The Vercel AI Gateway serves the model id and reads AI_GATEWAY_API_KEY.
      // Any AI SDK model works here: openai('gpt-5.6-luna') from @ai-sdk/openai calls the provider directly.
      agents: {
        default: createAgent({
          model: gateway('openai/gpt-5.6-luna'),
          system: 'You are a thorough QA agent. Verify every outcome.',
        }),
      },
      // The engine declares the app it drives; APP_URL overrides the default at run time.
      targets: [{
        // Named after the platform the engine declares: "web".
        engine: playwright({
          url: process.env.APP_URL ?? 'http://localhost:3000',
          // Let the runner start the dev server and wait for url to answer:
          // command: { executable: 'npm', args: ['run', 'dev'] },
        }),
      }],
    } satisfies E2EConfig;
    ```

    ```ts title="tests/example.e2e.ts" theme={"theme":"catppuccin-mocha"}
    import { test } from '@e2edev/playwright';
    import { expect } from '@e2edev/e2e';

    test('app opens', async ({ app, web }) => {
      await app.open('/');
      await expect(web.locator('body')).toBeVisible();
    });

    // Runs once the key the model in e2e.config.ts reads is in the environment:
    // test('the agent drives a flow', async ({ app, agent }) => {
    //   await app.open('/');
    //   await agent.act('one goal in plain language');
    //   await agent.assert('one question about the screen');
    // });
    ```

    A target names the engine that drives it, and the engine declares the app:
    a URL for a browser, a bundle id on a device.
  </Step>

  <Step title="Install">
    <CodeGroup>
      ```bash npm theme={"theme":"catppuccin-mocha"}
      npm install
      ```

      ```bash pnpm theme={"theme":"catppuccin-mocha"}
      pnpm install
      ```

      ```bash bun theme={"theme":"catppuccin-mocha"}
      bun install
      ```
    </CodeGroup>
  </Step>

  <Step title="Run">
    ```bash theme={"theme":"catppuccin-mocha"}
    npx --no-install e2e run
    ```

    The first run downloads a browser, once. Then:

    ```text theme={"theme":"catppuccin-mocha"}
     RUN  e2e v0.11.0 /home/ada/my-app
          run 01a08550-4cfd-780e-a5f6-98980fc63fed · targets: web

     ✓ playwright engine for target "web" prepared 8.85s
     ✓ |web| tests/example.e2e.ts (1 test) 135ms
       ✓ app opens 135ms

     Test Files  1 passed (1)
          Tests  1 passed (1)
       Start at  08:36:59
       Duration  523ms
         Report  .e2e/report.json
    ```

    No model was called. The runner only checks the model when a test asks for
    the `agent` fixture.
  </Step>
</Steps>

Every run writes `.e2e/report.json`. The exit code says what went wrong: 1 is
a test failure, 2 is configuration, 3 is infrastructure.

<Tip>
  Always call the CLI as `npx --no-install e2e`. Bare `npx e2e` fetches whatever
  package is called `e2e` on npm when the runner is not installed locally.
</Tip>

## Add an agent step

The scaffold's example file ends with a commented-out agent test. Give it a
goal your app can satisfy, or write a new file next to it:

```ts title="tests/agent.e2e.ts" theme={"theme":"catppuccin-mocha"}
import { test } from '@e2edev/playwright';
import { expect } from '@e2edev/e2e';

test('a visitor signs up for a trial', async ({ app, agent, screen }) => {
  await app.open('/');

  await agent.act('sign up for a free trial as {name} with email {email}', {
    params: { name: 'Ada Lovelace', email: 'ada@example.test' },
  });

  await agent.assert('the welcome screen greets Ada by name');
  await expect(screen.getByRole('status')).toContainText('trial');
});
```

One goal per `act`. Real values travel through `params`, not through the
instruction. The final `expect` costs no model calls and cannot vary. There
are no sleeps anywhere: queries poll, actions wait, assertions retry.

This run needs a key. The scaffold's model is `gateway('openai/gpt-5.6-luna')`
from the Vercel AI Gateway, which reads `AI_GATEWAY_API_KEY`:

```bash theme={"theme":"catppuccin-mocha"}
AI_GATEWAY_API_KEY=... npx --no-install e2e run tests/agent.e2e.ts
```

Any AI SDK model instance works in that slot. [Choosing a model](/models)
covers OpenRouter, local servers, and calling a provider directly.

Useful flags while you iterate:

```bash theme={"theme":"catppuccin-mocha"}
npx --no-install e2e run tests/agent.e2e.ts   # one file, or just agent.e2e.ts
npx --no-install e2e run --headed             # watch the browser
npx --no-install e2e run --no-cache           # skip replay, run the agent live
npx --no-install e2e list                     # what would run, without running it
```

## Notes

Config and test files are ES modules. e2e loads `.ts` files as ESM whatever
the nearest `package.json` says, so a CommonJS project keeps its module type.
A `tsconfig.json` is optional; add one for editor completions.

For a mobile app, choose **Mobile (iOS/Android)** in `init` and follow
[Testing iOS and Android](/mobile).

## Next

<CardGroup cols={2}>
  <Card title="Writing tests" href="/writing-tests">
    Goals, checks, extraction, and the deterministic APIs.
  </Card>

  <Card title="Starting your app" href="/starting-your-app">
    Let the runner start the dev server and its dependencies.
  </Card>

  <Card title="Signing in" href="/authentication">
    Sign in once with a secret the model never sees.
  </Card>

  <Card title="Continuous integration" href="/ci">
    The one workflow you need.
  </Card>
</CardGroup>
