What to Review Before You Commit Playwright Codegen Output
Review Playwright Codegen output for stable locators, meaningful assertions, explicit setup, and failure evidence before committing generated tests.
Playwright Codegen is excellent at turning a browser walkthrough into runnable code. It is not a code-review decision.
That distinction matters because generated tests often look finished. They open the right page, fill the right field, click the right button, and may even contain a useful assertion. A clean recording can still depend on accidental state, choose a locator that only works in today's DOM, or stop before the business outcome is actually visible.
The practical answer is simple: use Codegen to capture intent quickly, then review the output as test code before it reaches CI.
This guide gives you the commands, a worked synthetic checkout example, and a copyable review card for that second step.
What Playwright Codegen does
Playwright's official test-generator documentation says Codegen records browser actions and generates test code while you interact with the page. It prioritizes role, text, and test-id locators, and it refines a locator when more than one element matches.
Run it with a starting URL:
npx playwright codegen https://example.com
You can also choose a target language:
npx playwright codegen --target=playwright-test https://example.com
For authenticated work, save browser state during one recording and load it later:
npx playwright codegen --save-storage=auth.json https://example.com/login
npx playwright codegen --load-storage=auth.json https://example.com/account
Treat auth.json as sensitive. It can contain cookies and local storage. Keep it out of source control unless the file is a deliberately sanitized fixture.
Codegen gives you a fast first draft. The output still needs decisions about setup, assertions, test data, secrets, network behavior, and the result that makes the scenario valuable.
The five-part review
Review generated code in this order.
1. Make the starting state explicit
Ask what had to be true before recording began.
A generated script may preserve the clicks you performed without preserving how the account, cart, permissions, feature flags, locale, or browser storage reached that state. The test can pass on your machine because your recording profile already contains the answer.
Move durable setup into fixtures, API helpers, or explicit UI steps. Pin the environment dimensions that can change behavior. If you load storage state, document which role and account state it represents.
A useful test should be able to answer:
role: returning buyer
cart: one in-stock item
locale: en-US
feature flag: redesigned checkout on
starting URL: /checkout/shipping
Without that contract, the first failure in CI becomes a reconstruction exercise.
2. Review every locator as a product contract
Generated does not mean permanent.
Prefer a locator that describes how a user or tester understands the control. Playwright recommends user-facing attributes such as role and accessible name. A test id can be appropriate when the product deliberately maintains it as a testing contract.
Be suspicious of selectors tied to generated classes, DOM depth, positional filters, or nearby text that is likely to change.
For example, this is coupled to implementation detail:
await page.locator('.action-42').first().click();
This describes the intended control:
await page.getByRole('button', { name: 'Continue to payment' }).click();
This can also be valid when the test id is an explicit contract:
await page.getByTestId('continue-checkout').click();
The best choice depends on the UI. Do not replace every generated locator mechanically. Verify uniqueness, accessibility, and stability on the actual page.
3. Replace recorded motion with meaningful assertions
A recording proves that actions were accepted while you recorded them. A test needs to prove the outcome you care about.
Generated code may end after a click:
await page.getByRole('button', { name: 'Continue to payment' }).click();
Add an assertion at the receiver-owned boundary:
await expect(page.getByRole('status')).toHaveText('Payment step ready');
For a real checkout, that boundary might be a payment step, order record, confirmation number, or independent read-back. Choose the smallest durable outcome that separates success from a button that merely accepted input.
4. Decide what evidence a failure must preserve
Codegen produces test code, not a complete failure-evidence policy.
A practitioner question on Stack Overflow asks whether Codegen can record all network requests and responses and later replay the test from that traffic. The question exposes an important boundary: action generation, network capture, and deterministic replay are related jobs, but they are not the same feature.
Decide what CI should retain before the first failure occurs:
use: {
trace: 'retain-on-failure',
screenshot: 'only-on-failure',
video: 'retain-on-failure'
}
Then narrow the evidence to the scenario. If a save depends on one API response, make that response or resulting state legible. If a role or feature flag changes the page, preserve it in the test metadata. More files are not automatically better. The goal is to make the first contradiction easy to find.
5. Remove recorder noise and expose intent
Recordings often include exploratory detours, repeated clicks, long literal text, or navigation that belongs in shared setup.
Delete actions that do not contribute to the scenario. Name the test after the behavior, not the page. Extract repeated setup only when the abstraction makes the business state clearer.
A useful final shape is short enough to scan:
test('returning buyer can continue from shipping to payment', async ({ page }) => {
await seedCheckout({ role: 'returning-buyer', items: ['sku-123'] });
await page.goto('/checkout/shipping');
await page.getByLabel('Email').fill('buyer@example.test');
await page.getByRole('button', { name: 'Continue to payment' }).click();
await expect(page.getByRole('heading', { name: 'Payment' })).toBeVisible();
});
The helper name, locator choices, and final assertion now explain the test without replaying the recording in your head.
A synthetic locator review
I built a small local checkout fixture for this article. It is synthetic, not a customer application or performance benchmark.
The page contains two buttons that share the class action-42. One is Continue to payment; the other is Continue shopping. The payment button also has the explicit test id continue-checkout, and clicking it reveals a status message that reads Payment step ready.
A browser inspection returned these counts:
.action-42 matches: 2
[data-testid="continue-checkout"] matches: 1
visible button names: Continue to payment, Continue shopping
After clicking the payment button, an independent DOM read-back confirmed:
status visible: true
status text: Payment step ready
The lesson is not that test ids always beat roles. The role and accessible name are clear and unique in this fixture. The lesson is that review turns a plausible generated action into an explicit decision about target identity and outcome.
Copyable Playwright Codegen review card
Paste this into a pull request that includes generated browser tests:
### Playwright Codegen review
- [ ] Starting role, data, storage, flags, locale, and URL are explicit
- [ ] Secrets and recorded auth state are not committed
- [ ] Each locator describes a stable user-facing or test contract
- [ ] Positional, generated-class, and DOM-depth selectors are justified or removed
- [ ] Exploratory clicks and duplicate navigation are removed
- [ ] The test asserts a consequential result, not only click acceptance
- [ ] Async work is verified at the correct completion boundary
- [ ] Failure evidence is configured for the first useful contradiction
- [ ] Network or third-party dependencies are controlled or surfaced
- [ ] The test passes from a clean state without relying on the recorder profile
This review is small enough to use on every generated test. It also creates a better engineering handoff when the test later fails.
When Codegen is the wrong starting point
Do not force a recorder into every test.
Write the test directly when the workflow is mostly API setup, the important behavior is a reusable abstraction already present in the suite, or the scenario requires precise network and clock control from the beginning. Use Codegen when a real browser walkthrough is the fastest way to discover the interaction path and candidate locators.
The right division of labor is straightforward:
Codegen captures the path.
Review defines the contract.
Assertions prove the outcome.
Failure evidence makes the result diagnosable.
If locator choice is the part slowing you down, Samelogic's Playwright locator generator workflow is the relevant next step. It owns the locator-generation product intent; this article owns the narrower review-before-commit workflow.
Sources
Playwright test generator documentation: https://playwright.dev/docs/codegen
Playwright locator guidance: https://playwright.dev/docs/locators
Practitioner question about Codegen and network recording: https://stackoverflow.com/questions/78782510/is-it-possible-to-record-all-network-traffic-when-using-playwright-codegen
Samelogic Playwright locator generator: https://samelogic.com/workflows/playwright-locator-generator
Related workflows
Move from editorial context into the selector, Playwright, and bug-reproduction pages that turn exact UI evidence into action.




