+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 152 of 355

🎯 End-to-End Testing: Cypress with TypeScript

Master E2E testing with Cypress and TypeScript - learn to test complete user workflows, handle real browser interactions, and ensure your app works perfectly πŸš€

πŸš€Intermediate
35 min read

Prerequisites

  • Basic TypeScript knowledge πŸ“
  • Understanding of web applications ⚑
  • Familiarity with DOM interactions πŸ’»
  • Integration testing concepts πŸ”—

What you'll learn

  • Set up Cypress with TypeScript for E2E testing 🎯
  • Write comprehensive end-to-end test scenarios πŸ—οΈ
  • Handle complex user interactions and workflows πŸ”„
  • Test real browser behavior and edge cases 🌐
  • Debug and optimize E2E test performance πŸ›

🎯 Introduction

Welcome to the ultimate testing experience - End-to-End (E2E) testing with Cypress! πŸŽ‰ If integration tests are like testing your recipe by tasting each step, E2E tests are like inviting friends over for dinner and watching them enjoy the complete meal experience! 🍽️

Cypress is the rockstar of E2E testing frameworks, and when combined with TypeScript, it becomes a powerhouse that ensures your entire application works flawlessly from the user’s perspective. You’ll learn to simulate real user interactions, test complete workflows, and catch those sneaky bugs that only appear in production.

By the end of this tutorial, you’ll be confidently writing E2E tests that give you peace of mind about your application’s reliability! Let’s dive into this exciting journey! πŸŠβ€β™‚οΈ

πŸ“š Understanding End-to-End Testing

πŸ€” What is End-to-End Testing?

E2E testing is like being a secret shopper for your own app! πŸ•΅οΈβ€β™€οΈ It tests your application exactly as a real user would interact with it - clicking buttons, filling forms, navigating pages, and verifying the complete user experience.

E2E testing with Cypress means:

  • ✨ Testing in real browsers (Chrome, Firefox, Edge)
  • πŸš€ Simulating actual user interactions
  • πŸ›‘οΈ Verifying complete user workflows
  • πŸ“Š Testing across different screen sizes and devices

πŸ’‘ Why Cypress + TypeScript?

Here’s why this combination is absolutely magical:

  1. Type Safety πŸ”’: Catch errors before running tests
  2. IntelliSense πŸ’»: Amazing autocomplete and suggestions
  3. Refactoring Support πŸ”§: Rename safely across test files
  4. Better Debugging πŸ›: Clear error messages and stack traces
  5. Team Collaboration 🀝: Self-documenting test code

Real-world example: Your TypeScript E2E tests catch a breaking change when someone renames a data attribute that your tests depend on! πŸ›‘οΈ

πŸ”§ Cypress + TypeScript Setup

πŸ“ Installation and Configuration

Let’s get Cypress running with TypeScript:

# πŸ“¦ Install Cypress and TypeScript support
npm install --save-dev cypress typescript
npm install --save-dev @cypress/webpack-preprocessor ts-loader

# πŸš€ Initialize Cypress
npx cypress open

TypeScript Configuration:

// πŸ“„ cypress.config.ts
import { defineConfig } from 'cypress';

export default defineConfig({
  e2e: {
    // 🎯 Base URL for your application
    baseUrl: 'http://localhost:3000',
    
    // πŸ“ Test files location
    specPattern: 'cypress/e2e/**/*.cy.ts',
    
    // πŸ”§ Support files
    supportFile: 'cypress/support/e2e.ts',
    
    // πŸ“Š Viewport settings
    viewportWidth: 1280,
    viewportHeight: 720,
    
    // ⏱️ Timeouts
    defaultCommandTimeout: 10000,
    requestTimeout: 10000,
    responseTimeout: 10000,
    
    // πŸ“Ή Video and screenshots
    video: true,
    screenshotOnRunFailure: true,
    
    // 🎨 Test runner settings
    experimentalStudio: true,
    
    setupNodeEvents(on, config) {
      // πŸ”§ TypeScript preprocessing
      const webpack = require('@cypress/webpack-preprocessor');
      const options = {
        webpackOptions: {
          resolve: {
            extensions: ['.ts', '.tsx', '.js']
          },
          module: {
            rules: [
              {
                test: /\.tsx?$/,
                loader: 'ts-loader',
                options: { transpileOnly: true }
              }
            ]
          }
        }
      };
      
      on('file:preprocessor', webpack(options));
      
      return config;
    }
  }
});

TypeScript Support Files:

// πŸ“„ cypress/support/e2e.ts
import './commands';

// 🎯 Global type declarations
declare global {
  namespace Cypress {
    interface Chainable {
      // πŸ” Custom login command
      login(email: string, password: string): Chainable<void>;
      
      // πŸ›’ Custom shopping cart commands
      addToCart(productId: string): Chainable<void>;
      checkout(): Chainable<void>;
      
      // πŸ“ Form helpers
      fillContactForm(data: ContactFormData): Chainable<void>;
      
      // 🎯 Wait for API calls
      waitForApi(endpoint: string): Chainable<void>;
    }
  }
}

// 🌟 Custom types for test data
export interface ContactFormData {
  name: string;
  email: string;
  message: string;
  emoji?: string;
}

export interface UserTestData {
  id: string;
  email: string;
  password: string;
  name: string;
  role: 'user' | 'admin';
}

🎨 Custom Commands

Create reusable TypeScript commands:

// πŸ“„ cypress/support/commands.ts
import { ContactFormData, UserTestData } from './e2e';

// πŸ” Authentication commands
Cypress.Commands.add('login', (email: string, password: string) => {
  cy.log(`πŸ” Logging in as ${email}`);
  
  cy.visit('/login');
  cy.get('[data-cy=email-input]').type(email);
  cy.get('[data-cy=password-input]').type(password);
  cy.get('[data-cy=login-button]').click();
  
  // βœ… Verify successful login
  cy.url().should('not.include', '/login');
  cy.get('[data-cy=user-menu]').should('be.visible');
  
  cy.log('βœ… Login successful!');
});

// πŸ›’ E-commerce commands
Cypress.Commands.add('addToCart', (productId: string) => {
  cy.log(`πŸ›’ Adding product ${productId} to cart`);
  
  cy.get(`[data-cy=product-${productId}]`).within(() => {
    cy.get('[data-cy=add-to-cart-btn]').click();
  });
  
  // βœ… Verify product added
  cy.get('[data-cy=cart-count]').should('contain', '1');
  cy.get('.toast-success').should('contain', 'Added to cart! πŸŽ‰');
  
  cy.log('βœ… Product added to cart!');
});

// πŸ“ Form helpers
Cypress.Commands.add('fillContactForm', (data: ContactFormData) => {
  cy.log(`πŸ“ Filling contact form for ${data.name}`);
  
  cy.get('[data-cy=name-input]').type(data.name);
  cy.get('[data-cy=email-input]').type(data.email);
  cy.get('[data-cy=message-textarea]').type(data.message);
  
  if (data.emoji) {
    cy.get('[data-cy=emoji-selector]').select(data.emoji);
  }
  
  cy.log('βœ… Contact form filled!');
});

// 🌐 API waiting helpers
Cypress.Commands.add('waitForApi', (endpoint: string) => {
  cy.log(`⏳ Waiting for API call to ${endpoint}`);
  
  cy.intercept('GET', `**/api${endpoint}`).as('apiCall');
  cy.wait('@apiCall').then((interception) => {
    expect(interception.response?.statusCode).to.eq(200);
  });
  
  cy.log('βœ… API call completed!');
});

πŸ’‘ Practical E2E Test Examples

πŸ›’ Example 1: E-commerce User Journey

Let’s test a complete shopping experience:

// πŸ§ͺ cypress/e2e/ecommerce/shopping-flow.cy.ts
describe('πŸ›’ E-commerce Shopping Flow', () => {
  beforeEach(() => {
    // 🌱 Setup test data
    cy.task('seedDatabase');
    cy.visit('/');
  });
  
  describe('🎯 Happy Path Shopping', () => {
    it('should complete full shopping journey', () => {
      // 🏠 Start from homepage
      cy.log('🏠 Starting shopping journey from homepage');
      
      // πŸ” Search for product
      cy.get('[data-cy=search-input]').type('TypeScript Coffee Mug{enter}');
      cy.get('[data-cy=search-results]').should('be.visible');
      
      // πŸ“± Verify product appears
      cy.get('[data-cy=product-list]').within(() => {
        cy.contains('TypeScript Coffee Mug β˜•').should('be.visible');
        cy.get('[data-cy=product-price]').should('contain', '$19.99');
      });
      
      // πŸ‘† Click on product
      cy.get('[data-cy=product-typescript-mug]').click();
      
      // πŸ“‹ Verify product details page
      cy.url().should('include', '/products/typescript-mug');
      cy.get('[data-cy=product-title]').should('contain', 'TypeScript Coffee Mug');
      cy.get('[data-cy=product-description]').should('be.visible');
      cy.get('[data-cy=product-images]').should('be.visible');
      
      // πŸ›’ Add to cart
      cy.addToCart('typescript-mug');
      
      // πŸ“ Proceed to checkout
      cy.get('[data-cy=cart-icon]').click();
      cy.get('[data-cy=checkout-button]').click();
      
      // πŸ‘€ Login for checkout
      cy.login('[email protected]', 'SecurePass123!');
      
      // 🏠 Fill shipping information
      cy.log('🏠 Filling shipping information');
      cy.get('[data-cy=shipping-form]').within(() => {
        cy.get('[data-cy=address-input]').type('123 TypeScript Lane');
        cy.get('[data-cy=city-input]').type('Code City');
        cy.get('[data-cy=zip-input]').type('12345');
        cy.get('[data-cy=country-select]').select('United States πŸ‡ΊπŸ‡Έ');
      });
      
      // πŸ’³ Payment information
      cy.log('πŸ’³ Processing payment');
      cy.get('[data-cy=payment-form]').within(() => {
        cy.get('[data-cy=card-number]').type('4242424242424242');
        cy.get('[data-cy=card-expiry]').type('12/25');
        cy.get('[data-cy=card-cvc]').type('123');
        cy.get('[data-cy=card-name]').type('Happy Customer');
      });
      
      // πŸ“Š Review order
      cy.get('[data-cy=order-summary]').within(() => {
        cy.contains('TypeScript Coffee Mug').should('be.visible');
        cy.contains('$19.99').should('be.visible');
        cy.contains('Shipping: $4.99').should('be.visible');
        cy.contains('Total: $24.98').should('be.visible');
      });
      
      // 🎯 Place order
      cy.get('[data-cy=place-order-button]').click();
      
      // βœ… Verify success
      cy.url().should('include', '/order-confirmation');
      cy.get('[data-cy=success-message]').should('contain', 'Order placed successfully! πŸŽ‰');
      cy.get('[data-cy=order-number]').should('be.visible');
      
      // πŸ“§ Verify confirmation email notice
      cy.get('[data-cy=email-notice]').should('contain', 'confirmation email has been sent');
      
      cy.log('πŸŽ‰ Complete shopping journey successful!');
    });
    
    it('should handle out of stock products gracefully', () => {
      // πŸ” Search for out-of-stock product
      cy.visit('/products/out-of-stock-item');
      
      // ⚠️ Verify out of stock message
      cy.get('[data-cy=stock-status]').should('contain', 'Out of Stock');
      cy.get('[data-cy=add-to-cart-btn]').should('be.disabled');
      
      // πŸ“§ Enable notification option
      cy.get('[data-cy=notify-when-available]').should('be.visible');
      cy.get('[data-cy=email-notification-input]').type('[email protected]');
      cy.get('[data-cy=notify-button]').click();
      
      // βœ… Verify notification setup
      cy.get('.toast-success').should('contain', 'We\'ll notify you when available! πŸ“§');
      
      cy.log('βœ… Out of stock handling test passed!');
    });
  });
  
  describe('🚫 Error Scenarios', () => {
    it('should handle payment failures gracefully', () => {
      // πŸ›’ Add product and proceed to checkout
      cy.visit('/products/typescript-mug');
      cy.addToCart('typescript-mug');
      cy.get('[data-cy=checkout-button]').click();
      cy.login('[email protected]', 'SecurePass123!');
      
      // πŸ’³ Use invalid card number
      cy.get('[data-cy=payment-form]').within(() => {
        cy.get('[data-cy=card-number]').type('4000000000000002'); // Declined card
        cy.get('[data-cy=card-expiry]').type('12/25');
        cy.get('[data-cy=card-cvc]').type('123');
        cy.get('[data-cy=card-name]').type('Test User');
      });
      
      cy.get('[data-cy=place-order-button]').click();
      
      // ⚠️ Verify error handling
      cy.get('[data-cy=payment-error]').should('contain', 'Payment failed');
      cy.get('[data-cy=retry-payment-btn]').should('be.visible');
      
      // πŸ”„ User should be able to retry
      cy.url().should('include', '/checkout');
      
      cy.log('βœ… Payment failure handling test passed!');
    });
  });
});

πŸ” Example 2: Authentication and User Management

Testing user authentication flows:

// πŸ§ͺ cypress/e2e/auth/authentication.cy.ts
describe('πŸ” Authentication System', () => {
  beforeEach(() => {
    cy.task('resetDatabase');
    cy.visit('/');
  });
  
  describe('πŸ‘€ User Registration', () => {
    it('should register new user successfully', () => {
      const newUser = {
        name: 'New TypeScript Developer πŸ‘¨β€πŸ’»',
        email: '[email protected]',
        password: 'SecureTypeScript123!'
      };
      
      // πŸ“ Navigate to registration
      cy.get('[data-cy=register-link]').click();
      cy.url().should('include', '/register');
      
      // πŸ“‹ Fill registration form
      cy.get('[data-cy=registration-form]').within(() => {
        cy.get('[data-cy=name-input]').type(newUser.name);
        cy.get('[data-cy=email-input]').type(newUser.email);
        cy.get('[data-cy=password-input]').type(newUser.password);
        cy.get('[data-cy=confirm-password-input]').type(newUser.password);
        
        // βœ… Accept terms
        cy.get('[data-cy=terms-checkbox]').check();
        
        // πŸš€ Submit registration
        cy.get('[data-cy=register-button]').click();
      });
      
      // βœ… Verify registration success
      cy.url().should('include', '/verify-email');
      cy.get('[data-cy=verification-message]').should('contain', 'Check your email');
      
      // πŸ“§ Simulate email verification
      cy.task('getVerificationToken', newUser.email).then((token) => {
        cy.visit(`/verify-email?token=${token}`);
        
        // βœ… Verify account activation
        cy.get('[data-cy=verification-success]').should('be.visible');
        cy.get('[data-cy=login-link]').click();
      });
      
      // πŸ” Login with new account
      cy.login(newUser.email, newUser.password);
      
      // βœ… Verify successful login
      cy.get('[data-cy=welcome-message]').should('contain', newUser.name);
      cy.get('[data-cy=user-avatar]').should('be.visible');
      
      cy.log('πŸŽ‰ User registration flow completed successfully!');
    });
    
    it('should validate registration form inputs', () => {
      cy.get('[data-cy=register-link]').click();
      
      // 🚫 Test empty form submission
      cy.get('[data-cy=register-button]').click();
      
      // ⚠️ Verify validation errors
      cy.get('[data-cy=name-error]').should('contain', 'Name is required');
      cy.get('[data-cy=email-error]').should('contain', 'Email is required');
      cy.get('[data-cy=password-error]').should('contain', 'Password is required');
      
      // πŸ“§ Test invalid email
      cy.get('[data-cy=email-input]').type('invalid-email');
      cy.get('[data-cy=register-button]').click();
      cy.get('[data-cy=email-error]').should('contain', 'Invalid email format');
      
      // πŸ”’ Test weak password
      cy.get('[data-cy=email-input]').clear().type('[email protected]');
      cy.get('[data-cy=password-input]').type('weak');
      cy.get('[data-cy=register-button]').click();
      cy.get('[data-cy=password-error]').should('contain', 'Password too weak');
      
      cy.log('βœ… Form validation tests passed!');
    });
  });
  
  describe('πŸ”‘ Password Reset Flow', () => {
    it('should handle password reset process', () => {
      const userEmail = '[email protected]';
      
      // πŸ” Go to login page
      cy.get('[data-cy=login-link]').click();
      
      // πŸ”‘ Click forgot password
      cy.get('[data-cy=forgot-password-link]').click();
      
      // πŸ“§ Enter email for reset
      cy.get('[data-cy=reset-email-input]').type(userEmail);
      cy.get('[data-cy=send-reset-button]').click();
      
      // βœ… Verify reset email sent
      cy.get('[data-cy=reset-sent-message]').should('contain', 'Reset link sent');
      
      // πŸ”— Simulate clicking reset link
      cy.task('getPasswordResetToken', userEmail).then((token) => {
        cy.visit(`/reset-password?token=${token}`);
        
        // πŸ”‘ Set new password
        const newPassword = 'NewSecurePass123!';
        cy.get('[data-cy=new-password-input]').type(newPassword);
        cy.get('[data-cy=confirm-new-password-input]').type(newPassword);
        cy.get('[data-cy=reset-password-button]').click();
        
        // βœ… Verify password reset success
        cy.get('[data-cy=password-reset-success]').should('be.visible');
        
        // πŸ” Test login with new password
        cy.login(userEmail, newPassword);
        cy.get('[data-cy=user-menu]').should('be.visible');
      });
      
      cy.log('πŸŽ‰ Password reset flow completed successfully!');
    });
  });
});

πŸ“± Example 3: Responsive Design Testing

Test across different devices:

// πŸ§ͺ cypress/e2e/responsive/mobile-experience.cy.ts
describe('πŸ“± Mobile Experience', () => {
  const devices = [
    { name: 'iPhone 12', width: 390, height: 844 },
    { name: 'iPad', width: 820, height: 1180 },
    { name: 'Samsung Galaxy', width: 360, height: 740 }
  ];
  
  devices.forEach((device) => {
    describe(`πŸ“ ${device.name} (${device.width}x${device.height})`, () => {
      beforeEach(() => {
        cy.viewport(device.width, device.height);
        cy.visit('/');
      });
      
      it('should have working mobile navigation', () => {
        // πŸ” Test hamburger menu
        cy.get('[data-cy=hamburger-menu]').should('be.visible');
        cy.get('[data-cy=desktop-nav]').should('not.be.visible');
        
        // πŸ‘† Open mobile menu
        cy.get('[data-cy=hamburger-menu]').click();
        cy.get('[data-cy=mobile-nav]').should('be.visible');
        
        // πŸ”— Test navigation links
        cy.get('[data-cy=mobile-nav]').within(() => {
          cy.get('[data-cy=nav-products]').should('be.visible');
          cy.get('[data-cy=nav-about]').should('be.visible');
          cy.get('[data-cy=nav-contact]').should('be.visible');
        });
        
        // ❌ Close menu
        cy.get('[data-cy=close-mobile-nav]').click();
        cy.get('[data-cy=mobile-nav]').should('not.be.visible');
        
        cy.log(`βœ… Mobile navigation works on ${device.name}!`);
      });
      
      it('should have touch-friendly buttons', () => {
        cy.visit('/products');
        
        // 🎯 Test button sizes (minimum 44px for touch)
        cy.get('[data-cy=add-to-cart-btn]').first().then(($btn) => {
          const height = $btn.height() || 0;
          expect(height).to.be.at.least(44);
        });
        
        // πŸ‘† Test touch interactions
        cy.get('[data-cy=product-card]').first().within(() => {
          cy.get('[data-cy=add-to-cart-btn]').click();
        });
        
        // βœ… Verify touch feedback
        cy.get('.toast-success').should('be.visible');
        
        cy.log(`βœ… Touch interactions work on ${device.name}!`);
      });
    });
  });
});

πŸš€ Advanced Cypress Patterns

πŸ§™β€β™‚οΈ API Testing Integration

Combine E2E with API testing:

// 🌐 Testing API calls during E2E flows
describe('🌐 API Integration Tests', () => {
  it('should intercept and verify API calls', () => {
    // 🎭 Set up API intercepts
    cy.intercept('GET', '/api/products*', { fixture: 'products.json' }).as('getProducts');
    cy.intercept('POST', '/api/orders', { fixture: 'order-response.json' }).as('createOrder');
    
    // πŸ›’ Perform user actions
    cy.visit('/products');
    cy.wait('@getProducts').then((interception) => {
      expect(interception.response?.statusCode).to.eq(200);
      expect(interception.response?.body).to.have.property('products');
    });
    
    // 🎯 Test order creation
    cy.addToCart('typescript-mug');
    cy.get('[data-cy=checkout-button]').click();
    cy.login('[email protected]', 'SecurePass123!');
    cy.get('[data-cy=place-order-button]').click();
    
    // βœ… Verify API was called correctly
    cy.wait('@createOrder').then((interception) => {
      expect(interception.request.body).to.have.property('items');
      expect(interception.request.body.items).to.have.length(1);
      expect(interception.request.body.items[0].productId).to.eq('typescript-mug');
    });
    
    cy.log('πŸŽ‰ API integration test passed!');
  });
});

πŸ”„ Page Object Pattern

Organize your tests with Page Objects:

// πŸ“„ cypress/support/pages/LoginPage.ts
export class LoginPage {
  private selectors = {
    emailInput: '[data-cy=email-input]',
    passwordInput: '[data-cy=password-input]',
    loginButton: '[data-cy=login-button]',
    errorMessage: '[data-cy=login-error]',
    forgotPasswordLink: '[data-cy=forgot-password-link]'
  };
  
  visit(): void {
    cy.visit('/login');
    cy.log('πŸ“„ Visited login page');
  }
  
  fillEmail(email: string): LoginPage {
    cy.get(this.selectors.emailInput).type(email);
    return this;
  }
  
  fillPassword(password: string): LoginPage {
    cy.get(this.selectors.passwordInput).type(password);
    return this;
  }
  
  clickLogin(): void {
    cy.get(this.selectors.loginButton).click();
  }
  
  login(email: string, password: string): void {
    this.fillEmail(email)
        .fillPassword(password)
        .clickLogin();
    cy.log(`πŸ” Logged in as ${email}`);
  }
  
  verifyErrorMessage(message: string): void {
    cy.get(this.selectors.errorMessage).should('contain', message);
  }
  
  clickForgotPassword(): void {
    cy.get(this.selectors.forgotPasswordLink).click();
  }
}

// πŸ§ͺ Using Page Object in tests
import { LoginPage } from '../support/pages/LoginPage';

describe('πŸ” Login with Page Object', () => {
  const loginPage = new LoginPage();
  
  it('should login successfully', () => {
    loginPage.visit();
    loginPage.login('[email protected]', 'password123');
    
    // βœ… Verify successful login
    cy.url().should('not.include', '/login');
  });
});

⚠️ Common E2E Testing Pitfalls

😱 Pitfall 1: Flaky Tests Due to Timing

// ❌ Wrong - race conditions and flaky tests!
describe('🚫 Flaky Test Example', () => {
  it('has timing issues', () => {
    cy.visit('/dashboard');
    cy.get('[data-cy=loading-spinner]'); // Doesn't wait!
    cy.get('[data-cy=dashboard-data]').should('be.visible'); // πŸ’₯ Fails randomly!
  });
});

// βœ… Correct - proper waiting and assertions!
describe('βœ… Stable Test Example', () => {
  it('waits properly for content', () => {
    cy.visit('/dashboard');
    
    // ⏳ Wait for loading to complete
    cy.get('[data-cy=loading-spinner]').should('be.visible');
    cy.get('[data-cy=loading-spinner]').should('not.exist');
    
    // βœ… Then verify content
    cy.get('[data-cy=dashboard-data]').should('be.visible');
    cy.get('[data-cy=user-stats]').should('contain', 'Welcome back');
  });
});

🀯 Pitfall 2: Hardcoded Test Data

// ❌ Wrong - brittle hardcoded values!
describe('🚫 Hardcoded Test Data', () => {
  it('uses hardcoded values', () => {
    cy.visit('/products');
    cy.get('[data-cy=product-list]').should('have.length', 42); // πŸ’₯ Brittle!
    cy.contains('Specific Product Name').click(); // πŸ’₯ Brittle!
  });
});

// βœ… Correct - flexible test data!
describe('βœ… Flexible Test Data', () => {
  beforeEach(() => {
    // 🌱 Seed known test data
    cy.task('seedTestProducts', {
      count: 5,
      category: 'typescript-tools'
    });
  });
  
  it('uses dynamic test data', () => {
    cy.visit('/products');
    
    // βœ… Flexible assertions
    cy.get('[data-cy=product-list]').should('have.length.at.least', 1);
    cy.get('[data-cy=product-card]').first().within(() => {
      cy.get('[data-cy=product-name]').should('not.be.empty');
      cy.get('[data-cy=product-price]').should('match', /\$\d+\.\d{2}/);
    });
  });
});

πŸ› οΈ E2E Testing Best Practices

  1. 🎯 Test User Journeys: Focus on complete workflows, not individual features
  2. πŸ“± Test Multiple Viewports: Ensure responsive design works everywhere
  3. ⚑ Keep Tests Independent: Each test should work in isolation
  4. 🏷️ Use Data Attributes: Add data-cy attributes for reliable selectors
  5. πŸ“Š Monitor Test Performance: Keep test suite execution time reasonable
  6. πŸ”„ Practice DRY: Create reusable commands and page objects
  7. 🎭 Mock External Services: Control third-party dependencies

πŸ§ͺ Hands-On Exercise

🎯 Challenge: Build a Social Media App E2E Test Suite

Create comprehensive E2E tests for a social media platform:

πŸ“‹ Requirements:

  • βœ… User registration and profile setup
  • πŸ“ Create, edit, and delete posts
  • ❀️ Like and comment on posts
  • πŸ‘₯ Follow/unfollow users
  • πŸ” Search for users and content
  • πŸ“± Test on mobile and desktop
  • πŸŒ™ Test dark/light theme switching

πŸš€ Bonus Points:

  • Real-time notification testing
  • Image upload functionality
  • Privacy settings testing
  • Report content functionality

πŸ’‘ Solution Starter

πŸ” Click to see solution starter
// 🎯 Social media E2E test starter
describe('πŸ“± Social Media Platform', () => {
  const testUser = {
    name: 'Social Media Star ⭐',
    email: '[email protected]',
    password: 'SocialPass123!'
  };
  
  beforeEach(() => {
    cy.task('resetSocialDatabase');
    cy.task('createTestUser', testUser);
  });
  
  describe('πŸ‘€ User Profile Journey', () => {
    it('should complete profile setup after registration', () => {
      // πŸ” Login
      cy.login(testUser.email, testUser.password);
      
      // πŸ‘€ Setup profile
      cy.get('[data-cy=profile-setup-prompt]').should('be.visible');
      cy.get('[data-cy=setup-profile-btn]').click();
      
      // πŸ“‹ Fill profile information
      cy.get('[data-cy=profile-form]').within(() => {
        cy.get('[data-cy=bio-textarea]').type('TypeScript enthusiast! πŸ’™');
        cy.get('[data-cy=location-input]').type('Code City πŸ™οΈ');
        cy.get('[data-cy=website-input]').type('https://typescript.dev');
      });
      
      // πŸ“Έ Upload profile picture
      cy.get('[data-cy=avatar-upload]').selectFile('cypress/fixtures/avatar.jpg');
      
      // βœ… Save profile
      cy.get('[data-cy=save-profile-btn]').click();
      
      // πŸŽ‰ Verify profile creation
      cy.get('[data-cy=profile-success]').should('contain', 'Profile updated! πŸŽ‰');
      cy.get('[data-cy=user-avatar]').should('be.visible');
      
      cy.log('πŸ‘€ Profile setup completed successfully!');
    });
  });
  
  describe('πŸ“ Content Creation Flow', () => {
    it('should create and interact with posts', () => {
      cy.login(testUser.email, testUser.password);
      
      // πŸ“ Create new post
      cy.get('[data-cy=new-post-btn]').click();
      cy.get('[data-cy=post-content]').type('Just learned TypeScript E2E testing! πŸš€ #typescript #testing');
      cy.get('[data-cy=publish-post-btn]').click();
      
      // βœ… Verify post appears in feed
      cy.get('[data-cy=post-feed]').within(() => {
        cy.contains('Just learned TypeScript E2E testing!').should('be.visible');
        cy.get('[data-cy=post-author]').should('contain', testUser.name);
      });
      
      cy.log('πŸ“ Post creation test passed!');
    });
  });
});

πŸŽ“ Key Takeaways

You’ve mastered E2E testing with Cypress and TypeScript! Here’s what you can now do:

  • βœ… Set up Cypress with TypeScript for type-safe E2E testing πŸ”§
  • βœ… Write comprehensive user journey tests that simulate real usage πŸ›’
  • βœ… Handle complex interactions like authentication and payments πŸ”
  • βœ… Test responsive design across multiple devices πŸ“±
  • βœ… Debug and optimize E2E test performance πŸš€
  • βœ… Apply best practices for maintainable test suites πŸ› οΈ

Remember: E2E tests are your final safety net - they ensure your users have an amazing experience! 🎯

🀝 Next Steps

Congratulations! πŸŽ‰ You’ve mastered E2E testing with Cypress and TypeScript!

Here’s what to explore next:

  1. πŸ’» Practice with the social media app exercise above
  2. πŸ—οΈ Add E2E tests to your current project
  3. πŸ“š Learn about Playwright for modern E2E testing (Tutorial #153)
  4. πŸ”„ Set up continuous integration for your E2E tests
  5. πŸ“Š Explore visual regression testing

Remember: Great E2E tests give you confidence to ship features knowing your users will love them. Keep testing, keep improving, and build amazing user experiences! πŸš€


Happy testing! πŸŽ‰πŸŽ―βœ¨