Building Your Learning Module...
Getting things ready for you!
Find videos you like?
Save to resource drawer for future reference!
E2E testing simulates real user scenarios from start to finish. Tests run in a real browser, clicking buttons, filling forms, and verifying the entire application works together.
Tests from user's perspective
Chrome, Firefox, Safari
Frontend + Backend + Database
Slow • Few • Expensive
Medium • Some • Moderate
Fast • Many • Cheap
import { test, expect } from '@playwright/test';
test('user can login', async ({ page }) => {
// Navigate to page
await page.goto('https://example.com/login');
// Fill login form
await page.fill('input[name="email"]', 'test@example.com');
await page.fill('input[name="password"]', 'password123');
// Click login button
await page.click('button[type="submit"]');
// Wait for navigation
await page.waitForURL('**/dashboard');
// Assert user is logged in
await expect(page.locator('h1')).toHaveText('Welcome back!');
await expect(page.locator('.user-menu')).toBeVisible();
});
test('shows error for invalid credentials', async ({ page }) => {
await page.goto('https://example.com/login');
await page.fill('input[name="email"]', 'wrong@example.com');
await page.fill('input[name="password"]', 'wrongpass');
await page.click('button[type="submit"]');
// Error message should appear
await expect(page.locator('.error-message')).toHaveText(
'Invalid email or password'
);
// Should stay on login page
await expect(page).toHaveURL(/.*login/);
});describe('Shopping Cart', () => {
beforeEach(() => {
cy.visit('https://shop.example.com');
});
it('can add items to cart', () => {
// Search for product
cy.get('input[type="search"]').type('laptop');
cy.get('button[type="submit"]').click();
// Select first product
cy.get('.product-card').first().click();
// Add to cart
cy.get('button').contains('Add to Cart').click();
// Verify cart badge updated
cy.get('.cart-badge').should('have.text', '1');
// Go to cart
cy.get('.cart-icon').click();
// Verify item in cart
cy.get('.cart-item')
.should('have.length', 1)
.should('contain', 'laptop');
});
it('can checkout', () => {
// Add item (using custom command)
cy.addToCart('laptop');
// Go to checkout
cy.get('.checkout-button').click();
// Fill shipping info
cy.get('input[name="address"]').type('123 Main St');
cy.get('input[name="city"]').type('New York');
cy.get('select[name="country"]').select('USA');
// Fill payment
cy.get('input[name="cardNumber"]').type('4111111111111111');
cy.get('input[name="expiry"]').type('12/25');
cy.get('input[name="cvv"]').type('123');
// Submit order
cy.get('button').contains('Place Order').click();
// Verify success
cy.url().should('include', '/order-confirmation');
cy.get('.success-message')
.should('contain', 'Order placed successfully');
});
});import { test, expect } from '@playwright/test';
test('contact form validation', async ({ page }) => {
await page.goto('https://example.com/contact');
// Submit empty form
await page.click('button[type="submit"]');
// Check validation errors
await expect(page.locator('#name-error'))
.toHaveText('Name is required');
await expect(page.locator('#email-error'))
.toHaveText('Email is required');
await expect(page.locator('#message-error'))
.toHaveText('Message is required');
// Fill name only
await page.fill('#name', 'John Doe');
await page.click('button[type="submit"]');
// Name error should disappear
await expect(page.locator('#name-error')).not.toBeVisible();
// Other errors still visible
await expect(page.locator('#email-error')).toBeVisible();
// Fill invalid email
await page.fill('#email', 'invalid-email');
await page.click('button[type="submit"]');
await expect(page.locator('#email-error'))
.toHaveText('Please enter a valid email');
// Fill valid email and message
await page.fill('#email', 'john@example.com');
await page.fill('#message', 'Hello, this is a test message');
await page.click('button[type="submit"]');
// Success message
await expect(page.locator('.success-banner'))
.toHaveText('Message sent successfully!');
});Encapsulate page interactions in reusable classes
Create reusable test helpers (login, addToCart, etc.)
Setup test data before tests run
Take screenshots and compare with baselines
// pages/LoginPage.js
class LoginPage {
constructor(page) {
this.page = page;
this.emailInput = page.locator('input[name="email"]');
this.passwordInput = page.locator('input[name="password"]');
this.submitButton = page.locator('button[type="submit"]');
this.errorMessage = page.locator('.error-message');
}
async goto() {
await this.page.goto('https://example.com/login');
}
async login(email, password) {
await this.emailInput.fill(email);
await this.passwordInput.fill(password);
await this.submitButton.click();
}
async getErrorMessage() {
return await this.errorMessage.textContent();
}
}
// test.spec.js
import { test, expect } from '@playwright/test';
import { LoginPage } from './pages/LoginPage';
test('user can login with valid credentials', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('test@example.com', 'password123');
await expect(page).toHaveURL('**/dashboard');
});
test('shows error for invalid credentials', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('wrong@example.com', 'wrong');
const error = await loginPage.getErrorMessage();
expect(error).toBe('Invalid credentials');
});// cypress/support/commands.js
// Custom login command
Cypress.Commands.add('login', (email, password) => {
cy.visit('/login');
cy.get('input[name="email"]').type(email);
cy.get('input[name="password"]').type(password);
cy.get('button[type="submit"]').click();
cy.url().should('include', '/dashboard');
});
// Custom command to add item to cart
Cypress.Commands.add('addToCart', (productName) => {
cy.get('input[type="search"]').type(productName);
cy.get('.product-card')
.contains(productName)
.parent()
.find('button')
.contains('Add to Cart')
.click();
});
// Custom command to clear cart
Cypress.Commands.add('clearCart', () => {
cy.get('.cart-icon').click();
cy.get('.clear-cart-button').click();
cy.get('.confirm-button').click();
});
// Using custom commands in tests
describe('Shopping', () => {
beforeEach(() => {
cy.login('test@example.com', 'password');
});
it('can add and remove items', () => {
cy.addToCart('laptop');
cy.get('.cart-badge').should('have.text', '1');
cy.clearCart();
cy.get('.cart-badge').should('not.exist');
});
});Tests run in actual browsers
Simulates real user behavior
Login, checkout, signup
Important user journeys
Organize tests better
Reusable, maintainable code
70% unit, 20% integration, 10% E2E
Testing pyramid approach