Playwright Generator Agent Guide for Maintainable Test Code
Turn reviewed Markdown plans into maintainable Playwright tests with the Generator agent, live verification, fixture reuse, and disciplined code review.

The Playwright Generator agent converts a reviewed Markdown plan from specs/ into executable Playwright Test files under tests/. It performs the scenarios against the live application while verifying selectors and assertions, then writes ordinary test code for your repository. Give Generator one approved plan, the seed and fixture conventions it must preserve, and a bounded output path. Review and run every generated test: live verification confirms an observed path worked during generation, but it does not prove the requirement is correct, the test is isolated, all projects pass, or the code is safe to merge.
Use Generator within the complete Playwright test-agents workflow. Install current definitions through the init-agents guide, create the input artifact with the Planner guide, and investigate later failures through the Healer guide. The canonical Playwright best-practices guide covers maintainability beyond agent generation. The QA skills catalog and Playwright CLI skill provide reusable repository context.
Playwright's official Generator documentation defines the narrow contract: a Markdown plan from specs/ is the input; Generator performs scenarios and checks selectors and assertions live; the output is a suite under tests/; generated tests can contain initial errors. The code-quality gates in this guide are team recommendations layered around that contract, not claims that Generator performs an independent design review.
Establish a generation contract
Do not begin with "generate tests for checkout." By generation time, the design work should already exist in an approved plan. The request should identify the exact plan, seed, fixture import, output area, and stopping condition. That prevents Generator from silently changing scope while it translates intent into code.
A generation contract has five artifacts or decisions:
- Approved plan: a reviewed file such as
specs/guest-checkout.md. - Seed example: the runnable test that demonstrates initialization and project style.
- Fixture boundary: the import that owns data, authentication, and cleanup.
- Output mapping: one test file per scenario where feasible, or a documented alternative.
- Review gate: generated files stop for inspection before broad execution or merge.
The official artifact convention prefers generated tests aligned one-to-one with specs wherever feasible. "Where feasible" allows a team to split a long scenario group or share fixtures. It does not justify hiding five independent cases in one serial test merely because that was easier to write.
Reject an unready plan before code exists
Generator can implement ambiguity very efficiently. Run a readiness check before invoking it.
| Plan property | Ready for Generator | Send back to planning |
|---|---|---|
| Scope | One bounded feature journey with explicit exclusions | "Cover the whole application" |
| Seed | Named, runnable, and reaches the required state | Missing, flaky, or lands on a different role |
| Data | Concrete synthetic values and cleanup owner | "Use any account" or production records |
| Steps | Ordered user actions with a clear stop | Implementation guesses or omitted transitions |
| Results | Observable outcomes for each meaningful branch | "Works correctly" or "should succeed" |
| Assumptions | Marked and approved or resolved | Hidden conflicts between PRD and observed UI |
| Structure | Scenario headings can map to test titles | Duplicate permutations without distinct risk |
| Safety | Mutation budget and forbidden areas are stated | Unbounded payment, publishing, or deletion |
If an expected result cannot be asserted without inventing a backend fixture or privileged endpoint, revise the plan. Generator should report an implementation gap, not fabricate infrastructure. A plan can intentionally require only a user-visible outcome when that is the contract.
Give Generator a precise host prompt
The following is natural language for the agent host. It is not a npx playwright subcommand:
Use the Playwright Generator agent to implement only the approved scenarios in
specs/guest-checkout.md. Use tests/seed.spec.ts as the initialization and style
example. Import test and expect from the fixture module demonstrated by that seed,
preserving its cart cleanup.
Write one file per scenario under tests/checkout/ where feasible. Keep the plan
and seed source comments in each file. Verify locators and assertions against the
QA Sandbox while performing the scenarios. Do not introduce new product flows,
real payment data, fixed sleeps, or production URLs. If a planned outcome cannot
be verified, report the gap instead of weakening the assertion. Stop after writing
the files and running the focused generated tests so the diff can be reviewed.
The prompt gives Generator freedom to discover current selectors while preserving intent and repository boundaries. It does not tell the agent that a passing local run authorizes a merge. The phrase "where feasible" mirrors the documented artifact convention rather than forcing an unnatural file split.
Avoid asking Generator to "fix whatever fails" in the same request. Generation and healing are different review stages. First inspect whether the code accurately represents the plan. If a specific generated test still fails after that review, provide its name to Healer or repair it manually with evidence.
Preserve source traceability in each test
The official generated example begins with comments naming its source spec and seed. Keep that relationship. It lets a reviewer compare code with intent and later decide whether a failure comes from the product, plan, or implementation.
Here is a small approved input:
## Scenario: Correct an unsupported postal code
**Seed:** tests/seed.spec.ts
1. Enter a valid address with postal code 00000 and continue.
2. Confirm the address step remains visible and the postal-code error appears.
3. Replace the code with 10001 and continue.
4. Confirm the delivery step appears and the error is gone.
A maintainable translation can look like this:
// spec: specs/guest-checkout.md
// seed: tests/seed.spec.ts
import { test, expect } from '../../fixtures';
test('guest corrects an unsupported postal code', async ({
page,
guestCheckout,
}) => {
await guestCheckout.openSeededCart();
const postalCode = page.getByRole('textbox', { name: 'Postal code' });
await postalCode.fill('00000');
await page.getByRole('button', { name: 'Continue to delivery' }).click();
await expect(page.getByRole('heading', { name: 'Delivery address' })).toBeVisible();
await expect(page.getByText('We do not deliver to this postal code')).toBeVisible();
await postalCode.fill('10001');
await page.getByRole('button', { name: 'Continue to delivery' }).click();
await expect(page.getByRole('heading', { name: 'Delivery options' })).toBeVisible();
await expect(page.getByText('We do not deliver to this postal code')).toBeHidden();
});
guestCheckout is an illustrative project fixture. The test imports the repository's fixture layer, uses user-facing locators, asserts the refusal before recovery, and checks that the final state differs from the original one. Replace exact copy with the real approved contract; do not copy this fictional application text into another product.
The generated code should not reproduce every Markdown sentence as comments. Preserve source pointers and comments that clarify business intent, but let expressive test titles, fixture names, locators, and assertions carry most of the meaning.
Evaluate live verification correctly
The official page says Generator verifies selectors and assertions live as it performs scenarios. That is useful evidence: the role is not merely guessing a selector from source text. It can interact with the rendered UI and confirm that its chosen assertions match the observed scenario.
Live verification still has a limited scope. It establishes that:
- The tested environment was reachable during generation.
- The selected locator resolved in that observed state.
- The performed action sequence reached the asserted outcome at least in that run.
- The generated syntax was executable enough for the role's attempt.
It does not establish that:
- The plan reflects the correct product requirement.
- The locator remains appropriate across locale, browser, viewport, or role variants.
- Data setup and cleanup are safe under parallel workers.
- Every configured Playwright project passes.
- The assertion is strong enough to reject a nearby incorrect outcome.
- A passing test is independent of state left by the generation session.
Call the result "live-verified during generation," not "self-validating." Normal review and test execution remain necessary.
Preserve the repository fixture model
Because Planner uses the seed as an example, Generator should already see the expected import and setup style. Reinforce it in the prompt. A generated test that bypasses shared fixtures can create duplicate login code, leaked records, and inconsistent cleanup.
Check that generated code:
- Imports
testandexpectfrom the repository's intended module. - Uses approved fixtures for authentication and data creation.
- Does not hard-code a base URL already owned by configuration.
- Does not read secrets into logs or source comments.
- Creates unique mutable records when workers can overlap.
- Leaves cleanup with a fixture or an explicit, reliable teardown path.
- Avoids dependence on test execution order.
Do not reject every helper extraction. A small flow fixture or page object can be appropriate when the repository already uses that pattern. Reject abstractions that hide assertions, combine unrelated journeys, or make a reader open five files to understand one scenario. The page-object model guide explains the broader trade-off.
Review locators as product contracts
The official Generator example uses role-based locators, and Generator verifies its choices live. During review, ask why each locator identifies the intended user-facing element.
Prefer a locator that expresses semantics and uniqueness. A button's role and accessible name often explain intent better than a generated CSS chain. A dedicated test ID may be appropriate when a stable semantic handle does not exist. Raw DOM position, transient class names, and copied element IDs usually bind the test to implementation details without improving behavior coverage.
A live-resolving locator can still be wrong. Two "Continue" buttons may exist in different regions. A broad text match can resolve to hidden or unrelated content. Scope locators to meaningful regions or records when the page contains repeated controls. Then assert the state transition caused by the action, not only that clicking did not throw.
Do not "improve reliability" with forced clicks or arbitrary sleeps unless the product contract genuinely requires a timing interval and the evidence supports it. A generated wait can hide a missing readiness assertion. Prefer a web-facing condition that proves the next state is available.
Review assertions for false-green behavior
Generator can verify an assertion live and still choose a weak assertion. Compare every expected result in the plan with at least one code assertion. Then test whether the assertion would fail for a plausible incorrect product state.
Weak examples include:
- Checking that the page URL contains
/checkoutwhen every checkout step shares that path. - Looking for generic text such as "Success" that already exists in a banner.
- Asserting only that an error is visible without checking which field or outcome failed.
- Confirming a row exists without restricting it to the synthetic record.
- Treating the absence of an exception as proof of a backend side effect.
Stronger assertions distinguish branches. A rejected postal code keeps the user on the address step, identifies the field, and prevents delivery options from appearing. A successful correction removes the refusal and presents the delivery state. Assert enough to separate these outcomes without encoding incidental layout.
Review negative space carefully. not.toBeVisible() and toBeHidden() are meaningful only when the locator identifies the expected element. A typo that matches nothing can make a negative assertion pass. Pair important absence checks with a preceding positive observation or a stable locator contract.
Keep scenarios isolated and debuggable
One plan scenario should usually become one independently runnable test. Isolation improves parallel execution, retries, failure ownership, and Healer input. A mega-test that creates a cart, checks validation, completes checkout, edits an order, and cancels it can fail late and obscure which approved behavior regressed.
Use test.describe to organize related scenarios, not to create hidden order dependencies. Shared setup belongs in fixtures or hooks when it is safe and clear. Each test should be able to run alone from its seed contract.
Name tests with actor, action, and outcome. "Checkout test 2" gives Healer and reviewers little context. "Guest corrects an unsupported postal code" maps directly to the plan and creates a precise failing test name, which is the documented input Healer expects.
If a scenario has several business phases that deserve separate trace steps, keep those phases visible in code. Do not add abstraction solely to reduce line count. Maintenance cost is driven by hidden coupling and unclear intent more than by a few repeated readable actions.
Run focused verification from a clean state
After reviewing the diff, execute ordinary Playwright Test commands rather than relying on the agent's generation session:
npx playwright test --list
npx playwright test tests/checkout/unsupported-postal-code.spec.ts
npx playwright test tests/checkout/unsupported-postal-code.spec.ts --project=chromium
The first command checks discovery. The second runs the generated file through configured projects; the third is a focused project diagnosis, not a substitute for the full configured set. Use the repository's package scripts if they wrap environment setup, reporters, or service startup.
Start from a clean data state. If the test passes only after Generator has manually navigated the app, the code is not self-contained. Restart the application or recreate the fixture state as the team normally does, then rerun. Execute relevant neighboring tests to detect fixture interference before broad CI.
Compilation, linting, formatting, and type checks remain repository responsibilities. Generator's live browser verification does not guarantee the output satisfies local static rules.
Failure paths in generated code
| Generated symptom | Likely defect | Review action |
|---|---|---|
| Locator matches multiple controls | Semantic scope is incomplete | Restrict to the intended region or record |
| Test passes when the feature is wrong | Assertion does not distinguish the required outcome | Map assertions back to every expected result |
| Test passes only after generation | Hidden state from the live session | Rebuild setup through fixtures and clean execution |
File imports @playwright/test directly | Repository fixture contract was ignored | Restore the approved fixture import unless intentionally changed |
| Several scenarios depend on order | Generated structure coupled state | Split tests and provision independent data |
| Fixed timeout was added | Readiness or product delay was not modeled | Replace with an observable condition supported by evidence |
| Plan step was omitted | Generator could not perform it or silently narrowed scope | Report the gap and revise plan or code explicitly |
| Exact text is brittle across locales | Assertion exceeds the product contract | Assert approved semantics or run the intended locale |
| Generated test is skipped | Failure signal was suppressed | Require an owner, reason, and explicit review; do not count as passing |
The test-agent docs acknowledge that generated tests may include initial errors that Healer can address. That statement does not mean every initial error should be handed off automatically. First determine whether the plan, seed, environment, or generated code is wrong. Healing a test built from an unapproved assumption only makes the wrong intent more durable.
Use Healer only after code review
Healer's documented input is a failing test name. A well-structured Generator output makes that input specific. Before invoking Healer, preserve the failure, confirm the named test maps to an approved scenario, and decide whether the product may actually be broken.
Do not ask Healer to sweep every generated file until CI turns green. The official workflow says it replays steps, inspects the current UI, suggests a patch, and reruns until pass or guardrails stop. Its possible output includes a skipped test. Every patch or skip needs the same code review as a manual change.
If Generator reports that an outcome cannot be verified live, return to the plan or environment instead of weakening assertions. Healer is not a substitute for missing requirements or inaccessible data.
Pull-request acceptance checklist
A generated suite is ready for a normal pull request only when reviewers can answer yes to the relevant checks:
- Source plan and seed comments point to real reviewed files.
- Every test title maps to one approved scenario.
- Fixture imports, authentication, data creation, and cleanup follow project policy.
- Locators identify the intended user-facing controls without fragile DOM chains.
- Assertions distinguish the required state from plausible incorrect states.
- Tests run independently and do not require Generator's residual browser state.
- No production URL, real customer data, token, or broad privileged account was added.
- No unexplained fixed sleep, forced action, retry increase, or skip masks uncertainty.
- Focused tests pass from a clean state under relevant projects.
- Static checks and neighboring tests pass through the repository's standard commands.
This is a human review standard. Playwright does not claim Generator certifies these properties automatically.
Version scope and limitations
Generator was introduced with Playwright Test Agents in version 1.56. This article reflects the current 1.61 documentation available July 14, 2026. Regenerate definitions on upgrades, as the official setup instructs, because tools and role instructions can change.
The official page documents a Node.js workflow with plans under specs/ and tests under tests/. It does not promise identical generation support for every language binding or coding-agent host. It also does not claim that generated code is exhaustive, deterministic across model runs, compliant with every repository architecture, or automatically approved.
Live verification is bounded by the reachable app, current data, selected browser context, and supplied plan. Cross-browser behavior, parallel safety, localization, responsive states, and external integrations require explicit scope and normal execution. Generator is a code-production role, not an oracle for product intent.
Frequently Asked Questions
What does the Playwright Generator agent take as input?
Its documented input is a Markdown plan from specs/. Name the exact approved file in the prompt and preserve the seed and fixture conventions it references.
Where does Generator write tests?
The official artifact model places generated Playwright tests under tests/, aligned one-to-one with specs wherever feasible. Choose a clear domain subdirectory when the repository uses one.
Does Generator verify selectors?
Yes. Playwright says it verifies selectors and assertions live while performing scenarios. Review the resulting locator semantics and rerun from clean state because a single observed resolution is not a durability guarantee.
Are generated tests guaranteed to pass?
No. The official page explicitly notes that generated tests may include initial errors. Diagnose the plan, seed, code, data, and product before deciding whether Healer is appropriate.
Should Generator create page objects?
Only when that matches the repository's architecture and improves clarity. The official agent contract does not require a page-object model. Avoid speculative abstractions that hide scenario intent.
Can I skip human review if live verification passed?
No. Live verification does not validate requirements, isolation, project coverage, security, or maintainability. Treat generated code like any other code contribution.
Should I ask Generator to add extra scenarios it discovers?
Prefer sending new behavior back to Planner so intent is reviewed in Markdown first. If scope changes during generation, update the plan and preserve traceability instead of silently adding code.
When should a generated test go to Healer?
After a reviewer confirms the plan and generated structure are legitimate, the environment is available, and one specifically named test still fails. Give Healer that test name and preserve evidence.
What if the test passes only in the generation browser?
Assume hidden state or incomplete setup until disproved. Close or reset the session, rebuild data through approved fixtures, and run the file with the normal Playwright Test command.
Keep the artifact chain reviewable
Return to the Planner guide when intent is ambiguous, use the Healer guide for a reviewed named failure, and maintain definitions through the init-agents tutorial. The test-agent pillar joins those ownership decisions into one lifecycle.
The primary behavior is documented on Playwright's official Generator section and artifact conventions. For broader code principles, read the canonical Playwright best-practices guide and page-object model guide. Browse QA skills or the Playwright CLI skill when the repository needs reusable agent instructions beyond Generator's role definition.