1---2name: playwright-generate-test-43description: Generate, save, run, and stabilize Playwright TypeScript tests from a user scenario using Playwright MCP exploration evidence. Use this skill when the user asks to create a Playwright test, generate an @playwright/test spec from a scenario, automate a browser flow, or turn Playwright MCP history into a passing test.4---56<!-- Generated from harness/github-copilot/plugins/testing-automation/skills/playwright-generate-test/SKILL.md by harness/claude-code/scripts/convert_from_copilot.py. Edit the source, not this file. -->78# Playwright test generation910Generate a Playwright TypeScript test from a user scenario by first exercising the scenario with Playwright MCP, then writing the spec into the `tests` directory, executing it, and iterating until it passes.1112## When to invoke1314- "Generate a Playwright test for this checkout flow."15- "Use Playwright MCP to create a test from this scenario."16- "Turn the browser interactions into an @playwright/test spec."17- "Save a Playwright test in the tests directory and make it pass."18- "Automate this UI scenario with Playwright."1920## Prerequisites and context2122- A concrete scenario is required: target URL or route, starting state, actions, and expected result. If the user provides no scenario, request one before generating code.23- Use Playwright MCP for browser navigation and interaction evidence before writing test code.24- Use the project's existing Playwright setup; do not add a new runner when `@playwright/test` is already present.25- Save generated specs under the existing `tests` directory. If the repository uses a nested Playwright convention such as `tests/e2e`, follow the existing convention inside `tests`.2627## Procedure28291. Parse the scenario into preconditions, actions, assertions, and any data dependencies.302. Open the app with Playwright MCP and perform the scenario one step at a time. Do not generate test code prematurely or solely from the written scenario.313. Capture durable selector evidence from the explored UI: accessible roles, labels, text, test ids, URLs, and visible state changes.324. Convert the completed interaction history into a Playwright TypeScript test using `@playwright/test`.335. Save the generated test file in the `tests` directory with a descriptive `.spec.ts` name.346. Execute the new test file with the existing project command when available, or with `npx playwright test <test-file>` when the project has Playwright installed.357. Fix selector, timing, setup, or assertion issues and rerun the same test until it passes or a real product defect blocks it.3637## Scenario extraction3839| Scenario part | Capture | Test representation |40| --- | --- | --- |41| Entry point | URL, route, fixture, auth state, viewport if relevant | `await page.goto('<url>')` or existing test fixture setup |42| User action | Clicks, fills, selections, keyboard input, uploads, navigation | `page.getByRole`, `getByLabel`, `getByText`, `locator`, `selectOption`, `press` |43| Observable result | URL change, visible message, table row, enabled control, network-driven UI state | `await expect(...).toBeVisible()`, `toHaveURL`, `toContainText`, `toBeEnabled` |44| Required wait | UI transition, navigation, async rendering | Prefer web-first assertions; avoid arbitrary sleeps |45| Test data | Unique names, emails, IDs, clean-up needs | Generate deterministic or unique data inside the test without hardcoded shared secrets |4647## Selector strategy4849| Preference | Use | Avoid |50| --- | --- | --- |51| Accessible role | `page.getByRole('button', { name: 'Save' })` | CSS class chains tied to styling |52| Form labels | `page.getByLabel('Email')` | Index-based selectors like `locator('input').nth(2)` |53| Stable test ids | `page.getByTestId('submit-order')` when the project uses them | Adding test ids without user approval unless already conventional |54| Visible text | `page.getByText('Order submitted')` for user-facing outcomes | Text that is dynamic, localized, or incidental |55| Structural locator | Scoped `locator()` only when accessible selectors are unavailable | XPath copied from browser devtools |5657## Test construction5859- Import from `@playwright/test`: `import { test, expect } from '@playwright/test';`.60- Name the test after the user-visible behavior, not the implementation detail.61- Keep the generated file focused on the requested scenario unless the user asks for a suite.62- Use `test.describe` only when grouping multiple related tests in the same file.63- Assert the final business outcome and at least one intermediate state when it prevents false positives.64- Prefer Playwright web-first assertions over `page.waitForTimeout`.65- Keep credentials, tokens, and private data out of the test. Use existing fixtures or environment-driven auth when the project already provides them.6667## Common defects6869| Defect | Why it fails | Correction |70| --- | --- | --- |71| Code before exploration | The test encodes assumptions and misses actual labels, routing, or timing | Complete all Playwright MCP steps before emitting code |72| Brittle selector | Styling or DOM order changes break the test without behavior changing | Use role, label, text, or test id selectors |73| Missing assertion | The test only clicks through the flow and can pass while the feature is broken | Assert visible outcome, URL, or persisted UI state |74| Arbitrary sleep | Slow or fast environments make `waitForTimeout` flaky | Use `await expect(locator).to...` |75| Unscoped data | Shared fixed values collide across runs | Generate unique data or clean up through existing helpers |7677## Troubleshooting7879| Symptom | Likely cause | Resolution |80| --- | --- | --- |81| Test cannot find a locator | The selected locator was not stable or the element appears later | Re-open the page with Playwright MCP, inspect accessible names, and replace with a web-first locator plus assertion |82| Test passes locally but fails in CI | Timing, viewport, auth state, or test data differs | Remove sleeps, assert readiness, use existing storage state or fixtures, and avoid shared data |83| Navigation assertion times out | The action does not navigate or the URL pattern is too strict | Assert the actual post-action UI state, or use a looser `toHaveURL` pattern backed by exploration evidence |84| Browser is not installed | Playwright dependency exists but browsers are missing | Run the project's documented install command, commonly `npx playwright install`, only when dependency setup is expected |8586## Output template8788```markdown89## Playwright test generation result9091**Status:** passed | blocked92**Scenario:** <one-sentence scenario summary>93**Test file:** `tests/<name>.spec.ts`9495### Exploration evidence96| Step | Interaction | Selector or URL | Observed outcome |97| --- | --- | --- | --- |98| 1 | <action> | `<selector-or-url>` | <visible result> |99100### Generated test101- Framework: `@playwright/test`102- Command: `<test command>`103- Result: pass | fail104105### Remaining work106- <none, or blocker with evidence>107```108109## Quality gate110111- [ ] A user scenario was provided or explicitly requested before code generation.112- [ ] Playwright MCP was used step by step before emitting the test.113- [ ] The emitted test imports from `@playwright/test`.114- [ ] The test file is saved under the `tests` directory.115- [ ] Selectors prefer roles, labels, text, or stable test ids over brittle DOM structure.116- [ ] Assertions verify the user-visible outcome of the scenario.117- [ ] The generated test file was executed.118- [ ] Failures were iterated until the test passed or a blocker was reported with evidence.