skills/playwright-e2e-tester/SKILL.md
--- license: Apache-2.0 name: playwright-e2e-tester version: 1.0.0 category: Code Quality & Testing tags: - e2e - playwright - testing - automation - ci-cd - cross-browser --- # Playwright E2E Tester ## Overview Expert in end-to-end testing with Playwright, the modern cross-browser testing framework. Specializes in test generation, page object patterns, visual regression testing, and CI/CD integration. Handles complex testing scenarios including authentication flows, API mocking,
npx skillsauth add curiositech/windags-skills playwright-e2e-testerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Expert in end-to-end testing with Playwright, the modern cross-browser testing framework. Specializes in test generation, page object patterns, visual regression testing, and CI/CD integration. Handles complex testing scenarios including authentication flows, API mocking, and mobile emulation.
Is element semantic (button, heading, form control)?
├─ YES → Use role-based locators (getByRole, getByLabel)
│ └─ Expected to change frequently?
│ ├─ NO → Stop here (most stable)
│ └─ YES → Add getByTestId as backup
└─ NO → Is element purely presentational?
├─ YES → Use getByTestId (add data-testid attribute)
│ └─ Can't modify markup?
│ └─ Use CSS selector (last resort, document fragility)
└─ NO → Use getByText for content-based selection
Action type:
├─ Navigation (page.goto, click link) → Use waitForURL()
├─ Element appears/disappears → Use waitForSelector() or visibility assertions
├─ API response affects UI → Use waitForResponse() then element assertion
├─ Animation/transition → Use waitForFunction() with custom condition
└─ Network request completion → Use page.route() with route.fulfill()
Test complexity:
├─ Single page interaction → Inline test with direct selectors
├─ Multi-step flow → Use Page Object Model pattern
├─ Cross-page workflow → Use fixtures for shared state
└─ Multi-app integration → Use projects with different configs
Symptoms: Error: Element is not attached to the DOM
Detection Rule: If you see detachment errors during dynamic content updates
Fix: Replace element variables with fresh locator calls:
// BAD: Storing element reference
const button = page.locator('button');
await button.click(); // May fail if DOM updated
// GOOD: Fresh locator each time
await page.locator('button').click();
Symptoms: Tests fail with "Timeout exceeded" in CI but pass locally Detection Rule: If tests have inconsistent CI failures with 30s+ timeouts Fix: Implement explicit waits with proper conditions:
// BAD: Blind timeout increase
await page.waitForTimeout(5000);
// GOOD: Wait for specific condition
await page.waitForSelector('[data-testid="loading"]', { state: 'hidden' });
await expect(page.locator('[data-testid="results"]')).toBeVisible();
Symptoms: Intermittent failures where elements "aren't ready yet" Detection Rule: If test flakiness correlates with slow network/CPU Fix: Chain waits to ensure proper sequencing:
// BAD: Assuming immediate availability
await page.click('#submit');
await page.fill('#new-field', 'value'); // May fail
// GOOD: Wait for UI state transition
await page.click('#submit');
await page.waitForSelector('#new-field:not([disabled])');
await page.fill('#new-field', 'value');
Symptoms: Tests break when CSS classes or DOM structure changes Detection Rule: If tests fail after frontend refactoring without feature changes Fix: Migrate to semantic locators:
// BAD: Structural dependency
await page.click('.header > .nav > .item:nth-child(3)');
// GOOD: Semantic meaning
await page.getByRole('navigation').getByRole('link', { name: 'Products' }).click();
Symptoms: Visual regression tests fail due to minor rendering differences Detection Rule: If screenshot tests fail in CI with <5% pixel differences Fix: Configure appropriate tolerances and masks:
await expect(page).toHaveScreenshot('page.png', {
maxDiffPixelRatio: 0.01,
mask: [page.locator('[data-testid="dynamic-timestamp"]')],
animations: 'disabled'
});
Scenario: Test user login with error handling and success validation
Expert Approach:
import { test, expect } from '@playwright/test';
test.describe('User Authentication', () => {
test('should handle complete login flow', async ({ page }) => {
// Decision: Use Page Object for multi-step flow
const loginPage = new LoginPage(page);
await loginPage.goto();
// Decision: Test error state first (negative case)
await loginPage.signIn('[email protected]', 'wrongpass');
// Wait strategy: Error message should appear
await expect(page.getByRole('alert')).toContainText('Invalid credentials');
// Decision: Clear state before positive test
await loginPage.clearForm();
// Decision: Use valid test data
await loginPage.signIn('[email protected]', 'validpass');
// Wait strategy: Navigation indicates success
await page.waitForURL('/dashboard');
// Verification: Check authenticated state
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
await expect(page.getByText('Welcome back, John')).toBeVisible();
});
});
class LoginPage {
constructor(private page: Page) {}
async goto() {
await this.page.goto('/login');
// Wait for form to be interactive
await this.page.waitForSelector('form[data-testid="login-form"]');
}
async signIn(email: string, password: string) {
// Decision: Use semantic locators
await this.page.getByLabel('Email').fill(email);
await this.page.getByLabel('Password').fill(password);
await this.page.getByRole('button', { name: 'Sign In' }).click();
}
async clearForm() {
await this.page.getByLabel('Email').clear();
await this.page.getByLabel('Password').clear();
}
}
Novice would miss: Error state testing, proper wait strategies, form state management Expert catches: Complete flow coverage, semantic locators, defensive waits
Test implementation checklist:
Don't use this skill for:
vitest-testing-patterns insteadapi-architect for endpoint validationDelegate to other skills:
github-actions-pipeline-builderaccessibility-auditorvitest-testing-patternsdata-ai
license: Apache-2.0 NOT for unrelated tasks outside this domain.
development
Use when designing caching strategies (cache-aside, write-through, write-behind), implementing distributed locks, building rate limiters, leaderboards, real-time streams (XADD/consumer groups), pub/sub, or tuning eviction policies. Triggers: thundering-herd on cache miss, dogpile on key expiry, Redlock vs SET-NX-PX choice, sliding-window rate limiter, hot-key on a single cluster slot, big-key blowup, MULTI/EXEC across slots, KEYS in production. NOT for Redis Cluster operations/admin (different domain), embedded KV (SQLite, leveldb), in-process LRU caches, or Memcached.
tools
Drawing the `'use client'` boundary correctly in React Server Components apps (Next.js App Router, RSC frameworks) — leaf-pushing, slot composition, serialization rules, and environment poisoning prevention. Grounded in react.dev and Next.js 16 docs.
development
Use when designing rate limiting for an API, choosing between token bucket / sliding window / leaky bucket / fixed window, implementing it in Redis, deciding edge (Cloudflare/Upstash) vs origin enforcement, sizing per-user vs per-IP vs per-endpoint quotas, returning the right 429 response with Retry-After, or fixing the boundary-burst bug in fixed-window limiters. Triggers: 429 too many requests, INCR + EXPIRE, ZADD + ZREMRANGEBYSCORE + ZCARD, X-RateLimit-Remaining header, Cloudflare WAF rate limiting rules, Upstash @upstash/ratelimit, leaky bucket shaping vs policing, distributed rate limiter consistency. NOT for DDoS mitigation specifically (different scale), CAPTCHA / bot management, full WAF design, or per-user quota billing.