Generate end-to-end Cypress tests with Cursor by referencing your React component files and route structure with @file context, creating test-specific .cursorrules for Cypress conventions, and using Composer to generate complete test specs that cover user flows, form submissions, and API interactions.
Why E2E Test Generation Needs Component Context
Cursor generates better E2E tests when it can see your actual component structure, route definitions, and test IDs. Without this context, it guesses at selectors, invents page URLs, and produces tests that fail on the first run. This tutorial shows you how to feed Cursor the right project context and rules to generate Cypress or Playwright specs that actually pass against your running application.
Prerequisites
- Cursor installed (Pro recommended)
- Cypress or Playwright installed and configured in your project
- A React application with at least one complete user flow
- Test IDs (data-testid) added to key interactive elements
Step-by-step guide
Create E2E testing rules for Cursor
Create E2E testing rules for Cursor
Add a .cursor/rules/e2e-testing.mdc file that specifies your E2E framework, selector strategy, and test conventions. This ensures Cursor generates tests using your preferred patterns instead of generic selectors that break with UI changes.
1---2description: E2E test generation rules3globs: "cypress/e2e/**, e2e/**, **/*.cy.ts"4alwaysApply: false5---67- E2E framework: Cypress 13+8- Use data-testid attributes for selectors: cy.get('[data-testid="submit-btn"]')9- NEVER use CSS class selectors or tag-only selectors10- Base URL: http://localhost:300011- Use cy.intercept() to mock API calls, never hit real APIs12- Wait for elements with cy.get().should('be.visible') before interacting13- Group tests with describe per user flow, it per scenario14- Include beforeEach for common setup (login, seed data)15- Test both happy path and error statesPro tip: If you use Playwright instead of Cypress, change the framework line and Cursor will switch to page.getByTestId(), expect(), and Playwright assertions.
Expected result: E2E rules auto-attach when editing any Cypress or E2E test file.
Reference components and routes for context
Reference components and routes for context
When generating E2E tests, reference your route definitions and the components being tested. This gives Cursor the actual URLs, component structure, and interactive elements it needs to generate accurate selectors and navigation steps.
1// Prompt to type in Cursor Composer (Cmd+I):2// @src/App.tsx @src/pages/LoginPage.tsx @src/pages/DashboardPage.tsx3// Generate Cypress E2E tests for the login flow:4// 1. Visit the login page5// 2. Fill in email and password fields6// 3. Click submit7// 4. Verify redirect to dashboard8// 5. Verify user name appears in the header9// Also test: invalid credentials, empty fields, network error10// Mock all API calls with cy.intercept()11// Use data-testid selectors onlyPro tip: Reference @src/api/ or your API client file so Cursor knows the exact endpoints to mock with cy.intercept().
Expected result: Cursor generates a complete Cypress spec file with tests for the login flow using actual selectors from your components.
Generate API intercepts from your API client
Generate API intercepts from your API client
Reference your API client or service files so Cursor can generate accurate cy.intercept() stubs. This ensures mock responses match your actual API contracts. Open Chat (Cmd+L) to generate the intercepts separately, then incorporate them into your test specs.
1// Prompt to type in Cursor Chat (Cmd+L):2// @src/api/authApi.ts @src/types/user.ts3// Generate cy.intercept() stubs for every endpoint in this API client.4// For each endpoint provide:5// 1. The intercept call with method and URL pattern6// 2. A success response fixture with realistic data7// 3. An error response fixture (401, 500)8// Return as a Cypress commands file I can import.Expected result: A set of reusable cy.intercept() commands matching your actual API endpoints and response types.
Generate a complete user flow test
Generate a complete user flow test
Use Composer (Cmd+I) to generate a full user journey test that spans multiple pages. Reference all pages involved in the flow. Tell Cursor the exact user story so it generates steps in the right order with proper waits and assertions.
1// Prompt to type in Cursor Composer (Cmd+I):2// @src/pages/LoginPage.tsx @src/pages/ProductListPage.tsx3// @src/pages/CartPage.tsx @src/pages/CheckoutPage.tsx4// Generate a Cypress E2E spec for the complete purchase flow:5// User story: user logs in, browses products, adds item to cart,6// goes to checkout, fills shipping form, submits order, sees confirmation.7// Requirements:8// - Mock all API calls with cy.intercept()9// - Use data-testid selectors10// - Assert loading states between page transitions11// - Test both successful order and payment failure scenariosPro tip: For long multi-page flows, use Plan Mode (Shift+Tab) first to have Cursor outline all test cases before writing code. This ensures complete coverage.
Expected result: A complete Cypress spec covering the full purchase flow with API mocks and error scenarios.
Add data-testid attributes to components using Cmd+K
Add data-testid attributes to components using Cmd+K
If your components lack test IDs, select a component file, press Cmd+K, and ask Cursor to add data-testid attributes to all interactive elements. This is faster than adding them manually and ensures consistent naming conventions.
1// Open a component file, select all content, press Cmd+K:2// Add data-testid attributes to every interactive element in this3// component (buttons, inputs, links, forms). Use the naming pattern:4// data-testid="{component}-{element}-{action}"5// Example: data-testid="login-email-input"Pro tip: Run this on all page components before generating E2E tests. The consistent naming pattern makes tests more readable and maintainable.
Expected result: All interactive elements in the component have descriptive data-testid attributes.
Complete working example
1describe('Login Flow', () => {2 beforeEach(() => {3 cy.intercept('POST', '/api/auth/login', {4 statusCode: 200,5 body: {6 user: { id: 1, name: 'Jane Doe', email: 'jane@example.com' },7 token: 'mock-jwt-token',8 },9 }).as('loginRequest');1011 cy.intercept('GET', '/api/user/profile', {12 statusCode: 200,13 body: { id: 1, name: 'Jane Doe', role: 'user' },14 }).as('profileRequest');1516 cy.visit('/login');17 });1819 it('should log in successfully with valid credentials', () => {20 cy.get('[data-testid="login-email-input"]').type('jane@example.com');21 cy.get('[data-testid="login-password-input"]').type('securepassword');22 cy.get('[data-testid="login-submit-btn"]').click();2324 cy.wait('@loginRequest');25 cy.url().should('include', '/dashboard');26 cy.get('[data-testid="header-user-name"]').should('contain', 'Jane Doe');27 });2829 it('should show error message for invalid credentials', () => {30 cy.intercept('POST', '/api/auth/login', {31 statusCode: 401,32 body: { error: 'Invalid email or password' },33 }).as('failedLogin');3435 cy.get('[data-testid="login-email-input"]').type('wrong@example.com');36 cy.get('[data-testid="login-password-input"]').type('wrongpassword');37 cy.get('[data-testid="login-submit-btn"]').click();3839 cy.wait('@failedLogin');40 cy.get('[data-testid="login-error-message"]')41 .should('be.visible')42 .and('contain', 'Invalid email or password');43 cy.url().should('include', '/login');44 });4546 it('should show validation errors for empty fields', () => {47 cy.get('[data-testid="login-submit-btn"]').click();48 cy.get('[data-testid="login-email-error"]').should('be.visible');49 cy.get('[data-testid="login-password-error"]').should('be.visible');50 });5152 it('should handle network error gracefully', () => {53 cy.intercept('POST', '/api/auth/login', { forceNetworkError: true }).as('networkError');5455 cy.get('[data-testid="login-email-input"]').type('jane@example.com');56 cy.get('[data-testid="login-password-input"]').type('securepassword');57 cy.get('[data-testid="login-submit-btn"]').click();5859 cy.get('[data-testid="login-error-message"]')60 .should('be.visible')61 .and('contain', 'Network error');62 });63});Common mistakes when generating E2E Tests with Cursor
Why it's a problem: Using CSS class selectors in Cursor-generated E2E tests
How to avoid: Add a .cursorrules entry requiring data-testid selectors only. Generate testid attributes first, then generate tests.
Why it's a problem: Not mocking API calls in E2E tests
How to avoid: Reference your API client with @file and require cy.intercept() for all network requests in your E2E rules.
Why it's a problem: Generating tests without referencing the actual component files
How to avoid: Always reference @src/pages/Page.tsx and @src/App.tsx (routes) in E2E test generation prompts.
Best practices
- Add data-testid attributes to all interactive elements before generating E2E tests
- Reference route definitions and component files with @file for accurate selector generation
- Mock all API calls with cy.intercept() to keep tests fast and deterministic
- Create a .cursor/rules/e2e-testing.mdc with auto-attaching globs for test directories
- Test both happy paths and error states (network errors, 401/403, empty states)
- Use Plan Mode (Shift+Tab) for complex multi-page flow tests before generating code
- Generate reusable Cypress custom commands for common actions like login
- Keep E2E tests focused on user-visible behavior, not implementation details
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Generate a Cypress E2E test for a login flow in a React app. The login page has email input, password input, and submit button. Mock the POST /api/auth/login endpoint. Test: successful login with redirect, invalid credentials, empty form submission, and network errors. Use data-testid selectors.
@src/pages/LoginPage.tsx @src/App.tsx Generate a Cypress E2E spec for the login flow. Use data-testid selectors from the actual component. Mock all API calls with cy.intercept(). Test: successful login with dashboard redirect, invalid credentials showing error, empty field validation, and network error handling.
Frequently asked questions
Should I use Cypress or Playwright with Cursor?
Cursor generates high-quality tests for both. Cypress has a richer ecosystem of plugins and a visual test runner. Playwright is faster and supports multiple browsers natively. Specify your choice in .cursorrules and Cursor will use the correct API.
How do I generate E2E tests for a page that requires authentication?
Create a Cypress custom command for login (cy.login) that sets the auth token programmatically via cy.intercept() or localStorage. Reference this command in your prompt so Cursor uses it in beforeEach blocks.
Can Cursor generate visual regression tests?
Cursor can generate Cypress screenshot commands and Percy/Applitools integration code. Add a rule specifying your visual testing tool and Cursor will include cy.matchImageSnapshot() or similar assertions.
Why do Cursor-generated E2E tests fail on the first run?
Usually because Cursor guessed at selectors or URLs instead of reading your actual component files. Always reference the components and route files with @file. Check that data-testid attributes exist in the DOM.
How do I handle dynamic data in E2E tests?
Use cy.intercept() to return predictable mock data. Reference your API types with @file so Cursor generates response fixtures matching your actual API contracts. Avoid assertions on timestamps or auto-generated IDs.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation