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
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 neededWrite 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 testsOptimize 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 analysisQuiz
1. What is E2E testing?
2. What is the Page Object Model?
3. What is the primary purpose of End-to-End Testing?
4. What is a common mistake when implementing End-to-End Testing?
Flashcards
Question
What is E2E testing?
Click to reveal answer
Answer
Testing complete user journeys in real browsers against a running application.
Question
What is the Page Object Model?
Click to reveal answer
Answer
A pattern that encapsulates page interactions to improve test maintainability.
Question
Why use fixtures?
Click to reveal answer
Answer
To provide consistent, reusable test data for reliable testing.
Question
What is network interception?
Click to reveal answer
Answer
Mocking API responses to control test data and isolate tests.
Question
What is End-to-End Testing?
Click to reveal answer
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