| name | playwright-tester |
|---|---|
| description | Automated UI testing using Playwright's native test runner. Writes persistent .spec.ts test files, runs them for free via `npx playwright test`, and fixes failures automatically. Use this skill whenever the user wants to test a web app, check if a frontend works, run UI tests, verify form submissions, test browser interactions, do end-to-end testing, stress test a website, or validate that recent changes didn't break anything. Also triggers for "run my tests", "check the app in a browser", "does this page work", "test the happy path", "find UI bugs", or any mention of Playwright, E2E tests, browser automation, or regression testing. This is the go-to skill for ANY browser-based testing — prefer it over manually driving browsers, screenshot-based approaches, or writing ad-hoc test scripts. When in doubt, use this skill. |
Write Playwright test files, run them natively (zero AI tokens), and fix failures in an automated loop.
There are three ways to do browser testing with AI:
- AI drives the browser live — burns tokens on every click, every page load, every assertion
- Screenshot-based (Claude-in-Chrome) — even worse, images are token-heavy and single-threaded
- AI writes tests, Playwright runs them — tokens spent once on authoring; execution is free forever
This skill uses approach 3. Test files persist, grow into a regression suite, and run in parallel via Playwright's native worker system. You get AI intelligence for test design without paying for execution.
Before starting, pick the right mode:
| Use single agent when... | Use swarm mode when... |
|---|---|
| Testing a specific feature or recent change | Testing an entire app or critical path comprehensively |
| User says "test this form" or "check if X works" | User says "test everything", "full coverage", "find bugs" |
| < 5 test scenarios needed | 10+ scenarios across different concern types |
| Fast iteration / "does this still work?" | Pre-release, first-time setup, or major refactor |
For swarm mode, jump to the Swarm Mode section below.
Before writing any tests, run a quick project check:
# Check for Playwright
cat package.json | grep playwright
# Check for existing config
ls playwright.config.ts 2>/dev/null || echo "No config found"
# Check for existing tests
find . -name "*.spec.ts" -o -name "*.spec.js" | head -20If Playwright is missing, install it:
npm install -D @playwright/test
npx playwright install chromiumIf playwright.config.ts is missing, create a minimal one (see Config Template below).
If it exists, read it before writing tests — especially the baseURL and webServer settings.
- Read the project structure — framework, entry points, routing, port the dev server runs on
- Check for existing test files; if found, read them to understand coverage and conventions
- Read
playwright.config.tsto understandbaseURL, timeouts, andwebServersetup - Identify what to test — if the user is vague, read the codebase and propose the highest-value targets (auth flows, core forms, critical user paths)
Write .spec.ts files organized by concern. Each file should be focused and independently runnable.
Recommended file layout:
tests/
e2e/
happy-path.spec.ts — core user journeys that must always work
validation.spec.ts — form validation, required fields, error states
edge-cases.spec.ts — empty inputs, special characters, boundary values
accessibility.spec.ts — keyboard nav, aria labels, screen reader basics
Locator priority (use in this order):
getByRole()— most resilient, matches user intentgetByLabel()— great for form fieldsgetByText()— good for buttons, links, visible contentdata-testidattribute — use when semantic locators aren't available- CSS selectors — last resort only; brittle, avoid
Good test hygiene:
- Each test must be independent — no shared mutable state between tests; use
beforeEachfor setup - Descriptive names that read like requirements:
test('submits contact form with valid data and shows success message') - Assert outcomes, not implementation:
expect(page.getByText('Success')).toBeVisible()notexpect(submitButton).toHaveClass('submitted') - For forms: test the complete round-trip (fill → submit → verify result), not just individual fields
- One behavior per test; multiple assertions are fine if they all verify the same behavior
When existing tests are found:
- Read them first to match style and avoid duplicating coverage
- Add new tests for uncovered areas; don't rewrite working tests
- Only update broken tests if the breakage reflects intentional code changes
npx playwright test --reporter=line 2>&1Key flags:
--reporter=line— clean output, easy to parse--project=chromium— single browser for speed during dev; run all browsers before deploy--headed— only when the user explicitly asks to watch tests run--trace on— when a failure is hard to diagnose (generates a trace file for deep debugging)
Playwright handles parallelism natively — no sub-agents needed for execution.
When tests fail:
Step 1 — Categorize the failure:
| Type | Description | Fix |
|---|---|---|
| Test bug | Wrong selector, bad assertion, race condition | Fix the test file |
| App bug | The application is actually broken | Fix app code, explain what broke |
| Flaky | Passes sometimes, fails sometimes | Add waitFor, increase timeout on that action only |
Don't conflate these — fixing the wrong thing wastes loops.
Step 2 — Fix and rerun:
npx playwright test --reporter=line 2>&1Always rerun after every fix. Report the rerun output even if everything passes — the user needs confirmation.
Step 3 — If still failing after 3 attempts:
Stop. Report exactly what's failing, why you think it's failing, and what you'd need to proceed. Don't keep iterating on something that might need architectural input.
After all tests pass (or the fix loop is exhausted):
- Summary table: total tests / passed / failed / skipped
- Any app bugs found and fixed — list explicitly
- Suggested additional coverage areas
- Flag any tests that passed on retry (flaky — needs attention)
Use when comprehensive coverage across multiple angles is needed simultaneously.
Spawn 3 sub-agents to write tests in parallel, then run the full suite once:
- Agent 1 — Happy Path: Core user journeys, success flows, standard inputs
- Agent 2 — Validation & Edge Cases: Error states, empty inputs, boundary values, special characters, XSS attempts
- Agent 3 — Accessibility & UX: Keyboard navigation, focus management, aria attributes, responsive behavior
Each agent writes to tests/e2e/. Once all agents complete, run:
npx playwright test --reporter=line 2>&1Playwright's worker system handles parallel execution of the combined test suite.
The key insight: sub-agents spend tokens on test design (high-value, creative); execution costs zero tokens regardless of how many tests were written.
If no playwright.config.ts exists, create one appropriate to the project. Generic starter:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: 'line',
use: {
baseURL: 'http://localhost:3000', // adjust to match actual dev server port
trace: 'on-first-retry',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
],
webServer: {
command: 'npm run dev', // adjust to match actual start command
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});Before creating this file: check package.json for the actual dev script name and port. A wrong
port means every test fails with a connection error before anything meaningful runs.
1. Wrong base URL / port
Most first-run failures aren't test bugs — they're connection refused errors because the port in
playwright.config.ts doesn't match the actual dev server. Always verify the port first.
2. Asserting before navigation completes
After page.goto(), some SPAs render asynchronously. Use await page.waitForLoadState('networkidle')
or assert on a visible element before interacting with the page.
3. Hard-coded selectors on dynamic content
If elements have auto-generated IDs or classes (common in component libraries), CSS selectors will
break on the next build. Use getByRole, getByLabel, or ask the dev team to add data-testid.
- Start narrow, expand later. Test the specific thing that changed first, then broaden.
- Don't over-test. 10-15 meaningful tests beat 100 trivial ones. Test behaviors users care about.
- CI-ready by default. Test files created here run in CI with zero modification.
- Use
--trace onfor hard failures. It generates a full trace you can inspect step-by-step.