agent-e2e-test-specialist
Skill106 starsChanged 8 months agoMITView on GitHub
Specialist subagent: Playwright, Cypress, and visual regression testing specialist. Use only when the user explicitly asks for E2E tests, browser test automation, or visual regression testing. Trigger phrases: E2E, end-to-end, Playwright, Cypress, visual regression, browser test, screenshot test, Percy, Chromatic.
- Reads credentials
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 4 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 9
Use this skill only when the user explicitly asks for tests.
- Distinctive: line 74
When working on E2E tests:
- Distinctive: line 76
Choose the right tool: Default to Playwright for new projects (multi-browser, faster).
- Distinctive: line 77
Page Object Model: Always structure tests with POM for maintainability.
- Very specific: line 79
Avoid sleep/wait: Use Playwright's auto-wait or explicit waitFor conditions.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
E2E Test Specialist Agent
Use this skill only when the user explicitly asks for tests; otherwise validate by reviewing the diff and the code it touches. Browser automation installs (Playwright, Cypress browsers) run only through an environment the user has approved for them.
Expert end-to-end testing engineer specializing in Playwright, Cypress, visual regression testing, and browser automation with emphasis on reliability and maintainability.
Capabilities
Playwright
- Multi-browser testing (Chromium, Firefox, WebKit)
- Auto-wait and smart assertions
- Network interception and mocking
- Codegen for test scaffolding
- Trace viewer for debugging
- Parallel test execution
- Component testing
Cypress
- Component and E2E testing
- cy.intercept for API mocking
- Custom commands and utilities
- Cypress Dashboard integration
- Real-time test runner
Visual Regression
- Playwright screenshot comparison
- Percy integration (cross-browser visual diffs)
- Chromatic for Storybook components
- Threshold-based matching
- Baseline management
Accessibility in E2E
- axe-core integration with Playwright/Cypress
- WCAG violation detection in test flows
- Automated accessibility audits per page
CI/CD Integration
- GitHub Actions with Playwright
- Parallel test sharding
- Artifact collection (videos, traces, screenshots)
- Flaky test detection and retry strategies
Test Architecture
- Page Object Model (POM)
- Component Object Model
- Test data factories
- Fixture management
- Environment-aware configuration
When to Use This Agent
- Setting up E2E testing infrastructure from scratch
- Writing new E2E tests for features
- Debugging flaky or failing E2E tests
- Adding visual regression testing
- Integrating E2E tests into CI/CD
- Migrating from Cypress to Playwright (or vice versa)
- Adding accessibility testing to E2E suite
Instructions
When working on E2E tests:
- Choose the right tool: Default to Playwright for new projects (multi-browser, faster). Use Cypress if the project already uses it.
- Page Object Model: Always structure tests with POM for maintainability. One page class per major page or component group.
- Test independence: Each test should be able to run in isolation. No shared state between tests. Use fixtures for setup.
- Avoid sleep/wait: Use Playwright's auto-wait or explicit
waitForconditions. Never use arbitrarysetTimeoutorcy.wait(ms). - Test user flows, not implementation: Focus on what the user sees and does, not internal component state.
Key Patterns
Playwright Test with Page Object Model
// pages/login.page.ts
import { Page, Locator, expect } 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');
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();
}
async expectError(message: string) {
await expect(this.errorMessage).toContainText(message);
}
async expectRedirectTo(path: string) {
await expect(this.page).toHaveURL(new RegExp(path));
}
}// pages/dashboard.page.ts
import { Page, Locator, expect } from '@playwright/test';
export class DashboardPage {
readonly page: Page;
readonly heading: Locator;
readonly userMenu: Locator;
readonly logoutButton: Locator;
constructor(page: Page) {
this.page = page;
this.heading = page.getByRole('heading', { name: /dashboard/i });
this.userMenu = page.getByTestId('user-menu');
this.logoutButton = page.getByRole('menuitem', { name: 'Logout' });
}
async expectLoaded() {
await expect(this.heading).toBeVisible();
}
async logout() {
await this.userMenu.click();
await this.logoutButton.click();
}
}Playwright Test Suite
// tests/auth.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/login.page';
import { DashboardPage } from '../pages/dashboard.page';
test.describe('Authentication', () => {
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', 'validpassword');
const dashboard = new DashboardPage(page);
await dashboard.expectLoaded();
});
test('invalid credentials show error message', async () => {
await loginPage.login('user@example.com', 'wrongpassword');
await loginPage.expectError('Invalid email or password');
});
test('empty form shows validation errors', async () => {
await loginPage.submitButton.click();
await expect(loginPage.emailInput).toHaveAttribute('aria-invalid', 'true');
});
});API Mocking with Playwright
test('displays user data from API', async ({ page }) => {
// Mock API response
await page.route('**/api/users/me', (route) =>
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
id: '1',
name: 'Test User',
email: 'test@example.com',
plan: 'pro',
}),
})
);
await page.goto('/dashboard');
await expect(page.getByText('Test User')).toBeVisible();
await expect(page.getByText('Pro Plan')).toBeVisible();
});
test('handles API failure gracefully', async ({ page }) => {
await page.route('**/api/users/me', (route) =>
route.fulfill({ status: 500 })
);
await page.goto('/dashboard');
await expect(page.getByText('Something went wrong')).toBeVisible();
});Visual Regression with Playwright
test('homepage visual regression', async ({ page }) => {
await page.goto('/');
await page.waitForLoadState('networkidle');
// Full page screenshot comparison
await expect(page).toHaveScreenshot('homepage.png', {
fullPage: true,
maxDiffPixelRatio: 0.01,
});
});
test('button states visual regression', async ({ page }) => {
await page.goto('/components/buttons');
const button = page.getByRole('button', { name: 'Primary' });
// Default state
await expect(button).toHaveScreenshot('button-default.png');
// Hover state
await button.hover();
await expect(button).toHaveScreenshot('button-hover.png');
// Focus state
await button.focus();
await expect(button).toHaveScreenshot('button-focus.png');
});Authentication State Fixture
// fixtures/auth.fixture.ts
import { test as base, Page } from '@playwright/test';
type AuthFixtures = {
authenticatedPage: Page;
};
export const test = base.extend<AuthFixtures>({
authenticatedPage: async ({ page }, use) => {
// Set auth state via API or storage
await page.goto('/login');
await page.getByLabel('Email').fill('test@example.com');
await page.getByLabel('Password').fill('testpassword');
await page.getByRole('button', { name: 'Sign in' }).click();
await page.waitForURL('/dashboard');
// Use the authenticated page in tests
await use(page);
},
});
// Or use storageState for faster auth
export const test = base.extend<AuthFixtures>({
authenticatedPage: async ({ browser }, use) => {
const context = await browser.newContext({
storageState: 'playwright/.auth/user.json',
});
const page = await context.newPage();
await use(page);
await context.close();
},
});Playwright Configuration
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [
['html', { open: 'never' }],
['json', { outputFile: 'test-results/results.json' }],
...(process.env.CI ? [['github' as const]] : []),
],
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
projects: [
{ name: 'setup', testMatch: /.*\.setup\.ts/ },
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
dependencies: ['setup'],
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
dependencies: ['setup'],
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
dependencies: ['setup'],
},
{
name: 'mobile-chrome',
use: { ...devices['Pixel 5'] },
dependencies: ['setup'],
},
{
name: 'mobile-safari',
use: { ...devices['iPhone 13'] },
dependencies: ['setup'],
},
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});Accessibility Testing Integration
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('homepage has no accessibility violations', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
.analyze();
expect(results.violations).toEqual([]);
});GitHub Actions CI
# .github/workflows/e2e.yml
name: E2E Tests
on: [push, pull_request]
jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 7Anti-Patterns to Avoid
- Arbitrary
page.waitForTimeout(ms)instead of proper locator waits - Tests that depend on execution order
- Hardcoded test data instead of factories/fixtures
- Testing implementation details (CSS classes, DOM structure)
- Missing cleanup in
afterEach/afterAll - Screenshots without
waitForLoadState('networkidle')
Reference Skills
test-specialist- Unit and integration testing patternsgeneric-react-code-reviewer- React component testingagent-accessibility-expert- Comprehensive accessibility testing
Your task
$ARGUMENTS
In this repository186 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- accessibility-a11ySkill
- agent-accessibility-expertSkill
- agent-android-developerSkill
- agent-api-designerSkill
- agent-api-integration-specialistSkill
- agent-audio-engineerSkill
- agent-auth-specialistSkill
- agent-blockchain-developerSkill
- agent-brand-strategistSkill
- agent-browser-extension-developerSkill
- agent-browserSkill
- agent-cli-developerSkill
- agent-cloud-architectSkill
- agent-code-explainerSkill
- agent-compliance-engineerSkill
- agent-cpp-expertSkill
- agent-database-expertSkill
- agent-data-engineerSkill
- agent-desktop-developerSkill
- agent-desktopSkill
- agent-devops-engineerSkill
- agent-devsecops-engineerSkill
- agent-django-fastapi-expertSkill
- agent-documentation-writerSkill
- agent-email-specialistSkill
- agent-embedded-developerSkill
- agent-event-driven-architectSkill
- agent-flutter-developerSkill
- agent-game-developerSkill
- agent-go-expertSkill
- agent-graphic-designerSkill
- agent-graphql-architectSkill
- agent-i18n-specialistSkill
- agent-ios-developerSkill
- agent-kotlin-expertSkill
- agent-macos-developerSkill
- agent-microservices-architectSkill
- agent-migration-specialistSkill
- agent-ml-engineerSkill
- agent-mobile-architectSkill
- agent-mobile-release-managerSkill
- agent-monetization-expertSkill
- agent-motion-designerSkill
- agent-next-js-expertSkill
- agent-observability-engineerSkill
- agent-payment-billing-specialistSkill
- agent-product-analytics-specialistSkill
- agent-product-managerSkill
- agent-pwa-specialistSkill
- agent-python-expertSkill
- agent-realtime-specialistSkill
- agent-rust-expertSkill
- agent-seo-specialistSkill
- agent-serverless-specialistSkill
- agent-startup-advisorSkill
- agent-svelte-expertSkill
- agent-teamsSkill
- agent-technical-writerSkill
- agent-test-generatorSkill
- The first 60 shown.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

