How to generate Playwright tests from a user story (with AI)
If you've ever stared at a Jira ticket like "User can reset password via email link, link expires after 1 hour" and thought "great, now I write 12 test cases and 600 lines of Playwright code" — this post is for you.
The shortcut: an AI test generator that reads the requirement, decides what to cover, drafts the steps, and emits a working Playwright suite. Below is the exact flow.
The requirement we'll use
User should be able to reset their password via the email link, and the link should expire after 1 hour.
That's it. One sentence. We're going to turn it into a test plan, then into runnable scripts.
Step 1 — Frame the requirement so the AI can reason about it
Most "AI test generation" tools fail because they treat the requirement as a black box. The first improvement: explicitly call out the acceptance criteria, edge cases, and out-of-scope items. Even a one-line requirement deserves three columns of structure.
For the password reset story above:
- Acceptance criteria: link gets sent, link works once, link expires after 1 hour
- Edge cases: invalid email, expired link clicked twice, tampered token, unverified account
- Out of scope: actual email delivery (Resend's job, not the app's)
The clearer the input, the better the AI's output. This is true for humans too.
Step 2 — Generate the test plan
This is where AI earns its keep. Given the framed requirement, the model produces a plan — case ID, title, preconditions, steps, expected results — typically 8 to 15 cases for a story of this size.
Sample output (truncated):
| ID | Title | Priority |
|---|---|---|
| TC-01 | Reset link is sent to a registered email | High |
| TC-02 | Link expires after exactly 1 hour | High |
| TC-03 | Invalid token returns a clear error | High |
| TC-04 | Already-used link cannot be reused | High |
| TC-05 | Unregistered email shows no enumeration hint | Medium |
| ... | ... | ... |
Notice TC-05 — no enumeration hint. The AI inferred this from the security framing even though we didn't ask. That's the difference between AI generation and template generation.
Step 3 — Generate Playwright code from the plan
Each test case gets converted into a Playwright script. The pattern:
import { test, expect } from '@playwright/test';
test('TC-02: Link expires after exactly 1 hour', async ({ page, request }) => {
// Setup — request a reset link, then time-travel the DB
await request.post('/api/auth/forgot-password', {
data: { email: 'test+expire@example.com' },
});
await timeTravel('+61 minutes');
// Exercise — click the expired link
await page.goto(getResetLinkFromMailbox('test+expire@example.com'));
// Assert — the page communicates expiry clearly
await expect(page.getByText(/link.*expired/i)).toBeVisible();
await expect(page.getByRole('link', { name: /request a new/i })).toBeVisible();
});
The hard part isn't writing this — it's getting the locators right. Generated locators based on guesses about your DOM are useless. SpacePilot QA solves this by crawling the live app before generating, so every selector is grounded in your real markup.
Step 4 — Run, observe, refine
Generation is never one-shot. The first run will surface 2-3 cases that need adjustment — maybe TC-05 needs different copy, or your reset page renders inside a modal you didn't tell the AI about. The right loop is:
- Run the suite
- Look at the failures
- Fix the test (or fix the requirement, often more revealing)
- Re-run
After 2-3 iterations you have a passing suite. Total time start-to-finish: 30-60 minutes for a story that would normally take a half-day of test writing.
What this gets you
Three concrete wins:
- Coverage breadth: AI finds edge cases humans skip when tired
- Consistency: every test follows the same shape (arrange/act/assert)
- Speed: the boring part — typing — is gone
What it doesn't get you: exploratory testing, performance testing, or "I have a hunch this will break under concurrent load" intuition. Those still need humans.
The deeper point
AI test generation isn't about replacing QA engineers. It's about moving them up the stack — from typing locators to designing test strategy, from writing assertions to deciding what's worth asserting in the first place.
If you're skeptical, run one ticket through it. Pick a real story you'd otherwise write yourself. Compare the output to what you'd produce manually. The gap closes fast.