Files
open-claw-team/.openclaw/workspace/skills/e2e-testing/SKILL.md
T

3.6 KiB

name, description
name description
e2e-testing End-to-end testing strategy using Playwright or similar frameworks. Covers critical user journeys, visual regression, and cross-browser testing. Use when setting up E2E tests, writing E2E suites, or troubleshooting E2E failures.

E2E Testing Skill

Based on ECC e2e-testing and Playwright patterns.

When to Use

  • Setting up E2E tests for a project
  • Writing E2E test scenarios
  • Fixing flaky E2E tests
  • User requests E2E test implementation

Strategy

What to Test E2E

Test CRITICAL user journeys only. Not every function. E2E is slow and expensive.

Prioritize:

  1. User signup and login flow
  2. Core feature: the ONE thing this app is built for
  3. Payment / checkout (if applicable)
  4. Key data creation, reading, update, deletion
  5. Error flow: what happens when things break

What NOT to Test E2E

  • Individual functions (unit tests)
  • Internal API contracts (integration tests)
  • UI component rendering (component tests)
  • Edge cases that are hard to trigger (unit tests)

Setup Pattern

// tests/e2e/login.spec.ts
import { test, expect } from '@playwright/test';

test.describe('Authentication', () => {
  test('user can login with valid credentials', async ({ page }) => {
    await page.goto('/login');
    await page.fill('[name="email"]', 'user@example.com');
    await page.fill('[name="password"]', 'correct-password');
    await page.click('button[type="submit"]');
    await expect(page).toHaveURL('/dashboard');
  });

  test('login fails with wrong password', async ({ page }) => {
    await page.goto('/login');
    await page.fill('[name="email"]', 'user@example.com');
    await page.fill('[name="password"]', 'wrong');
    await page.click('button[type="submit"]');
    await expect(page.locator('[data-testid="error"]')).toBeVisible();
  });
});

Page Object Model (For Complex Apps)

// tests/e2e/pages/LoginPage.ts
export class LoginPage {
  constructor(private page: Page) {}

  async goto() { await this.page.goto('/login'); }
  async fillEmail(email: string) { await this.page.fill('[name="email"]', email); }
  async fillPassword(password: string) { await this.page.fill('[name="password"]', password); }
  async submit() { await this.page.click('button[type="submit"]'); }
  async getError() { return this.page.locator('[data-testid="error"]'); }
}

Best Practices

  1. Independent tests: Each test starts fresh (no shared state)
  2. Deterministic: No flaky timing; use proper waits, not sleep
  3. Readable: Test names describe behavior, not implementation
  4. Fast: Parallelize when possible; limit browser contexts
  5. Data isolation: Use test fixtures, not production data
  6. Screenshots on failure: Configure in playwright.config.ts
// playwright.config.ts
export default defineConfig({
  use: {
    screenshot: 'only-on-failure',
    trace: 'retain-on-failure',
  },
  fullyParallel: true,
  retries: process.env.CI ? 2 : 0,
});

Common Anti-Patterns

  • Testing every possible input combination (do unit tests instead)
  • Hard-coded sleep/waitFixed (use proper waits on DOM conditions)
  • Shared test data between tests (creates flakiness)
  • Testing implementation details instead of user behavior
  • Too many E2E tests (aim for 20-30 covering critical flows max)

Report Format

## E2E Test Report
Tests: X pass / Y fail / Z skipped
Critical flows covered: [list]
Flaky tests detected: [list or "none"]
Screenshots saved: [path or "none"]
Browser: Chromium/Firefox/WebKit (version)

Status: All critical flows verified / Issues found: [list]