Skip to content
intermediatePhase 40 · Testing

End-to-End Testing

Write E2E tests with Playwright or Cypress for full user flows.

1h
0 problems
Topic Progress0%

E2E Frameworks

E2E Frameworks

Test complete user journeys in real browsers.

Cypress vs Playwright

Feature Cypress Playwright
Speed Fast Faster
Browser Chrome, Firefox, Edge Chrome, Firefox, Safari, Edge
Auto-wait Yes Yes
Debugging Time-travel Trace viewer
Free Yes (limits) Yes

Cypress Setup

npm install -D cypress
npx cypress open
// cypress/e2e/login.cy.js
describe('Login', () => {
  it('should login successfully', () => {
    cy.visit('/login');
    cy.get('[data-testid="email"]').type('user@example.com');
    cy.get('[data-testid="password"]').type('password123');
    cy.get('[data-testid="submit"]').click();
    cy.url().should('include', '/dashboard');
    cy.contains('Welcome, John!').should('be.visible');
  });
});

Playwright Setup

npm init playwright@latest
// tests/login.spec.js
const { test, expect } = require('@playwright/test');

test('should login successfully', 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, John!')).toBeVisible();
});

Writing E2E Tests

Writing E2E Tests

Page Object Model

// cypress/support/pages/LoginPage.js
class LoginPage {
  visit() {
    cy.visit('/login');
    return this;
  }

  fillEmail(email) {
    cy.get('[data-testid="email"]').type(email);
    return this;
  }

  fillPassword(password) {
    cy.get('[data-testid="password"]').type(password);
    return this;
  }

  submit() {
    cy.get('[data-testid="submit"]').click();
    return this;
  }

  login(email, password) {
    return this
      .fillEmail(email)
      .fillPassword(password)
      .submit();
  }
}

export default new LoginPage();

Test Example

describe('Shopping Flow', () => {
  beforeEach(() => {
    cy.intercept('GET', '/api/products', { fixture: 'products.json' });
    cy.visit('/');
  });

  it('should complete purchase', () => {
    // Browse products
    cy.contains('Products').click();
    cy.get('[data-testid="product-card"]').should('have.length.greaterThan', 0);

    // Add to cart
    cy.get('[data-testid="product-card"]').first().click();
    cy.get('[data-testid="add-to-cart"]').click();

    // Verify cart
    cy.get('[data-testid="cart-count"]').should('contain', '1');

    // Checkout
    cy.get('[data-testid="checkout"]').click();
    cy.get('[data-testid="shipping-name"]').type('John Doe');
    cy.get('[data-testid="shipping-address"]').type('123 Main St');
    cy.get('[data-testid="continue-to-payment"]').click();

    // Payment
    cy.get('[data-testid="card-number"]').type('4242424242424242');
    cy.get('[data-testid="card-expiry"]').type('12/25');
    cy.get('[data-testid="card-cvc"]').type('123');
    cy.get('[data-testid="place-order"]').click();

    // Confirmation
    cy.contains('Order Confirmed').should('be.visible');
  });
});

Network Interception

// Mock API responses
cy.intercept('GET', '/api/user', {
  id: 1,
  name: 'John',
  email: 'john@example.com',
}).as('getUser');

cy.visit('/profile');
cy.wait('@getUser');

// Verify request
cy.get('@getUser').its('request.url').should('contain', '/api/user');

Test Data Management

Test Data Management

Fixtures

// cypress/fixtures/products.json
[
  {
    "id": 1,
    "name": "Laptop",
    "price": 999.99,
    "inStock": true
  },
  {
    "id": 2,
    "name": "Phone",
    "price": 699.99,
    "inStock": true
  }
]

// Use in test
cy.intercept('GET', '/api/products', { fixture: 'products.json' });

API Seeding

// Before test
cy.request('POST', '/api/seed', {
  users: [
    { email: 'test@example.com', password: 'password123' }
  ],
  products: [
    { name: 'Test Product', price: 29.99 }
  ]
});

// After test
cy.request('POST', '/api/seed/reset');

Environment Variables

// cypress.config.js
module.exports = defineConfig({
  e2e: {
    baseUrl: process.env.CYPRESS_BASE_URL || 'http://localhost:3000',
    env: {
      apiUrl: process.env.API_URL || 'http://localhost:3001',
    },
  },
});

// In test
cy.visit(Cypress.env('loginUrl'));

Test Data Factories

// cypress/support/factories.js
export const createProduct = (overrides = {}) => ({
  id: Math.random().toString(36).substr(2, 9),
  name: 'Test Product',
  price: 29.99,
  description: 'Test description',
  ...overrides,
});

export const createUser = (overrides = {}) => ({
  id: Math.random().toString(36).substr(2, 9),
  email: `test-${Date.now()}@example.com`,
  name: 'Test User',
  ...overrides,
});

// In test
const product = createProduct({ name: 'Custom Product', price: 49.99 });
cy.intercept('POST', '/api/products', { body: product });

Practice Problems

0/3solved
Build End-to-End Testing Component

Create a reusable React component implementing End-to-End Testing. Include proper state management and accessibility.

Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where needed
End-to-End Testing Testing

Write unit and integration tests for End-to-End Testing using React Testing Library.

Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility tests
End-to-End Testing Performance

Optimize End-to-End Testing for performance. Consider memoization, code splitting, and bundle size.

Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysis

Quiz

1. What is E2E testing?

Question 1 options

2. What is the Page Object Model?

Question 2 options

3. What is the primary purpose of End-to-End Testing?

Question 3 options

4. What is a common mistake when implementing End-to-End Testing?

Question 4 options

Flashcards

Question

What is E2E testing?

Answer

Testing complete user journeys in real browsers against a running application.

Question

What is the Page Object Model?

Answer

A pattern that encapsulates page interactions to improve test maintainability.

Question

Why use fixtures?

Answer

To provide consistent, reusable test data for reliable testing.

Question

What is network interception?

Answer

Mocking API responses to control test data and isolate tests.

Question

What is End-to-End Testing?

Answer

End-to-End Testing is a key concept in frontend development.

Revision Notes

Key Takeaways

  • 1.E2E tests verify complete user journeys in real browsers
  • 2.Page Object Model improves test maintainability
  • 3.Fixtures provide consistent test data
  • 4.Network interception mocks API responses
  • 5.Focus on critical user journeys

Interview Tips

  • Explain E2E vs integration vs unit tests
  • Discuss the Page Object Model pattern
  • Know how to manage test data

Cheat Sheet

E2E Testing Cheat Sheet

Frameworks

  • Cypress: Fast, time-travel debugging
  • Playwright: Multi-browser, faster

Patterns

  • Page Object Model
  • Fixtures for test data
  • Network interception

Setup

cy.intercept('GET', '/api/data', { fixture: 'data.json' });
cy.visit('/page');

Best Practices

  • Test critical user journeys
  • Use data-testid selectors
  • Mock API responses
  • Clean up test data