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 with this body:
{
"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"
}
]
}- 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.
- 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.
- 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.
The use case this job follows, with its automation and trigger: Write the missing tests.
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.