home / skills / eyadsibai / ltk / e2e-testing

This skill helps you design and maintain robust end-to-end tests with Playwright or Cypress, improving reliability and debugging flaky journeys.

npx playbooks add skill eyadsibai/ltk --skill e2e-testing

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
3.8 KB
---
name: e2e-testing
description: Use when implementing end-to-end tests, using Playwright or Cypress, testing user journeys, debugging flaky tests, or asking about "E2E testing", "Playwright", "Cypress", "browser testing", "visual regression", "test automation"
version: 1.0.0
---

# E2E Testing Patterns

Build reliable, fast, and maintainable end-to-end test suites with Playwright and Cypress.

## What to Test with E2E

**Good for:**

- Critical user journeys (login, checkout, signup)
- Complex interactions (drag-and-drop, multi-step forms)
- Cross-browser compatibility
- Real API integration

**Not for:**

- Unit-level logic (use unit tests)
- API contracts (use integration tests)
- Edge cases (too slow)

## Playwright Configuration

```typescript
// playwright.config.ts
export default defineConfig({
    testDir: './e2e',
    timeout: 30000,
    fullyParallel: true,
    retries: process.env.CI ? 2 : 0,
    use: {
        baseURL: 'http://localhost:3000',
        trace: 'on-first-retry',
        screenshot: 'only-on-failure',
    },
    projects: [
        { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
        { name: 'mobile', use: { ...devices['iPhone 13'] } },
    ],
});
```

## Page Object Model

```typescript
export class LoginPage {
    readonly page: Page;
    readonly emailInput: Locator;
    readonly loginButton: Locator;

    constructor(page: Page) {
        this.page = page;
        this.emailInput = page.getByLabel('Email');
        this.loginButton = page.getByRole('button', { name: 'Login' });
    }

    async login(email: string, password: string) {
        await this.emailInput.fill(email);
        await this.page.getByLabel('Password').fill(password);
        await this.loginButton.click();
    }
}
```

## Waiting Strategies

```typescript
// Bad: Fixed timeouts
await page.waitForTimeout(3000);  // Flaky!

// Good: Wait for conditions
await expect(page.getByText('Welcome')).toBeVisible();
await page.waitForURL('/dashboard');

// Wait for API response
const responsePromise = page.waitForResponse(
    r => r.url().includes('/api/users') && r.status() === 200
);
await page.click('button');
await responsePromise;
```

## Network Mocking

```typescript
test('displays error when API fails', async ({ page }) => {
    await page.route('**/api/users', route => {
        route.fulfill({
            status: 500,
            body: JSON.stringify({ error: 'Server Error' }),
        });
    });

    await page.goto('/users');
    await expect(page.getByText('Failed to load')).toBeVisible();
});
```

## Visual Regression

```typescript
test('homepage looks correct', async ({ page }) => {
    await page.goto('/');
    await expect(page).toHaveScreenshot('homepage.png', {
        fullPage: true,
        maxDiffPixels: 100,
    });
});
```

## Accessibility Testing

```typescript
import AxeBuilder from '@axe-core/playwright';

test('no accessibility violations', async ({ page }) => {
    await page.goto('/');
    const results = await new AxeBuilder({ page }).analyze();
    expect(results.violations).toEqual([]);
});
```

## Best Practices

1. **Use Data Attributes**: `data-testid` for stable selectors
2. **Test User Behavior**: Click, type, see - not implementation
3. **Keep Tests Independent**: Each test runs in isolation
4. **Clean Up Test Data**: Create and destroy per test
5. **Use Page Objects**: Encapsulate page logic
6. **Optimize for Speed**: Mock when possible, parallel execution

## Bad vs Good Selectors

```typescript
// Bad
cy.get('.btn.btn-primary.submit-button').click();
cy.get('div > form > div:nth-child(2) > input').type('text');

// Good
cy.getByRole('button', { name: 'Submit' }).click();
cy.get('[data-testid="email-input"]').type('[email protected]');
```

## Debugging

```bash
# Headed mode
npx playwright test --headed

# Debug mode (step through)
npx playwright test --debug

# Trace viewer
npx playwright show-trace trace.zip
```

Overview

This skill helps teams design and maintain reliable end-to-end test suites using Playwright or Cypress. It focuses on testing critical user journeys, reducing flakiness, and improving test performance through practical patterns and configuration. The guidance covers selectors, page objects, waiting strategies, network mocking, visual regression, and accessibility checks.

How this skill works

I inspect your test goals and recommend where E2E fits versus unit or integration tests, then provide concrete examples and configuration snippets for Playwright and Cypress. Guidance includes creating stable selectors (data attributes/roles), encapsulating page logic with Page Objects, using conditional waits instead of fixed timeouts, and mocking network responses to speed and stabilize tests. I also explain running headed/debug modes, traces, and visual regression checks.

When to use it

  • Validating critical user journeys like login, signup, checkout, or complex workflows
  • Checking cross-browser or mobile behavior for real user interactions
  • Debugging flaky tests and improving stability of failing suites
  • Verifying UI regressions with screenshots or visual comparisons
  • Assessing accessibility of rendered pages as part of QA

Best practices

  • Prefer data-testid or ARIA roles for stable selectors instead of CSS classes or brittle DOM paths
  • Keep tests independent and idempotent; create and clean up test data per test
  • Avoid fixed timeouts; wait for element visibility, URL changes, or specific API responses
  • Encapsulate page interactions with Page Objects to reduce duplication and improve readability
  • Mock external services where appropriate to speed tests and isolate failures
  • Run tests in parallel and use retries/traces on CI to surface transient flakiness

Example use cases

  • Implement Playwright config with parallel projects for desktop and mobile and trace-on-retry for CI triage
  • Write a Page Object for login and reuse it across sign-in and session tests
  • Mock a failing API response to assert error handling and UI messaging
  • Capture full-page screenshots for visual regression and set a max-diff threshold
  • Run accessibility scans during E2E to fail on axe-core violations

FAQ

Should I test everything with E2E?

No. Reserve E2E for critical user journeys and complex interactions. Use unit tests for business logic and integration tests for API contracts.

How do I reduce flaky tests?

Replace fixed timeouts with explicit waits for visibility, URL changes, or network responses. Use stable selectors and isolate tests with clean test data and mocks.