Skip to content

Instantly share code, notes, and snippets.

@strelec00
Created March 28, 2026 11:11
Show Gist options
  • Select an option

  • Save strelec00/b76230c45523a54597b6d115f78b80f7 to your computer and use it in GitHub Desktop.

Select an option

Save strelec00/b76230c45523a54597b6d115f78b80f7 to your computer and use it in GitHub Desktop.
Claude Code SKILL.md - automated Playwright test generation, organization, and execution.
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.

Playwright Tester

Write Playwright test files, run them natively (zero AI tokens), and fix failures in an automated loop.

Why This Approach

There are three ways to do browser testing with AI:

  1. AI drives the browser live — burns tokens on every click, every page load, every assertion
  2. Screenshot-based (Claude-in-Chrome) — even worse, images are token-heavy and single-threaded
  3. 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.


Quick Decision: Single Agent vs Swarm

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.


Prerequisites

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 -20

If Playwright is missing, install it:

npm install -D @playwright/test
npx playwright install chromium

If 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.


Workflow

Phase 1: Assess

  1. Read the project structure — framework, entry points, routing, port the dev server runs on
  2. Check for existing test files; if found, read them to understand coverage and conventions
  3. Read playwright.config.ts to understand baseURL, timeouts, and webServer setup
  4. 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)

Phase 2: Author Tests

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):

  1. getByRole() — most resilient, matches user intent
  2. getByLabel() — great for form fields
  3. getByText() — good for buttons, links, visible content
  4. data-testid attribute — use when semantic locators aren't available
  5. CSS selectors — last resort only; brittle, avoid

Good test hygiene:

  • Each test must be independent — no shared mutable state between tests; use beforeEach for 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() not expect(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

Phase 3: Execute

npx playwright test --reporter=line 2>&1

Key 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.

Phase 4: Fix Loop (max 3 attempts)

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>&1

Always 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.

Phase 5: Report

After all tests pass (or the fix loop is exhausted):

  1. Summary table: total tests / passed / failed / skipped
  2. Any app bugs found and fixed — list explicitly
  3. Suggested additional coverage areas
  4. Flag any tests that passed on retry (flaky — needs attention)

Swarm Mode

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>&1

Playwright'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.


Config Template

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.


Common Pitfalls

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.


Tips

  • 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 on for hard failures. It generates a full trace you can inspect step-by-step.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment