playwright-testing
activeGenerates, runs, and debugs Playwright end-to-end tests for web applications. Automates browser testing setup, creates test files with accessible locators, and validates UI behavior. Use when the user asks to add tests, write e2e tests, test the frontend, verify UI behavior, add Playwright, set up browser testing, or when Claude needs to verify that frontend changes work correctly after implementation. Also triggers for requests like "make sure this works", "test the UI", "check the page renders", or any task involving browser-based verification of a React/Vite/Next.js application. Do NOT use for unit tests, component-level testing with Jest/Vitest, or backend API testing without a browser.
Quality Score Breakdown
Show checks (11)
- ✓ SKILL.md exists with exact casing 3/3
- ✓ Valid YAML frontmatter 3/3
- ✓ No unexpected frontmatter keys 1/1
- ✓ Name field valid (kebab-case) 2/2
- ✓ Name matches folder name 1/1
- ✓ Description field present 2/2
- ✓ No angle brackets in frontmatter 1/1
- ✓ Folder name is kebab-case 1/1
- ✓ No README.md inside skill folder 1/1
- ✓ Test directory with test-cases.yml exists 2/2
- ✓ Status 'active' is valid 1/1
Show checks (7)
- ✓ Contains action verbs: creates, generates, validates, automates 4/4
- ✓ Contains trigger indicators: use when, use for, trigger, asks to, requests 5/5
- ✓ Description is specific and actionable 4/4
- ✓ File types mentioned in description 3/3
- ✓ Description length: 699/1024 chars 2/2
- ✓ Has negative triggers (scope boundaries) 2/2
- ✓ Owner/author specified in metadata 2/2
Show checks (8)
- ✓ Skill body has content 3/3
- ✓ Has step/section structure 4/4
- ✓ Includes examples 5/5
- ✓ Includes error handling 4/4
- ✓ Uses progressive disclosure (references/scripts) 4/4
- ✓ Actionable language: 4/10 verb patterns found 3/3
- ✓ Word count: 585/5000 2/2
- ✓ All referenced paths exist 3/3
Show checks (10)
- ✓ test-cases.yml exists and parses 3/3
- ✓ 7 should-trigger tests ✓ 4/4
- ✓ 5 should-not-trigger tests ✓ 3/3
- ✓ 7 functional tests ✓ 5/5
- ✓ 2 negative tests ✓ 3/3
- ✓ 2 edge case tests ✓ 3/3
- ✓ Performance baseline documented 2/2
- ✓ All functional tests have ≥2 assertions 2/2
- ✓ All trigger phrases are diverse 2/2
- ✓ All assertions are specific 2/2
Show checks (5)
- ✓ No secrets detected 5/5
- ✓ No injection vectors in frontmatter 3/3
- ✓ Name is not reserved 3/3
- ✓ No suspicious code patterns 2/2
- ✓ External URLs: 2 2/2
Test Coverage
Playwright E2E Testing
Setup verification
Before writing any tests, verify Playwright is installed:
# Check if playwright config exists
ls playwright.config.ts 2>/dev/null || ls playwright.config.js 2>/dev/null
# Check if @playwright/test is in devDependencies
grep -q "@playwright/test" package.json 2>/dev/null
If Playwright is NOT installed, run the setup script: bash scripts/setup-playwright.sh from this skill’s directory. Then adapt playwright.config.ts to the project’s specific dev server port and base URL.
Writing tests
File placement and naming
- Place tests in
e2e/at the project root (not insidesrc/) - Name files descriptively:
e2e/<feature>.spec.ts - Group related tests in subdirectories:
e2e/auth/login.spec.ts
Test structure pattern
import { test, expect } from '@playwright/test';
test.describe('Feature: <name>', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/relevant-route');
});
test('should <expected behavior> when <action>', async ({ page }) => {
// Arrange: locate elements
const element = page.getByRole('button', { name: 'Submit' });
// Act: perform user actions
await element.click();
// Assert: verify outcomes
await expect(page.getByText('Success')).toBeVisible();
});
});
Locator priority (most to least preferred)
page.getByRole()— accessible roles:'button','link','heading','textbox','checkbox'page.getByText()— visible text contentpage.getByLabel()— form labelspage.getByPlaceholder()— input placeholderspage.getByTestId()—data-testidattributes (add to components when other locators are ambiguous)page.locator('css-selector')— last resort only
Never use fragile selectors like .css-1a2b3c or div > span:nth-child(3).
Handling async UI patterns
// Wait for loading states to resolve
await expect(page.getByRole('progressbar')).toBeHidden();
// Wait for network idle after navigation
await page.waitForLoadState('networkidle');
// Wait for specific API responses
const responsePromise = page.waitForResponse('**/api/artists');
await page.getByRole('button', { name: 'Search' }).click();
await responsePromise;
// Retry assertions (Playwright auto-retries expects)
await expect(page.getByText('Results')).toBeVisible({ timeout: 10000 });
Mocking API responses
test('displays artist data from API', async ({ page }) => {
await page.route('**/api/artists*', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ artists: [{ name: 'Test Artist', listeners: 50000 }] }),
});
});
await page.goto('/dashboard');
await expect(page.getByText('Test Artist')).toBeVisible();
});
Visual regression (when appropriate)
test('dashboard renders correctly', async ({ page }) => {
await page.goto('/dashboard');
await expect(page.getByRole('main')).toBeVisible();
await expect(page).toHaveScreenshot('dashboard.png', { maxDiffPixelRatio: 0.01 });
});
Running tests
# Run all tests
npx playwright test
# Run specific file
npx playwright test e2e/dashboard.spec.ts
# Run with UI mode (interactive debugging)
npx playwright test --ui
# Run headed (visible browser)
npx playwright test --headed
# Run specific test by title
npx playwright test -g "should display search results"
# Update visual snapshots
npx playwright test --update-snapshots
# View HTML report after failures
npx playwright show-report
Debugging failures
- Check the HTML report:
npx playwright show-report - Run the failing test in UI mode:
npx playwright test --ui -g "test name" - Add
await page.pause()inside a test to open the inspector - Use trace viewer: ensure
trace: 'on-first-retry'is in config, then opentest-results/*/trace.zip
Test categories to cover for a dashboard/chat UI
For reference on which categories of tests to write, see references/test-categories.md.
Common mistakes to avoid
- Do NOT use
page.waitForTimeout()— use proper assertions or waitFor patterns - Do NOT hardcode viewport sizes — use config or test.use() for responsive tests
- Do NOT test implementation details — test user-visible behavior
- Do NOT write tests that depend on execution order between files
- Do NOT skip flaky tests — fix the root cause (usually a missing wait)
- Do NOT use
force: trueon clicks — if an element needs force-click, the test is wrong