--- description: Generate end-to-end browser tests for user workflows shortcut: e2e --- # End-to-End Test Generator Generate comprehensive browser-based E2E tests for complete user workflows using Playwright, Cypress, or Selenium. ## Purpose Create automated tests that simulate real user interactions: - Complete user journeys (signup → login → purchase) - Multi-page workflows - Form interactions and validations - Navigation flows - Browser-specific behaviors - Mobile responsive testing ## Supported Frameworks **Playwright** (Recommended) - Multi-browser support (Chromium, Firefox, WebKit) - Auto-wait for elements - Network interception - Mobile emulation - Parallel execution **Cypress** - Time-travel debugging - Real-time reloads - Automatic waiting - Network stubbing - Screenshot/video capture **Selenium WebDriver** - Mature ecosystem - Cross-browser support - Remote execution (Selenium Grid) - Mobile testing (Appium) ## Test Generation When invoked, generate E2E tests with: 1. **User workflow analysis** - Identify key user journeys - Map page interactions - Define success criteria 2. **Page Object Model** - Create page classes - Define selectors - Implement action methods 3. **Test scenarios** - Happy path workflows - Error handling paths - Edge cases 4. **Assertions** - URL validation - Element presence - Content verification - State changes ## Example: Playwright Test ```typescript import { test, expect } from '@playwright/test'; test.describe('User Registration Flow', () => { test('should complete full registration workflow', async ({ page }) => { // Navigate to signup await page.goto('https://example.com/signup'); // Fill registration form await page.fill('[name="email"]', '[email protected]'); await page.fill('[name="password"]', 'SecurePass123!'); await page.fill('[name="confirmPassword"]', 'SecurePass123!'); await page.check('[name="terms"]'); // Submit and wait for navigation await page.click('button[type="submit"]'); await page.waitForURL('**/verify-email'); // Verify success message await expect(page.locator('.success-message')).toContainText( 'Please check your email' ); }); test('should show validation errors for invalid data', async ({ page }) => { await page.goto('https://example.com/signup'); // Submit empty form await page.click('button[type="submit"]'); // Verify error messages await expect(page.locator('.error-email')).toBeVisible(); await expect(page.locator('.error-password')).toBeVisible(); }); }); test.describe('E-commerce Purchase Flow', () => { test.beforeEach(async ({ page }) => { // Login before each test await page.goto('https://example.com/login'); await page.fill('[name="email"]', '[email protected]'); await page.fill('[name="password"]', 'password123'); await page.click('button[type="submit"]'); await page.waitForURL('**/dashboard'); }); test('should complete product purchase', async ({ page }) => { // Browse products await page.goto('https://example.com/products'); await page.click('text=Add to Cart').first(); // Verify cart badge await expect(page.locator('.cart-badge')).toHaveText('1'); // Go to checkout await page.click('.cart-icon'); await page.click('text=Checkout'); // Fill shipping info await page.fill('[name="address"]', '123 Main St'); await page.fill('[name="city"]', 'San Francisco'); await page.fill('[name="zip"]', '94102'); await page.click('text=Continue'); // Enter payment (test mode) await page.fill('[name="cardNumber"]', '4242424242424242'); await page.fill('[name="expiry"]', '12/25'); await page.fill('[name="cvc"]', '123'); await page.click('text=Place Order'); // Verify success await page.waitForURL('**/order/confirmation'); await expect(page.locator('.order-success')).toBeVisible(); await expect(page.locator('.order-number')).toContainText('ORDER-'); }); }); ``` ## Example: Cypress Test ```javascript describe('User Dashboard Workflow', () => { beforeEach(() => { cy.login('[email protected]', 'password123'); }); it('should load dashboard with user data', () => { cy.visit('/dashboard'); cy.get('.welcome-message').should('contain', 'Welcome, John'); cy.get('.user-stats').should('be.visible'); }); it('should create new project', () => { cy.visit('/dashboard'); cy.get('[data-testid="new-project"]').click(); cy.get('[name="projectName"]').type('My New Project'); cy.get('[name="description"]').type('Project description here'); cy.get('button[type="submit"]').click(); cy.url().should('include', '/projects/'); cy.get('.project-title').should('contain', 'My New Project'); }); }); ``` ## Page Object Model ```typescript // pages/LoginPage.ts export class LoginPage { constructor(private page: Page) {} async goto() { await this.page.goto('/login'); } async login(email: string, password: string) { await this.page.fill('[name="email"]', email); await this.page.fill('[name="password"]', password); await this.page.click('button[type="submit"]'); } async getErrorMessage() { return this.page.locator('.error-message').textContent(); } } // Test using Page Object test('login with page object', async ({ page }) => { const loginPage = new LoginPage(page); await loginPage.goto(); await loginPage.login('[email protected]', 'password123'); await expect(page).toHaveURL('/dashboard'); }); ``` ## Best Practices - **Use data-testid attributes** - Stable selectors - **Implement Page Objects** - Reusable page interactions - **Handle waits properly** - Auto-wait or explicit waits - **Test realistic scenarios** - Actual user workflows - **Parallelize tests** - Faster execution - **Capture screenshots on failure** - Debugging aid - **Use fixtures** - Consistent test data - **Test across browsers** - Chrome, Firefox, Safari