
E2e Testing
Stand up maintainable Playwright E2E suites with Page Object Model, fixtures, and CI-friendly stability patterns.
Overview
E2E Testing is an agent skill most often used in Ship (also Build frontend) that applies Playwright Page Object Model, fixtures, and CI patterns for stable end-to-end suites.
Install
npx skills add https://github.com/affaan-m/everything-claude-code --skill e2e-testingWhat is this skill?
- Documents tests/e2e layout with auth, features, and api spec folders plus shared fixtures
- Page Object Model pattern with locators, navigation, search, and networkidle wait strategies
- Covers Playwright configuration, CI/CD integration, artifact management, and flaky test mitigation
- Uses data-testid-oriented selectors and API response waits for stable UI flows
- Recommended tree: tests/e2e with auth, features, and api areas plus fixtures/ and playwright.config.ts
Adoption & trust: 5.3k installs on skills.sh; 210k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your UI regressions slip through because Playwright tests are flaky, disorganized, and painful to update after each feature change.
Who is it for?
Solo builders shipping web apps who want opinionated Playwright architecture before scaling test count in CI.
Skip if: Unit-only workflows or teams standardized on Cypress/Selenium who will not adopt Playwright conventions from this skill.
When should I use this skill?
When adding or refactoring Playwright E2E coverage, Page Object Model structure, or CI integration for browser tests.
What do I get? / Deliverables
You structure Playwright projects with POM, shared fixtures, and CI artifacts so E2E coverage stays fast and maintainable before release.
- Organized e2e spec files and fixture modules
- Page Object classes and playwright.config.ts aligned to CI
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
End-to-end verification is the canonical Ship testing shelf before launch confidence. Playwright specs, POM, and flaky-test tactics are explicitly testing discipline, not production monitoring.
Where it fits
Scaffold login and browse specs with POM while implementing new UI flows.
Wire Playwright into CI with artifacts before tagging a release.
Stabilize waits and networkidle usage when tests slow down the pipeline.
How it compares
Procedural Playwright playbook skill, not a hosted test runner or MCP browser server.
Common Questions / FAQ
Who is e2e-testing for?
Indie developers and small teams building web products who own Playwright E2E suites in TypeScript or JavaScript repos.
When should I use e2e-testing?
During Ship testing when adding CI gates, during Build frontend when scaffolding specs for new flows, and when refactoring flaky suites before launch.
Is e2e-testing safe to install?
The skill is documentation and patterns only; review the Security Audits panel on this page and treat any generated test scripts like normal project code.
SKILL.md
READMESKILL.md - E2e Testing
# E2E Testing Patterns Comprehensive Playwright patterns for building stable, fast, and maintainable E2E test suites. ## Test File Organization ``` tests/ ├── e2e/ │ ├── auth/ │ │ ├── login.spec.ts │ │ ├── logout.spec.ts │ │ └── register.spec.ts │ ├── features/ │ │ ├── browse.spec.ts │ │ ├── search.spec.ts │ │ └── create.spec.ts │ └── api/ │ └── endpoints.spec.ts ├── fixtures/ │ ├── auth.ts │ └── data.ts └── playwright.config.ts ``` ## Page Object Model (POM) ```typescript import { Page, Locator } from '@playwright/test' export class ItemsPage { readonly page: Page readonly searchInput: Locator readonly itemCards: Locator readonly createButton: Locator constructor(page: Page) { this.page = page this.searchInput = page.locator('[data-testid="search-input"]') this.itemCards = page.locator('[data-testid="item-card"]') this.createButton = page.locator('[data-testid="create-btn"]') } async goto() { await this.page.goto('/items') await this.page.waitForLoadState('networkidle') } async search(query: string) { await this.searchInput.fill(query) await this.page.waitForResponse(resp => resp.url().includes('/api/search')) await this.page.waitForLoadState('networkidle') } async getItemCount() { return await this.itemCards.count() } } ``` ## Test Structure ```typescript import { test, expect } from '@playwright/test' import { ItemsPage } from '../../pages/ItemsPage' test.describe('Item Search', () => { let itemsPage: ItemsPage test.beforeEach(async ({ page }) => { itemsPage = new ItemsPage(page) await itemsPage.goto() }) test('should search by keyword', async ({ page }) => { await itemsPage.search('test') const count = await itemsPage.getItemCount() expect(count).toBeGreaterThan(0) await expect(itemsPage.itemCards.first()).toContainText(/test/i) await page.screenshot({ path: 'artifacts/search-results.png' }) }) test('should handle no results', async ({ page }) => { await itemsPage.search('xyznonexistent123') await expect(page.locator('[data-testid="no-results"]')).toBeVisible() expect(await itemsPage.getItemCount()).toBe(0) }) }) ``` ## Playwright Configuration ```typescript import { defineConfig, devices } from '@playwright/test' export default defineConfig({ testDir: './tests/e2e', fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 1 : undefined, reporter: [ ['html', { outputFolder: 'playwright-report' }], ['junit', { outputFile: 'playwright-results.xml' }], ['json', { outputFile: 'playwright-results.json' }] ], use: { baseURL: process.env.BASE_URL || 'http://localhost:3000', trace: 'on-first-retry', screenshot: 'only-on-failure', video: 'retain-on-failure', actionTimeout: 10000, navigationTimeout: 30000, }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, { name: 'firefox', use: { ...devices['Desktop Firefox'] } }, { name: 'webkit', use: { ...devices['Desktop Safari'] } }, { name: 'mobile-chrome', use: { ...devices['Pixel 5'] } }, ], webServer: { command: 'npm run dev', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI, timeout: 120000, }, }) ``` ## Flaky Test Patterns ### Quarantine ```typescript test('flaky: complex search', async ({ page }) => { test.fixme(true, 'Flaky - Issue #123') // test code... }) test('conditional skip', async ({ page }) => { test.skip(process.env.CI, 'Flaky in CI - Issue #123') // test code... }) ``` ### Identify Flakiness ```bash npx playwright test tests/search.spec.ts --repeat-each=10 npx playwright test tests/search.spec.ts --retries=3 ``` ##