Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.

Implement end-to-end testing

Use E2E testing frameworks like Playwright or Cypress to test critical user journeys.

Utilities
Quick take
Typical fix time 30 min
  • Test critical user journeys: authentication, checkout, core business flows
  • Use data-testid attributes for stable, maintainable selectors
  • Implement Page Object Model for reusable test components
  • Run E2E tests in CI/CD with artifact uploads on failure
Why it matters: E2E tests catch integration issues that unit tests miss—verifying your application works correctly from the user's perspective across all components.

Rule Details

End-to-end tests verify that your application works correctly from the user's perspective, testing complete user journeys across multiple components and services.

Code Example

import { test, expect } from '@playwright/test'
 
test.describe('User Authentication', () => {
  test('should allow user to login', async ({ page }) => {
    await page.goto('/login')
 
    await page.fill('[data-testid="email"]', 'user@example.com')
    await page.fill('[data-testid="password"]', 'password123')
    await page.click('[data-testid="submit"]')
 
    await expect(page).toHaveURL('/dashboard')
    await expect(page.getByText('Welcome back')).toBeVisible()
  })
 
  test('should show error for invalid credentials', async ({ page }) => {
    await page.goto('/login')
 
    await page.fill('[data-testid="email"]', 'wrong@example.com')
    await page.fill('[data-testid="password"]', 'wrongpassword')
    await page.click('[data-testid="submit"]')
 
    await expect(page.getByText('Invalid credentials')).toBeVisible()
    await expect(page).toHaveURL('/login')
  })
})

Why It Matters

E2E tests catch integration issues that unit tests miss—verifying your application works correctly from the user's perspective across all components.

Critical Journeys to Test

  • User authentication (login, logout, registration)
  • Core business flows (checkout, booking, submission)
  • Navigation and routing
  • Form submissions with validation
  • Error states and recovery

Cypress Example

describe('Checkout Flow', () => {
  beforeEach(() => {
    cy.login('user@example.com', 'password')
    cy.visit('/products')
  })
 
  it('should complete checkout', () => {
    // Add product to cart
    cy.get('[data-testid="product-1"]').click()
    cy.get('[data-testid="add-to-cart"]').click()
 
    // Go to checkout
    cy.get('[data-testid="cart-icon"]').click()
    cy.get('[data-testid="checkout"]').click()
 
    // Fill shipping info
    cy.get('#address').type('123 Main St')
    cy.get('#city').type('New York')
    cy.get('#zip').type('10001')
 
    // Complete order
    cy.get('[data-testid="place-order"]').click()
 
    // Verify success
    cy.url().should('include', '/order-confirmation')
    cy.contains('Thank you for your order').should('be.visible')
  })
})

Best Practices

Use Data Attributes for Selectors

<!-- ✅ Good: Stable selectors -->
<button data-testid="submit-form">Submit</button>
 
<!-- ❌ Bad: Brittle selectors -->
<button class="btn btn-primary mt-4">Submit</button>

Page Object Model

// pages/LoginPage.ts
export class LoginPage {
  constructor(private page: Page) {}
 
  async navigate() {
    await this.page.goto('/login')
  }
 
  async login(email: string, password: string) {
    await this.page.fill('[data-testid="email"]', email)
    await this.page.fill('[data-testid="password"]', password)
    await this.page.click('[data-testid="submit"]')
  }
 
  async expectError(message: string) {
    await expect(this.page.getByText(message)).toBeVisible()
  }
}
 
// tests/login.spec.ts
test('should login successfully', async ({ page }) => {
  const loginPage = new LoginPage(page)
  await loginPage.navigate()
  await loginPage.login('user@example.com', 'password')
  await expect(page).toHaveURL('/dashboard')
})

Test Isolation

// Reset state before each test
test.beforeEach(async ({ page }) => {
  await page.request.post('/api/test/reset')
})
 
// Use test fixtures for consistent data
test.use({
  storageState: 'playwright/.auth/user.json'
})

CI/CD Configuration

# GitHub Actions
name: E2E Tests
on: [push, pull_request]
 
jobs:
  e2e:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
      - run: npm ci
      - run: npx playwright install --with-deps
 
      - name: Run E2E tests
        run: npx playwright test
 
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: playwright-report
          path: playwright-report/

Standards

  • Use these references as the standard for how the test or monitoring strategy should behave in the shipped workflow.
  • Check the implementation against Playwright Docs before treating the rule as satisfied.
  • Check the implementation against Testing Library Guiding Principles before treating the rule as satisfied.

Verification

  1. Run the relevant test or CI step locally and confirm it fails when the rule is violated.
  2. Ensure the automation blocks regressions instead of only printing warnings.
  3. Cover at least one representative high-risk flow, component, or route.
  4. Keep thresholds or assertions in version control so changes remain reviewable.

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Review this project for E2E test coverage of critical user journeys like authentication, checkout, and form submissions.

Fix

Auto-fix issues

Add E2E tests for critical user journeys using Playwright or Cypress.

Explain

Learn more

Explain E2E testing best practices including test organization, selectors, and CI/CD integration.

Review

Code review

Review tests, CI workflows, and enforcement points related to Implement end-to-end testing. Flag exact gaps where the rule is not automatically verified or where failures do not block regressions.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

Playwright
playwright.devTool

Rules that often go hand-in-hand with this one.

Include accessibility testing

Automate accessibility testing with tools like axe-core, jest-axe, or Playwright's accessibility testing.

Testing
Test across all major browsers

Website works correctly across major browsers (Chrome, Firefox, Safari, Edge).

Testing
Use mutation testing to measure how well tests detect bugs

Run Stryker mutation testing on critical business logic to verify that your test suite will actually catch real bugs, not just achieve line coverage.

Testing

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385