QA How-To
Generating Playwright tests from a user story (2026)
Learn generating Playwright tests from a user story with risk analysis, test matrices, accessible locators, API setup, runnable TypeScript, and review gates.
24 min read | 3,299 words
TL;DR
Generating Playwright tests from a user story works best as a test-design workflow: clarify behavior, map criteria to examples and risks, select the right test layer, then implement a small independent browser suite with semantic locators and observable assertions. Compile and run every generated draft, and reject unsupported assumptions even when the code looks polished.
Key Takeaways
- Turn acceptance criteria into observable examples before writing browser code.
- Add risk, boundary, state, accessibility, and failure-path analysis because stories rarely specify complete coverage.
- Place business rules and data setup at lower layers, then keep browser tests focused on user-visible behavior.
- Use Playwright role, label, and text locators with web-first assertions instead of sleeps and fragile CSS chains.
- Make test data unique and independent so tests can run in any order and in parallel.
- Treat AI-generated code as an untrusted draft that must compile, execute, trace to intent, and pass human review.
- Review test value, oracle quality, and maintenance cost, not merely acceptance-criterion line coverage.
Generating Playwright tests from a user story is not a direct translation from each sentence into one test() block. First convert the story into observable rules, examples, risks, states, and open questions. Then choose which checks belong in Playwright, arrange deterministic data, and implement only the browser journeys that provide valuable user-level evidence.
This guide follows one subscription story from analysis to a runnable TypeScript suite. It also shows how to use an AI assistant responsibly without accepting invented selectors or business rules. By the end, you will have a traceable test matrix, a complete local demo, maintainable Playwright tests, and a review checklist that works for real pull requests.
TL;DR
| Story artifact | QA transformation | Automation output |
|---|---|---|
| User role and goal | Actor, value, entry point | One critical user journey |
| Acceptance criterion | Concrete examples and oracle | Focused assertion at the right layer |
| Business rule | Partitions and boundaries | Mostly unit or API coverage |
| State dependency | Setup, cleanup, ownership | API fixture or isolated account |
| Ambiguity | Question and explicit assumption | No guessed behavior in code |
| Risk | Impact and likelihood | Priority plus negative scenario |
| Accessibility need | Semantic interaction and announcements | Role, label, focus, and live-message checks |
The browser suite should be the smallest set that proves the customer-facing contract. More generated tests do not automatically mean more confidence.
1. Start generating Playwright tests from a user story with examples
Use a concrete story throughout the design.
As a visitor, I want to subscribe with my email address so that I receive product updates.
Acceptance criteria:
- A valid new email shows
Subscription confirmed.- An empty email shows
Email is required.- An invalid email shows
Enter a valid email.- An already subscribed email shows
This email is already subscribed.
Extract the actor, goal, value, entry condition, action, result, and observable language. The actor is an unauthenticated visitor. The action is form submission. The primary oracle is user-visible status. The story does not define exact email syntax, case normalization, rate limits, localization, persistence duration, or what happens when the service is unavailable. Those are questions, not permission to invent rules.
Turn each criterion into Given, When, Then examples even if the team does not use a BDD framework. Given a visitor is on the subscription page, when they submit an empty field, then the page exposes Email is required and does not create a subscription. This exposes the need for both a visible oracle and a side-effect check.
Ask product and engineering about decision-changing gaps. Which email validator owns the rule? Are plus aliases allowed? Is a duplicate comparison case-insensitive? Does success require confirmed persistence or only accepted processing? Should errors receive focus or an accessible live announcement? Capture answers in the story or test contract.
Do not wait for every optional detail. Automate confirmed behavior and keep unresolved questions visible. Playwright test design techniques can help turn examples into lean coverage without binding one test to every sentence.
2. Convert Acceptance Criteria Into a Risk-Based Test Matrix
Create a matrix before writing code. It makes coverage and omissions reviewable by product, developers, and QA. Each row should describe behavior, layer, setup, oracle, and priority.
| ID | Scenario | Layer | Setup | Primary oracle | Priority |
|---|---|---|---|---|---|
| SUB-01 | New valid email | Browser | Unique email | Confirmation is announced | Critical |
| SUB-02 | Empty email | Browser or component | None | Required message, no request | High |
| SUB-03 | Invalid format | Unit plus one browser example | Invalid partition | Validation message | High |
| SUB-04 | Existing email | API setup plus browser | Pre-created subscription | Duplicate message | High |
| SUB-05 | Service failure | API or component | Controlled failure | Recoverable error, no false success | High |
| SUB-06 | Keyboard submission | Browser accessibility | None | Operable flow and announced result | Medium |
| SUB-07 | Email format boundaries | Unit | Partition data table | Rule implementation | Medium |
Acceptance criteria provide examples, not exhaustive test analysis. Add risks from interfaces, customer consequence, and implementation. Double-click could create duplicate requests. A network retry could create two subscriptions. A stale success message might remain after a failed second attempt. An error could be visual but unavailable to screen readers.
Select priorities from impact, likelihood, change scope, and existing lower-layer coverage. Do not automate every theoretical email address in a browser. The browser test should prove that client validation and user feedback are wired correctly. The validator itself needs fast table-driven unit tests.
Keep traceability at scenario level. SUB-03 -> AC3 is useful only if the expected assertion truly represents the accepted email rule. A test named invalid email that merely checks the button is visible does not cover AC3.
3. Choose the Correct Playwright Test Layer
Playwright Test supports browser testing and direct HTTP requests, but that does not mean all story rules belong in end-to-end UI checks. Place each risk at the lowest layer that produces trustworthy evidence. A focused browser set validates integration among markup, client behavior, network, and visible outcome.
Use unit tests for pure email parsing and boundary tables. Use component tests if your stack and team already support them for form states. Use API tests for status codes, normalization, idempotency, persistence, and service errors. Use browser tests for the primary journey, a representative validation failure, a duplicate-state journey, keyboard operation, and user-visible recovery.
Layering improves diagnosis. If twenty malformed addresses fail only through the browser, a UI suite spends time rediscovering the same validator defect. If the browser never checks an invalid input, wiring or accessible messaging could break while unit tests remain green.
State the contract between layers. The API setup for a duplicate test should use a supported endpoint or fixture, not internal database manipulation unless database behavior is the subject. It should return only after the subscription is ready. The UI test then verifies what a visitor sees for that known state.
Use Playwright API testing examples to build fast workflow and setup checks. The aim is not a pyramid diagram for its own sake. It is fast feedback, low duplication, and a clear reason for every browser interaction.
4. Set Up a Runnable Playwright TypeScript Project
Create an empty directory and install the current Playwright Test package plus Chromium. These commands use supported package and CLI names. The generated lockfile records the exact installed version, so CI should use npm ci after committing it.
npm init -y
npm install --save-dev @playwright/test typescript
npx playwright install chromium
Add the following playwright.config.ts. It starts the local demonstration server, uses the documented baseURL option, captures a trace on the first retry, and runs only Chromium for this compact example.
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
retries: process.env.CI ? 2 : 0,
reporter: [['list'], ['html', { open: 'never' }]],
use: {
baseURL: 'http://127.0.0.1:4173',
trace: 'on-first-retry',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
],
webServer: {
command: 'node demo-server.mjs',
url: 'http://127.0.0.1:4173/health',
reuseExistingServer: !process.env.CI,
},
});
Add scripts such as "test:e2e": "playwright test" to package.json, or run npx playwright test directly. Do not copy retries blindly. Retries can produce useful trace evidence in CI, but a test that passes only on retry is still an instability signal that needs ownership.
A real repository may already define browsers, storage state, reporters, and a web server. Extend that configuration instead of creating a conflicting second one. Keep secrets in environment or CI secret storage, never in the story, prompt, test, trace, or committed configuration.
5. Create the Local Demo Application
The following Node.js server makes the article example self-contained. Save it as demo-server.mjs. It serves an accessible form and a small JSON endpoint. It intentionally implements only the confirmed demonstration rules and stores subscriptions in memory.
import http from 'node:http';
const subscriptions = new Set();
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const page = String.raw`<!doctype html>
<html lang="en">
<head><meta charset="utf-8"><title>Product updates</title></head>
<body>
<main>
<h1>Product updates</h1>
<form id="subscribe" novalidate>
<label for="email">Email address</label>
<input id="email" name="email" type="email">
<button type="submit">Subscribe</button>
</form>
<p id="status" role="status" aria-live="polite"></p>
</main>
<script>
const form = document.querySelector('#subscribe');
const status = document.querySelector('#status');
form.addEventListener('submit', async event => {
event.preventDefault();
const email = new FormData(form).get('email');
if (!email) { status.textContent = 'Email is required'; return; }
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
status.textContent = 'Enter a valid email'; return;
}
const response = await fetch('/api/subscriptions', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ email }),
});
const result = await response.json();
status.textContent = result.message;
});
</script>
</body>
</html>`;
function json(response, status, value) {
response.writeHead(status, { 'content-type': 'application/json' });
response.end(JSON.stringify(value));
}
const server = http.createServer((request, response) => {
if (request.method === 'GET' && request.url === '/') {
response.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
response.end(page);
return;
}
if (request.method === 'GET' && request.url === '/health') {
json(response, 200, { status: 'ok' });
return;
}
if (request.method === 'POST' && request.url === '/api/subscriptions') {
let body = '';
request.on('data', chunk => { body += chunk; });
request.on('end', () => {
try {
const email = JSON.parse(body).email;
if (typeof email !== 'string' || !emailPattern.test(email)) {
json(response, 400, { message: 'Enter a valid email' });
} else if (subscriptions.has(email.toLowerCase())) {
json(response, 409, { message: 'This email is already subscribed' });
} else {
subscriptions.add(email.toLowerCase());
json(response, 201, { message: 'Subscription confirmed' });
}
} catch {
json(response, 400, { message: 'Enter a valid email' });
}
});
return;
}
response.writeHead(404).end();
});
server.listen(4173, '127.0.0.1', () => {
console.log('Demo server listening on http://127.0.0.1:4173');
});
This is demonstration infrastructure, not a production service. It lacks persistence, throttling, confirmation mail, security headers, graceful shutdown, and robust payload limits. Those gaps should not become accidental expected behavior in the browser tests.
The example is still valuable because the setup is executable and deterministic within one run. A publication-quality automation guide should provide code a reader can verify, rather than fragments calling invented pages or methods.
6. Implement the Playwright Tests With Strong Oracles
Create tests/subscription.spec.ts with the following code. It uses Playwright Test's test, expect, built-in request fixture, role and label locators, web-first assertions, and crypto.randomUUID() from Node.
import { randomUUID } from 'node:crypto';
import { expect, test } from '@playwright/test';
const uniqueEmail = () => `qa-${randomUUID()}@example.test`;
test.describe('newsletter subscription story', () => {
test('SUB-01 confirms a new valid subscription', async ({ page }) => {
const email = uniqueEmail();
await page.goto('/');
await page.getByLabel('Email address').fill(email);
await page.getByRole('button', { name: 'Subscribe' }).click();
await expect(page.getByRole('status')).toHaveText('Subscription confirmed');
});
test('SUB-02 explains that email is required', async ({ page }) => {
await page.goto('/');
await page.getByRole('button', { name: 'Subscribe' }).click();
await expect(page.getByRole('status')).toHaveText('Email is required');
});
test('SUB-03 rejects a representative invalid email', async ({ page }) => {
await page.goto('/');
await page.getByLabel('Email address').fill('not-an-email');
await page.getByRole('button', { name: 'Subscribe' }).click();
await expect(page.getByRole('status')).toHaveText('Enter a valid email');
});
test('SUB-04 reports an existing subscription', async ({ page, request }) => {
const email = uniqueEmail();
const setupResponse = await request.post('/api/subscriptions', { data: { email } });
expect(setupResponse.status()).toBe(201);
await page.goto('/');
await page.getByLabel('Email address').fill(email);
await page.getByRole('button', { name: 'Subscribe' }).click();
await expect(page.getByRole('status')).toHaveText('This email is already subscribed');
});
});
Run npx playwright test, inspect the list output, then use npx playwright show-report for the HTML report. These are supported Playwright CLI commands. If a failure has a trace, open it from the report or with npx playwright show-trace path-to-trace.zip.
Each test has one behavioral purpose, a traceability ID, isolated data, semantic locators, and an observable expected result. The duplicate scenario uses direct HTTP setup to avoid repeating the UI journey as precondition. The setup response is asserted so a failed fixture cannot produce a misleading UI failure.
7. Design Locators, Assertions, and Waits for User Intent
Prefer locators that reflect how users and accessibility tools perceive the page. getByRole, getByLabel, getByText, and getByPlaceholder are supported Playwright locators. A test ID is useful for elements without a stable user-facing identity, but it should be an intentional contract rather than a substitute for accessible markup.
Web-first assertions such as toHaveText, toBeVisible, and toHaveURL retry until the condition is met or times out. They express the outcome and avoid manual polling. Do not add waitForTimeout(2000) after every action. A fixed sleep is both slower than necessary and too short under unpredictable conditions.
Assert the story outcome at the strongest observable boundary. For a subscription, a button click alone proves no business result. A 201 response alone does not prove accessible feedback. The browser test checks the live status, while an API test can verify response and durable state. Avoid asserting unrelated implementation details such as an internal CSS class unless that styling is itself the requirement.
Use exactness thoughtfully. An exact accessible name helps avoid matching Subscribe later when the intended button is Subscribe. For messages controlled by product copy, exact text provides clear contract evidence. If localized copy is expected, assert stable semantics or locale-specific resources instead of hard-coding English everywhere.
When a response is asynchronous, wait on the resulting UI state or a specific known response as part of the action flow. Register event waits before the triggering action when using page.waitForResponse. Do not use broad networkidle waiting as a universal readiness definition for modern applications with background requests.
8. Make Generated Tests Independent and Parallel-Safe
Playwright runs test files in worker processes and can run tests in parallel according to configuration. Generated code must not assume alphabetical order, reuse one mutable account, or depend on a prior test creating state. Every test should arrange its own preconditions and produce unique mutable data.
The example uses randomUUID() so subscriptions do not collide. In a real system, combine a run ID, worker index if needed, and recognizable prefix so cleanup and diagnosis are possible. Avoid timestamps alone because parallel tests can generate the same value, and do not put secrets or personal data in test addresses.
Choose cleanup according to the environment. A dedicated disposable environment can be reset between runs. A shared QA environment may need a supported deletion API or expiring test records. Cleanup in afterEach is useful but must not hide the original failure if deletion fails. Record created resources and make cleanup idempotent.
Authentication state can be prepared once when it is truly immutable, but tests that modify the same user still conflict. Use separate accounts or isolated entities. Keep storage-state files out of source control when they contain session material.
Prove independence. Run a single test, repeat it, shuffle file execution through isolated runs, and enable parallel execution. Use npx playwright test tests/subscription.spec.ts -g "SUB-04" to target the duplicate case by title. If it fails alone, the precondition is incomplete. Playwright flaky test debugging covers artifacts and classification when generated tests expose instability.
9. Use AI to Draft Tests Without Surrendering Test Design
An AI assistant can transform a structured matrix into draft code, propose missing risks, or refactor repeated setup. Give it the story, clarified decisions, relevant DOM or component contract, API schema, existing fixtures, Playwright configuration, locator policy, and explicit output boundaries. Without repository context, it is likely to invent selectors, routes, fixtures, and helpers.
Prompt for uncertainty. Require the assistant to list assumptions and unresolved questions before code, cite each test to a matrix ID, use only supplied interfaces, and avoid dependencies not already approved. Ask it to produce a minimal patch rather than an alternate framework. Then review the output as untrusted code.
Compile and execute every draft. TypeScript catches nonexistent fixture names and invalid types, but not weak oracles. Run tests against a known-good build and deliberately break a covered rule to confirm detection. Review whether tests pass for the right reason, not because a locator found an unrelated element or an assertion was too broad.
Check security and privacy. Prompts, generated code, traces, screenshots, and reports can expose credentials or customer data. Use approved tools, sanitized examples, least-privilege test accounts, and repository secret scanning. Do not ask a model to query production merely because the generated code can make HTTP requests.
Measure assistant value through accepted tests, edit severity, coverage added, review time, instability, and detected faults. Generated line count and speed to first draft are incomplete. AI code review for Playwright tests offers a complementary checklist for generated patches.
10. Scale generating Playwright tests from a user story in CI
Require formatting or linting, TypeScript checks, Playwright execution, and report artifacts according to repository standards. Test the smallest relevant project on pull requests and a broader browser matrix at an appropriate stage. The story's supported browser policy, not tool capability alone, determines projects.
Preserve traceability in test titles, annotations, or external test management integration. IDs should connect to stable behavior, not a ticket URL that disappears. Avoid one giant test per story because a middle failure hides later criteria and makes retry evidence ambiguous. Also avoid splitting every click into its own test, which repeats setup without independent value.
Quarantine is a temporary ownership state, not permanent success. When a generated test flakes, retain the first failure evidence, classify product, test, environment, or data cause, and assign a deadline. A retry pass does not erase instability. Report clean-pass and retry-pass counts separately.
Review story changes. When acceptance criteria change, update the matrix first, identify obsolete and new examples, then adjust the lowest appropriate layers. Diffing generated code without revisiting intent can preserve a polished test for a retired rule.
For AI-assisted generation in CI, keep output in a branch or artifact and require review. Gate invented interfaces, secret patterns, compile failure, unsafe hosts, skipped tests, weak assertions, and missing required matrix IDs. Record model and prompt versions if generation results are compared over time.
11. Review the Pull Request and Maintain the Suite
A reviewer should be able to answer why each test exists, what risk it covers, how data is arranged, what user-observable oracle proves success, and how a failure will be diagnosed. If the answer is the AI generated it from AC2, review is not complete.
Inspect names and structure first. Titles should describe behavior, not implementation. Keep setup close enough to understand and abstract only stable domain actions. Page objects can reduce duplication in large suites, but a generic clickButton() layer often hides intent and makes failures harder to read.
Inspect failure behavior. Temporarily alter the demo message or duplicate response and verify the intended test fails with a useful diff. Run in parallel and repeat. Confirm traces, screenshots, and logs contain enough evidence without secrets. Review runtime and redundant navigation.
Maintain a test-to-risk map and prune tests that no longer protect distinct behavior. When UI copy, selectors, or workflows change, determine whether the product contract changed before updating assertions. Blindly making a test green can encode a regression as expected behavior.
Track operational signals: clean-pass rate, diagnosis time, test runtime, repeated root causes, escaped defects, and maintenance changes. A small trustworthy suite is more valuable than a rapidly generated suite engineers ignore.
Interview Questions and Answers
These questions test both Playwright API knowledge and the test-design judgment required to automate user stories.
Q: How do you convert a user story into Playwright tests?
I extract actor, goal, value, business rules, states, inputs, outputs, and open questions. I turn acceptance criteria into concrete examples, add risk and boundary analysis, then choose the lowest useful layer for each check. Only user-facing journeys that need browser evidence become Playwright UI tests, with traceability and strong oracles.
Q: Is one test per acceptance criterion a good rule?
It is a traceability starting point, not a design rule. One criterion may need several partitions at lower layers, and several criteria may form one meaningful journey. I optimize for independent behavioral evidence, diagnosis, and maintenance rather than matching line counts.
Q: Which Playwright locators do you prefer?
I prefer role and accessible name, label, or other user-facing locators because they describe intent and encourage accessible markup. I use explicit test IDs when no stable user-facing contract exists. I avoid long CSS or XPath chains tied to layout.
Q: Why use web-first assertions instead of sleeps?
Web-first assertions retry the desired condition until it succeeds or reaches the configured timeout. A fixed sleep waits too long on fast runs and may still be too short on slow runs. Assertions also produce clearer failure evidence about the expected state.
Q: How do you set up an existing user for a UI scenario?
I use a supported API or fixture to create isolated state, assert that setup succeeded, and then open the UI for the behavior under test. This is faster and clearer than repeating unrelated UI setup. Data is unique and cleanup is safe for parallel runs.
Q: How do you review AI-generated Playwright code?
I verify every interface against the repository, map tests to confirmed behavior, compile, run, and inject a covered failure. I inspect locators, waits, assertions, data isolation, secrets, skipped tests, dependencies, and maintenance. Fluent code is treated as a draft until evidence shows it fails for the intended product defect.
Q: How do you prevent generated Playwright tests from becoming flaky?
I remove order dependence and shared mutable data, use semantic locators and web-first assertions, wait for observable conditions, and make setup deterministic. I run tests alone, repeatedly, and in parallel, retaining first-failure artifacts and classifying instability rather than masking it with retries.
Common Mistakes
- Translating each acceptance-criterion sentence directly into browser code without examples or questions.
- Inventing unspecified business rules, selectors, endpoints, or fixtures to complete a generated test.
- Putting every boundary and data partition into a slow browser suite.
- Asserting only clicks, response status, or element visibility instead of the story outcome.
- Using long CSS chains, XPath tied to layout, or a test ID for every visible control.
- Adding fixed sleeps instead of waiting for an observable condition.
- Reusing one account or record across tests and depending on execution order.
- Hiding failed setup so the UI test reports a misleading product defect.
- Accepting AI-generated code because it compiles without proving that its oracle detects a fault.
- Letting retries convert an unstable test into a normal pass.
- Updating assertions to match changed behavior without confirming the product contract.
Conclusion
Generating Playwright tests from a user story is effective when test design leads and code follows. Clarify the contract, build concrete examples and a risk matrix, choose the correct layer, arrange isolated data, and assert observable user outcomes with supported Playwright APIs. The runnable subscription project demonstrates that full path.
Use AI to accelerate drafting only within supplied repository facts, then compile, execute, fault-inject, and review its output. Begin with one high-value story, keep the browser suite focused, and make every test earn its maintenance cost through distinct, diagnosable evidence.
Interview Questions and Answers
How would you generate Playwright tests from a user story?
I first convert the story into concrete examples, risks, states, boundaries, and unresolved questions. I build a traceability matrix and select the lowest useful layer for each scenario. Then I implement a small set of independent browser journeys using semantic locators, supported setup APIs, and web-first assertions, followed by compile, execution, and fault-detection review.
What information do you need before automating a user story?
I need the actor, value, entry conditions, confirmed business rules, observable outcomes, states, data ownership, dependencies, accessibility expectations, and supported environments. I also record ambiguities such as normalization or failure behavior rather than guessing them in test code.
How do you decide whether a scenario belongs in a Playwright UI test?
I use a browser test when the risk depends on real user interaction, rendering, client integration, navigation, or accessible feedback. Pure rules and broad data partitions belong in unit or API layers. The browser suite retains critical journeys and a few representative negative paths.
Why are role and label locators preferred in Playwright?
They express user intent and are less coupled to DOM structure than layout selectors. They also encourage accessible markup and make the test readable. I use test IDs intentionally when a stable user-facing locator does not exist.
How do you make Playwright tests parallel-safe?
Each test creates its own unique state and never relies on execution order or a prior test. I isolate accounts or entities, make cleanup idempotent, avoid shared mutable files, and verify setup. I run tests individually, repeatedly, and in parallel to prove the design.
How do you verify an AI-generated test is valuable?
I map it to a confirmed risk and inspect whether the assertion proves the user outcome. I run it on a known-good build and inject or simulate the covered defect to ensure it fails for the intended reason. I also compare review and maintenance cost with the distinct coverage it adds.
What are common red flags in generated Playwright code?
Invented helpers, nonexistent selectors, fixed sleeps, broad text matches, hard-coded shared data, swallowed setup errors, unconditional retries, skipped tests, and assertions that only confirm an action occurred are major red flags. New dependencies and external hosts also need explicit review.
Frequently Asked Questions
How do you generate Playwright tests from a user story?
Extract the actor, goal, rules, states, and open questions, then convert acceptance criteria into observable examples and a risk matrix. Select the lowest useful test layer and implement only the journeys needing browser-level evidence.
Should every acceptance criterion become one Playwright test?
No. One criterion may require several lower-layer partitions, while several criteria may form one coherent browser journey. Organize tests for independent behavioral evidence, diagnosis, and maintenance.
Can AI generate reliable Playwright tests?
AI can create useful drafts when supplied with confirmed behavior, DOM or accessibility contracts, APIs, fixtures, and repository conventions. Every draft still needs interface verification, compilation, execution, fault injection, security review, and human judgment.
Which locators should generated Playwright tests use?
Prefer locators based on role and accessible name, label, and other user-facing semantics. Use intentional test IDs where a stable user-facing identity is unavailable, and avoid brittle layout-dependent CSS or XPath chains.
How should Playwright test data be created from a story?
Create unique, recognizable data through supported APIs or fixtures, assert setup success, and keep every test independent. Choose idempotent cleanup or disposable environments and avoid customer data.
How do you validate AI-generated Playwright assertions?
Trace each assertion to an approved rule and observable outcome, run it on the known-good system, then deliberately break the covered behavior. The test should fail for the expected reason with useful evidence.
What should block generated Playwright tests from merging?
Block invented interfaces, unsafe hosts, exposed secrets, compilation or execution failure, skipped coverage, incorrect or weak oracles, order dependence, and uncontrolled shared data. Review requirements can also mandate traceability and clean parallel runs.