<< All versions
Skill v1.0.1
currentAutomated scan96/100affaan-m/everything-claude-code/e2e-testing
4 files
──Details
PublishedMay 15, 2026 at 01:57 AM
Content Hashsha256:f84ae93c159bf6ec...
Git SHA375d750b4c14
Bump Typepatch
──Files
Files (1 file, 7.9 KB)
SKILL.md7.9 KBactive
SKILL.md · 328 lines · 7.9 KB
version: "1.0.1" name: e2e-testing description: Playwright E2E testing patterns, Page Object Model, configuration, CI/CD integration, artifact management, and flaky test strategies. origin: ECC
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: Pagereadonly searchInput: Locatorreadonly itemCards: Locatorreadonly createButton: Locatorconstructor(page: Page) {this.page = pagethis.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: ItemsPagetest.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=10npx playwright test tests/search.spec.ts --retries=3
Common Causes & Fixes
Race conditions:
typescript
// Bad: assumes element is readyawait page.click('[data-testid="button"]')// Good: auto-wait locatorawait page.locator('[data-testid="button"]').click()
Network timing:
typescript
// Bad: arbitrary timeoutawait page.waitForTimeout(5000)// Good: wait for specific conditionawait page.waitForResponse(resp => resp.url().includes('/api/data'))
Animation timing:
typescript
// Bad: click during animationawait page.click('[data-testid="menu-item"]')// Good: wait for stabilityawait page.locator('[data-testid="menu-item"]').waitFor({ state: 'visible' })await page.waitForLoadState('networkidle')await page.locator('[data-testid="menu-item"]').click()
Artifact Management
Screenshots
typescript
await page.screenshot({ path: 'artifacts/after-login.png' })await page.screenshot({ path: 'artifacts/full-page.png', fullPage: true })await page.locator('[data-testid="chart"]').screenshot({ path: 'artifacts/chart.png' })
Traces
typescript
await browser.startTracing(page, {path: 'artifacts/trace.json',screenshots: true,snapshots: true,})// ... test actions ...await browser.stopTracing()
Video
typescript
// In playwright.config.tsuse: {video: 'retain-on-failure',videosPath: 'artifacts/videos/'}
CI/CD Integration
yaml
# .github/workflows/e2e.ymlname: E2E Testson: [push, pull_request]jobs:test:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v4- uses: actions/setup-node@v4with:node-version: 20- run: npm ci- run: npx playwright install --with-deps- run: npx playwright testenv:BASE_URL: ${{ vars.STAGING_URL }}- uses: actions/upload-artifact@v4if: always()with:name: playwright-reportpath: playwright-report/retention-days: 30
Test Report Template
markdown
# E2E Test Report**Date:** YYYY-MM-DD HH:MM**Duration:** Xm Ys**Status:** PASSING / FAILING## Summary-Total: X | Passed: Y (Z%) | Failed: A | Flaky: B | Skipped: C## Failed Tests### test-name**File:** `tests/e2e/feature.spec.ts:45`**Error:** Expected element to be visible**Screenshot:** artifacts/failed.png**Recommended Fix:** [description]## Artifacts-HTML Report: playwright-report/index.html-Screenshots: artifacts/*.png-Videos: artifacts/videos/*.webm-Traces: artifacts/*.zip
Wallet / Web3 Testing
typescript
test('wallet connection', async ({ page, context }) => {// Mock wallet providerawait context.addInitScript(() => {window.ethereum = {isMetaMask: true,request: async ({ method }) => {if (method === 'eth_requestAccounts')return ['0x1234567890123456789012345678901234567890']if (method === 'eth_chainId') return '0x1'}}})await page.goto('/')await page.locator('[data-testid="connect-wallet"]').click()await expect(page.locator('[data-testid="wallet-address"]')).toContainText('0x1234')})
Financial / Critical Flow Testing
typescript
test('trade execution', async ({ page }) => {// Skip on production — real moneytest.skip(process.env.NODE_ENV === 'production', 'Skip on production')await page.goto('/markets/test-market')await page.locator('[data-testid="position-yes"]').click()await page.locator('[data-testid="trade-amount"]').fill('1.0')// Verify previewconst preview = page.locator('[data-testid="trade-preview"]')await expect(preview).toContainText('1.0')// Confirm and wait for blockchainawait page.locator('[data-testid="confirm-trade"]').click()await page.waitForResponse(resp => resp.url().includes('/api/trade') && resp.status() === 200,{ timeout: 30000 })await expect(page.locator('[data-testid="trade-success"]')).toBeVisible()})