e2e-testing

安装量: 78
排名: #12320

安装

npx skills add https://github.com/affaan-m/everything-claude-code --skill 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) 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 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 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 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 npx playwright test tests/search.spec.ts --repeat-each = 10 npx playwright test tests/search.spec.ts --retries = 3 Common Causes & Fixes Race conditions: // Bad: assumes element is ready await page . click ( '[data-testid="button"]' ) // Good: auto-wait locator await page . locator ( '[data-testid="button"]' ) . click ( ) Network timing: // Bad: arbitrary timeout await page . waitForTimeout ( 5000 ) // Good: wait for specific condition await page . waitForResponse ( resp => resp . url ( ) . includes ( '/api/data' ) ) Animation timing: // Bad: click during animation await page . click ( '[data-testid="menu-item"]' ) // Good: wait for stability await page . locator ( '[data-testid="menu-item"]' ) . waitFor ( { state : 'visible' } ) await page . waitForLoadState ( 'networkidle' ) await page . locator ( '[data-testid="menu-item"]' ) . click ( ) Artifact Management Screenshots 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 await browser . startTracing ( page , { path : 'artifacts/trace.json' , screenshots : true , snapshots : true , } ) // ... test actions ... await browser . stopTracing ( ) Video // In playwright.config.ts use : { video : 'retain-on-failure' , videosPath : 'artifacts/videos/' } CI/CD Integration

.github/workflows/e2e.yml

name : E2E Tests on : [ push , pull_request ] jobs : test : 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 env : BASE_URL : $ { { vars.STAGING_URL } } - uses : actions/upload - artifact@v4 if : always() with : name : playwright - report path : playwright - report/ retention-days : 30 Test Report Template

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 test ( 'wallet connection' , async ( { page , context } ) => { // Mock wallet provider await 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 test ( 'trade execution' , async ( { page } ) => { // Skip on production — real money test . 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 preview const preview = page . locator ( '[data-testid="trade-preview"]' ) await expect ( preview ) . toContainText ( '1.0' ) // Confirm and wait for blockchain await 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 ( ) } )

返回排行榜