Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use when writing E2E tests with Playwright, setting up test infrastructure, or debugging flaky browser tests. Invoke to write test scripts, create page objects, configure test fixtures, set up reporters, add CI integration, implement API mocking, or perform visual regression testing. Trigger terms: Playwright, E2E test, end-to-end, browser testing, automation, UI testing, visual testing, Page Object Model, test flakiness.
.claude/skills/jeffallan-playwright-expert/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-05 | ✗→✓ | ▲ Improved | 24% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 35% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 141% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 67% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 60% | 0% |
E2E testing specialist with deep expertise in Playwright for robust, maintainable browser automation.
Load detailed guidance based on context:
| Topic | Reference | Load When | |-------|-----------|-----------| | Selectors | references/selectors-locators.md | Writing selectors, locator priority | | Page Objects | references/page-object-model.md | POM patterns, fixtures | | API Mocking | references/api-mocking.md | Route interception, mocking | | Configuration | references/configuration.md | playwright.config.ts setup | | Debugging | references/debugging-flaky.md | Flaky tests, trace viewer |
waitForTimeout() (use proper waits)first(), nth() without good reasontypescript// ✅ Role-based selector — resilient to styling changes await page.getByRole('button', { name: 'Submit' }).click(); await page.getByLabel('Email address').fill('user@example.com'); // ❌ CSS class selector — breaks on refactor await page.locator('.btn-primary.submit-btn').click(); await page.locator('.email-input').fill('user@example.com');
typescript// pages/LoginPage.ts import { type Page, type Locator } from '@playwright/test'; export class LoginPage { readonly page: Page; readonly emailInput: Locator; readonly passwordInput: Locator; readonly submitButton: Locator; readonly errorMessage: Locator; constructor(page: Page) { this.page = page; this.emailInput = page.getByLabel('Email address'); this.passwordInput = page.getByLabel('Password'); this.submitButton = page.getByRole('button', { name: 'Sign in' }); this.errorMessage = page.getByRole('alert'); } async goto() { await this.page.goto('/login'); } async login(email: string, password: string) { await this.emailInput.fill(email); await this.passwordInput.fill(password); await this.submitButton.click(); } }
typescript// tests/login.spec.ts import { test, expect } from '@playwright/test'; import { LoginPage } from '../pages/LoginPage'; test.describe('Login', () => { let loginPage: LoginPage; test.beforeEach(async ({ page }) => { loginPage = new LoginPage(page); await loginPage.goto(); }); test('successful login redirects to dashboard', async ({ page }) => { await loginPage.login('user@example.com', 'correct-password'); await expect(page).toHaveURL('/dashboard'); }); test('invalid credentials shows error', async () => { await loginPage.login('user@example.com', 'wrong-password'); await expect(loginPage.errorMessage).toBeVisible(); await expect(loginPage.errorMessage).toContainText('Invalid credentials'); }); });
typescript// 1. Run failing test with trace enabled // playwright.config.ts use: { trace: 'on-first-retry', screenshot: 'only-on-failure', } // 2. Re-run with retries to capture trace // npx playwright test --retries=2 // 3. Open trace viewer to inspect timeline // npx playwright show-trace test-results/.../trace.zip // 4. Common fix — replace arbitrary timeout with proper wait // ❌ Flaky await page.waitForTimeout(2000); await page.getByRole('button', { name: 'Save' }).click(); // ✅ Reliable — waits for element state await page.getByRole('button', { name: 'Save' }).waitFor({ state: 'visible' }); await page.getByRole('button', { name: 'Save' }).click(); // 5. Verify fix — run test 10x to confirm stability // npx playwright test --repeat-each=10
When implementing Playwright tests, provide:
Playwright, Page Object Model, auto-waiting, locators, fixtures, API mocking, trace viewer, visual comparisons, parallel execution, CI/CD integration
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | pass→pass | 5,661 | 5,177 | -9% | 1 | 1 | 0% | 972 | 2,345 | +141% | 0 | 0 | — |
case-02 | pass→pass | 7,532 | 4,668 | -38% | 1 | 1 | 0% | 1,242 | 2,071 | +67% | 0 | 0 | — |
case-03 | fail→fail | 9,081 | 7,916 | -13% | 1 | 1 | 0% | 1,750 | 2,767 | +58% | 0 | 0 | — |
case-04 | pass→pass | 9,283 | 8,098 | -13% | 1 | 1 | 0% | 1,738 | 2,773 | +60% | 0 | 0 | — |
case-05 | fail→pass | 10,366 | 5,327 | -49% | 1 | 1 | 0% | 1,959 | 2,425 | +24% | 0 | 0 | — |
case-06 | pass→pass | 8,806 | 6,533 | -26% | 1 | 1 | 0% | 1,800 | 2,602 | +45% | 0 | 0 | — |
case-07 | pass→pass | 8,426 | 6,941 | -18% | 1 | 1 | 0% | 1,804 | 2,885 | +60% | 0 | 0 | — |
case-08 | pass→pass | 4,975 | 2,807 | -44% | 1 | 1 | 0% | 1,010 | 1,780 | +76% | 0 | 0 | — |
case-09 | pass→pass | 2,885 | 2,148 | -26% | 1 | 1 | 0% | 528 | 1,657 | +214% | 0 | 0 | — |
case-10 | pass→pass | 9,733 | 2,322 | -76% | 1 | 1 | 0% | 1,600 | 1,639 | +2% | 0 | 0 | — |
case-11 | pass→pass | 1,923 | 1,752 | -9% | 1 | 1 | 0% | 339 | 1,540 | +354% | 0 | 0 | — |
case-12 | pass→pass | 2,096 | 2,143 | +2% | 1 | 1 | 0% | 349 | 1,601 | +359% | 0 | 0 | — |
case-13 | pass→pass | 13,593 | 10,887 | -20% | 1 | 1 | 0% | 2,289 | 3,404 | +49% | 0 | 0 | — |
case-14 | fail→pass | 13,137 | 9,540 | -27% | 1 | 1 | 0% | 2,439 | 3,297 | +35% | 0 | 0 | — |
case-15 | pass→pass | 7,595 | 5,325 | -30% | 1 | 1 | 0% | 1,337 | 2,125 | +59% | 0 | 0 | — |
case-16 | pass→pass | 5,470 | 5,617 | +3% | 1 | 1 | 0% | 916 | 2,299 | +151% | 0 | 0 | — |
case-17 | pass→pass | 9,669 | 9,186 | -5% | 1 | 1 | 0% | 1,842 | 3,049 | +66% | 0 | 0 | — |
case-18 | pass→pass | 5,925 | 6,004 | +1% | 1 | 1 | 0% | 979 | 2,179 | +123% | 0 | 0 | — |
case-19 | pass→pass | 13,840 | 12,086 | -13% | 1 | 1 | 0% | 2,443 | 3,325 | +36% | 0 | 0 | — |
case-20 | pass→pass | 19,410 | 13,890 | -28% | 1 | 1 | 0% | 2,862 | 3,716 | +30% | 0 | 0 | — |
case-21 | pass→pass | 9,039 | 8,225 | -9% | 1 | 1 | 0% | 1,676 | 2,812 | +68% | 0 | 0 | — |
case-22 | pass→pass | 6,459 | 5,734 | -11% | 1 | 1 | 0% | 1,145 | 2,362 | +106% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +9 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.