# Write Playwright Tests for a Next.js App

Yes. An agent writes Playwright specs for sign-up, sign-in and the pricing checkout redirect, runs them headless against next dev in its sandbox and opens a PR.

[Run this on Rush](https://cloud.getrush.ai/run/write-playwright-tests/nextjs/), in a Project that has your repository.

## What the agent does

The agent reads how the app is built before it writes a spec. In nextjs/saas-starter, `package.json` pins `next` 15.6.0-canary.59, React 19.1, Drizzle ORM with `postgres`, `stripe` ^18.1, `jose` for the session JWT and `bcryptjs`; there is a `pnpm-lock.yaml` and no test script, no Playwright and no `e2e/` directory. `next.config.ts` turns on `experimental.ppr` and `clientSegmentCache`, and `middleware.ts` redirects any `/dashboard` path without a `session` cookie to `/sign-in` and refreshes the cookie on every GET. The app needs `POSTGRES_URL`, `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET`, `BASE_URL` and `AUTH_SECRET` from `.env.example`, so the agent starts Postgres in its sandbox, writes a `.env`, runs `pnpm db:migrate`, and seeds with `pnpm db:seed`, which creates `test@test.com` with password `admin123`, a `Test Team`, and the Stripe `Base` and `Plus` products the pricing page looks up by name.

The flows the specs cover are server actions, which is why browser tests earn their place here. `app/(login)/login.tsx` renders one form for both modes with `Email` and `Password` labels, hidden `redirect`, `priceId` and `inviteId` inputs, and a submit button that reads `Sign in` or `Sign up`. `app/(login)/actions.ts` validates with Zod, and `signIn` returns the text `Invalid email or password. Please try again.` on a wrong password or redirects to `/dashboard`; `signUp` returns `Failed to create user. Please try again.` for a duplicate email and creates a team named `<email>'s Team` for a fresh one. `app/(dashboard)/pricing/page.tsx` renders two cards whose form posts `checkoutAction` with a hidden `priceId`, and `createCheckoutSession` in `lib/payments/stripe.ts` sends a signed-out visitor to `/sign-up?redirect=checkout&priceId=<id>` and a signed-in one to a Stripe Checkout URL.

The specs drive those forms as a user would. The sign-up spec opens `/sign-up`, fills `Email` with a unique address per run and `Password` with eight or more characters, clicks `Sign up`, and expects the URL to become `/dashboard`; a second case submits the same email again and expects the duplicate-user message. The sign-in spec uses the seeded `test@test.com`, checks the redirect to `/dashboard` and that `/dashboard` without a cookie lands on `/sign-in`, and checks the wrong-password text. The checkout spec clicks `Get Started` on the `Base` card while signed out and expects `/sign-up` with `redirect=checkout` and a `priceId` in the query; signed in, it expects navigation to a `checkout.stripe.com` URL and stops there, since it never completes a payment.

It puts the specs in `e2e/` with a `playwright.config.ts` whose `webServer` runs `pnpm dev` on port 3000 and whose `baseURL` matches `BASE_URL`, installs `@playwright/test` and Chromium with `pnpm exec playwright install chromium`, and runs the suite headless several times to check it is stable. A case that exposes the app rather than the spec stays in the suite as `test.fail()` with the reason in the description; the sign-up action accepting an email that the sign-in schema would reject, because `signUpSchema` has no `min(3).max(255)` on `email`, is the kind of difference it checks and reports.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Write Playwright end-to-end tests for sign-up, sign-in and the pricing checkout redirect in this Next.js app. Start Postgres in the sandbox, create .env from .env.example, run pnpm db:migrate and pnpm db:seed, and use the seeded test@test.com account for sign-in. Put the specs in e2e/ with a playwright.config.ts whose webServer starts pnpm dev on port 3000, drive the forms by their Email and Password labels and the Sign up, Sign in and Get Started buttons, assert the redirects to /dashboard, /sign-in and /sign-up?redirect=checkout, and stop at the Stripe Checkout URL without paying. Run the suite headless until it is stable and open a pull request with the passing suite. If a test exposes a real bug, mark it expected to fail and explain it in the description.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

A pull request on its own branch that adds `e2e/sign-up.spec.ts`, `e2e/sign-in.spec.ts` and `e2e/checkout.spec.ts`, a `playwright.config.ts`, `@playwright/test` in `devDependencies` and a `test:e2e` script in `package.json`, and a line in `.gitignore` for `playwright-report/` and `test-results/`. The description says which labels and buttons each spec uses, how the database and Stripe test products were seeded, and pastes the headless run's output.

Any spec marked `test.fail()` comes with the behaviour it found, the file in `app/(login)/actions.ts` or `lib/payments/stripe.ts` responsible, and what the agent thinks the fix is, so the pull request is a report as well as a suite. The session transcript keeps every `pnpm` and `playwright` command with its output, and `GET /o/{org}/sessions/{id}/messages` returns it.

A reviewer checks that the specs locate elements by label and role rather than by Tailwind class names, that the checkout spec stops at Stripe and never submits card details, and that the unique sign-up email per run cannot collide when the suite runs in parallel in CI.

## Run it on every Next.js canary upgrade

This app tracks a Next.js canary, so when Dependabot or Renovate opens a pull request that bumps `next` or `react`, a GitHub webhook on your server can send a request with that pull request's URL in `resources` asking the agent to run the e2e suite on the upgrade branch and fix any spec it broke. Server actions, `middleware.ts` and partial prerendering are what canaries change, and these three flows cover all of them.

Set a webhook URL on the API key and Rush POSTs `run.completed` or `run.failed` when the turn finishes, so your server can comment the suite's result on the upgrade pull request before anyone merges it.

## Why run it on Rush?

Writing browser tests for this app means a Postgres database, Stripe test products created through the API, a seeded user and a running dev server before the first assertion, which is why the template ships without them. The agent sets all of that up in its own sandbox with its own Chromium, and your laptop and your Stripe dashboard stay as they were.

The pull request comes with the suite's own output and the transcript of every command, so you can see the specs pass before you read them. Several apps built on the same starter can each get a suite in parallel, and a person decides what merges.

## Frequently asked questions

### Does it need my real Stripe account?

It needs a Stripe test-mode secret key, because `pnpm db:seed` creates the `Base` and `Plus` products the pricing page reads and `createCheckoutSession` calls Stripe to build the Checkout URL. Save it as a Project secret named `STRIPE_SECRET_KEY` and start the session in that Project; the agent reads it as an environment variable. The checkout spec stops at the Stripe URL and never pays.

### Why run against next dev and not next build?

`pnpm dev` is what the README documents and what a developer runs, and the config's `experimental.ppr` behaves the same for these flows in both. The agent can switch the `webServer` command to `pnpm build && pnpm start` if you ask for it in the prompt.

### Does it use pnpm?

Yes. The repository has a `pnpm-lock.yaml` and the README's commands are `pnpm install`, `pnpm db:migrate` and `pnpm db:seed`, so the agent installs with pnpm and adds the Playwright dependency with it.

### Will it write unit tests for the server actions too?

Not in this run. The prompt asks for Playwright specs for three flows; the server actions are covered through the browser. A follow-up message to the same session can ask for Vitest tests of `signIn` and `signUp` on the same branch.
