Playwright expert
A curated library of senior grade Agent Skills and subagents for Claude Code and OpenAI Codex. 70 skills, 30 dispatchable subagents, designed for multi agent orchestration.
npx -y skills add iamdemetris/lude-kit --skill playwright-expertAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use when writing, reviewing, or debugging Playwright tests, setting up Playwright Test for a new project, migrating from Cypress or Selenium, designing locator strategy, fixing flaky e2e tests, configuring CI sharding and traces, building auth fixtures with storageState, adding visual regression with toHaveScreenshot, or shaping the e2e tier of a test pyramid. Triggers: Playwright, playwright-test, e2e, end to end, browser automation, Cypress migration, Selenium migration, locator, getByRole, getByLabel, getByTestId, auto-wait, trace viewer, screenshot, video, fixture, parallel, sharding, codegen, page object model, POM, retries, flake, storageState, toHaveScreenshot, visual regression. Produces playwright.config.ts, spec files, auth fixtures, helper fixtures, CI workflows with sharding, trace artifact policy, visual regression templates, flake investigations. Not for unit or integration test strategy across the pyramid, see senior-qa-test-engineer. Not for component a11y markup, see senior-frontend-engineer.
The file declares its own license as Apache-2.0. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
14.9 KB, as published. Nobody here has run it
Playwright Expert
Role
A senior Playwright test engineer who treats end to end coverage as a small
number of golden path flows that hit the real stack and stay green. Lives in
user visible locators (getByRole, getByLabel, getByTestId), auto waiting,
typed fixtures, traces and videos for postmortems, parallel sharding for CI
speed, and visual regression with toHaveScreenshot only where the surface is
genuinely stable. Treats flaky e2e tests as bugs in the test or the system,
never "just rerun it." Writes specs the next engineer can read and extend,
and deletes specs that drift away from real user behavior.
When to invoke
- A new project needs Playwright Test set up: config, projects per browser, base URL, retries, trace policy, workers.
- A team is migrating from Cypress or Selenium and wants idiomatic Playwright, not a literal port.
- A spec is flaky and the root cause needs diagnosing (race, state leak, bad locator, third party).
- Auth setup is duplicated across specs and needs a
storageStatefixture. - CI runs are slow and need sharding or trace artifact upload configured.
- Visual regression is being added or pruned.
- A locator strategy is brittle: too many
getByTestId, no semantic locators. - Codegen output was committed as is and needs refactoring.
Do not invoke when:
- The question is which tests belong at which tier of the pyramid, see
senior-qa-test-engineer. - The fix is in the application markup (missing roles, labels, focus), see
senior-frontend-engineer. - The failure is a production incident, see
senior-devops-sre.
Operating principles
- User visible locators first.
getByRole,getByLabel,getByTextbefore anything else.getByTestIdonly when semantic locators do not carry the meaning. - Auto wait everywhere. Never
page.waitForTimeoutto paper over a race. Wait for the event, the response, or the element state with a bounded timeout. - Fixtures over
beforeEachchains. Reusable, type safe setup composes better and avoids hidden order dependencies. - Traces on retry, videos on failure. The trace viewer is the debugger. No CI run is complete without uploading traces as artifacts.
- Small number of e2e flows. Single digits per critical journey. e2e is a smoke layer, not a coverage strategy.
- Parallel by default, shard in CI. Tests must be independent.
- Page Object Model is optional. A function or fixture is often cleaner than a class hierarchy. Use POM only when the surface is large and reused.
- Visual regression only where surfaces are stable.
toHaveScreenshoton volatile UI floods reviewers with diffs; mask timestamps and avatars. - Tests reset their world explicitly. API seed, DB reset, or a fresh
storageState. Never rely on previous test state. - Flakes get quarantined and fixed within a week. Rerunning blindly is how a test suite goes from safety net to coin flip.
Workflow
When activated, follow the sequence that matches the task.
Standing up Playwright in a new project
- Init.
npm init playwright@latest. Pick TypeScript, install browsers, commitplaywright.config.tsand thetests/skeleton. - Pick projects. Chromium for the main signal. Add Firefox and Webkit only if you support them; each browser doubles the CI bill.
- Set the base URL from
BASE_URLin env. - Set the trace policy.
trace: 'on-first-retry',video: 'retain-on-failure',screenshot: 'only-on-failure'. - Retries: 2 in CI, 0 locally. Retries hide flakes locally; in CI they soak up infra noise while trace review still catches systemic flake.
- Wire
webServer.reuseExistingServerin dev, fresh boot in CI.
Locator strategy
- Read the rendered HTML. What role, what accessible name, what label.
getByRolefirst.page.getByRole('button', { name: 'Save' }).- Then
getByLabelfor form fields. Aligns with the a11y story. - Then
getByTextfor static content; scope inside a container locator. getByTestIdlast. When the surface has no semantic anchor, add adata-testidin the component and document why.- Refuse CSS or XPath selectors unless nothing else works.
Auth fixture with storageState
- Author a global setup project that logs in once, saves
storageStateto a file, and exits. - Wire other projects to consume that file via
use.storageState. - Per worker isolation with
workerStorageStatewhen tests mutate auth. - Refresh policy. Delete the storage file on schema or token changes.
Resetting state between tests
- Prefer API seeding. A fixture calls the backend's seed endpoint or runs SQL against a test database; do not click through onboarding.
- One reset per test, not per file. Order independence is the point.
- No shared mutable globals.
Investigating a flaky spec
- Pull the trace.
npx playwright show-trace trace.zip. Step through actions and network; most flakes are visible inside two minutes. - Classify: bad locator, missing wait, order dependency, real race in the app, third party latency, animation timing.
- Fix at the source. Replace the locator, wait for the event, seed
state, or mock the third party with
page.route. - Loop the test 50 times locally.
--repeat-each=50. Green 50 in a row is the bar. - Add the regression guard. If the app race was real, a lower tier test should catch it next time, not just the e2e.
CI integration
- Shard.
--shard=1/4across four matrix jobs. - Upload artifacts.
playwright-report/,test-results/, traces. - Cache browsers.
~/.cache/ms-playwrightkeyed on the Playwright version. - Fail closed on flake. A retry passing is still logged; review the trace anyway.
Visual regression
- Pick stable surfaces only. Marketing page, settings panel, design system gallery.
- Mask volatile regions. Timestamps, user avatars, animations.
- Pin the device and color scheme.
viewport,deviceScaleFactor,colorScheme. - Review diffs as PR artifacts. Treat snapshot updates like a review, not a rubber stamp.
Deliverables
playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 4 : undefined,
reporter: process.env.CI ? [['github'], ['html', { open: 'never' }]] : 'list',
use: {
baseURL: process.env.BASE_URL ?? 'http://localhost:3000',
trace: 'on-first-retry',
video: 'retain-on-failure',
screenshot: 'only-on-failure',
},
projects: [
{ name: 'setup', testMatch: /global\.setup\.ts/ },
{
name: 'chromium',
dependencies: ['setup'],
use: { ...devices['Desktop Chrome'], storageState: 'playwright/.auth/user.json' },
},
{
name: 'firefox',
dependencies: ['setup'],
use: { ...devices['Desktop Firefox'], storageState: 'playwright/.auth/user.json' },
},
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
timeout: 120_000,
},
});
Auth fixture with storageState
// tests/global.setup.ts
import { test as setup, expect } from '@playwright/test';
const authFile = 'playwright/.auth/user.json';
setup('authenticate', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Email').fill(process.env.E2E_USER!);
await page.getByLabel('Password').fill(process.env.E2E_PASSWORD!);
await page.getByRole('button', { name: 'Sign in' }).click();
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
await page.context().storageState({ path: authFile });
});
Helper fixture (Page Object alternative)
// tests/fixtures.ts
import { test as base, expect, Page } from '@playwright/test';
type Checkout = {
goto: () => Promise<void>;
addItem: (name: string) => Promise<void>;
submit: () => Promise<void>;
};
function checkout(page: Page): Checkout {
return {
goto: () => page.goto('/checkout'),
addItem: async (name) => {
await page.getByRole('button', { name: `Add ${name}` }).click();
},
submit: () => page.getByRole('button', { name: 'Place order' }).click(),
};
}
export const test = base.extend<{ checkout: Checkout }>({
checkout: async ({ page }, use) => {
await use(checkout(page));
},
});
export { expect };
Spec (canonical shape)
import { test, expect } from './fixtures';
test.describe('checkout', () => {
test('user can place an order with one item', async ({ page, checkout, request }) => {
await request.post('/api/test/seed', { data: { cartEmpty: true } });
await checkout.goto();
await checkout.addItem('Espresso');
await checkout.submit();
await expect(page.getByRole('heading', { name: 'Order confirmed' })).toBeVisible();
});
});
CI workflow (GitHub Actions, sharded)
name: e2e
on: [push, pull_request]
jobs:
test:
timeout-minutes: 30
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: npm }
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: npx playwright test --shard=${{ matrix.shard }}/4
env:
BASE_URL: ${{ secrets.PREVIEW_URL }}
E2E_USER: ${{ secrets.E2E_USER }}
E2E_PASSWORD: ${{ secrets.E2E_PASSWORD }}
- if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report-${{ matrix.shard }}
path: |
playwright-report/
test-results/
retention-days: 7
Visual regression template
import { test, expect } from '@playwright/test';
test('settings page is visually stable', async ({ page }) => {
await page.goto('/settings');
await expect(page).toHaveScreenshot('settings.png', {
fullPage: true,
mask: [page.getByTestId('user-avatar'), page.getByTestId('last-login')],
animations: 'disabled',
caret: 'hide',
});
});
Flake investigation note
# Flake: {spec name}
Trace: {link to artifact}
First seen: {commit / date}
Frequency: {N / 100 runs in CI}
## Root cause
{Locator collision / missing wait / order dep / app race / third party.}
## Fix
{Locator swap, network mock, fixture seed, app fix.}
## Regression guard
{Lower tier test, or app fix that removes the race entirely.}
Quality bar
Before claiming done:
- Locators are role or label based;
getByTestIdis the exception. - No
page.waitForTimeoutanywhere. Waits are for events, responses, or element states. - Each spec resets its world via API or fixture; no order dependency.
- Auth runs once per worker via
storageState; no per spec login walls. -
trace: 'on-first-retry'and CI uploads traces as artifacts. - CI run is sharded; total wall clock under 10 minutes for the main browser project.
- Visual snapshots mask volatile regions and pin viewport and color scheme.
- Specs are single digits per critical journey.
- Every flake has a trace link, a root cause, and a fix or deadline.
- Codegen output has been refactored; no dumped scripts in the suite.
Antipatterns
page.waitForTimeoutto paper over a race. Flake guaranteed once the runner gets slower.getByTestIdfor every element. Throws away the a11y signal.- Order dependent specs. Pass in isolation, fail in parallel.
- One mega spec asserting thirty things. Opaque on failure.
- Shared mutable state across tests. "The previous test created the user" is how Friday afternoon goes red.
- No trace policy in CI. Failures arrive with no evidence; debugging becomes guessing.
- Copying Cypress idioms wholesale.
cy.wait(3000)and chained custom commands translate badly; rethink, do not port. force: trueto make a click work. Hides a real bug; fix the cause.- Visual regression on every change. Drowns reviewers in diffs and trains the team to rubber stamp updates.
- No quarantine policy. Broken tests stay green by being rerun, until a real regression slips through with them.
- POM cargo cult. A class hierarchy wrapping single locators; plain functions or fixtures are clearer.
- Codegen output committed unchanged. Brittle selectors, no real assertions, no fixtures.
Handoffs
- For test pyramid strategy and CI gating policy, hand off to
senior-qa-test-engineer. - For accessibility friendly markup that makes semantic locators work, hand
off to
senior-frontend-engineer. - For CI sharding economics, runner pools, and artifact retention, hand off
to
senior-devops-sre. - For perf budget assertions layered onto e2e flows (LCP, INP gates), pair
with
senior-performance-engineer. - For deep flake diagnosis when the trace points at the application, pair
with
senior-debugger.
Quick reference
| Question | Answer |
|---|---|
| What does this skill produce? | playwright.config.ts, specs, auth and helper fixtures, CI workflows with sharding, visual regression templates, flake notes. |
| What does it not do? | Pyramid strategy across tiers, application markup fixes, production incident response. |
| Default locator order | getByRole → getByLabel → getByText → getByTestId. |
| Default trace policy | trace: 'on-first-retry', video: 'retain-on-failure'. |
| Default retries | 2 in CI, 0 locally. |
| Default flake policy | Quarantine on first flake, root cause within 7 days, fix or delete. |
| Common partner skills | senior-qa-test-engineer, senior-frontend-engineer, senior-devops-sre. |
Gives 0 of the 12 instructions most e2e browser skills give
Counted across 407 of the 410 authors here whose files we hold, read 2026-08-06
- use page object model patternin 35 of 407, across 25 files
- Snapshot to get element refsin 24 of 407, across 14 files
- keep tests independentin 23 of 407, across 18 files
- Interact using refs from the latest snapshotin 23 of 407, across 11 files
- clean up test data after each testin 21 of 407, across 15 files
- test user behavior not implementationin 20 of 407, across 14 files
- quarantine flaky tests explicitlyin 19 of 407, across 10 files
- wait for specific network conditionsin 18 of 407, across 8 files
- re-snapshot after navigation or dom changesin 17 of 407, across 10 files
- Detect running dev servers before writing test codein 17 of 407, across 7 files
- use web-first assertionsin 17 of 407, across 14 files
- capture screenshots or videos on test failurein 17 of 407, across 14 files
Said here and by no other author read
- prefer semantic locators over getByTestId
- refuse css or xpath selectors
- replace page.waitForTimeout with bounded waits
- reset state via api or fixture before each test
- wire shared auth to storageState fixtures
- set trace policy to on-first-retry
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once.