Cypress Core Knowledge
Deep Knowledge: Use mcp__documentation__fetch_docs with technology: cypress for comprehensive documentation.
When NOT to Use This Skill
-
Unit Testing - Use vitest or jest for isolated tests
-
Multi-Tab Scenarios - Cypress doesn't support multiple tabs
-
Native Mobile Apps - Use Appium or Detox
-
Multi-Browser Testing - Limited browser support compared to Playwright
-
Performance Testing - Use k6, Lighthouse, or dedicated tools
Basic Test
describe('Login', () => { beforeEach(() => { cy.visit('/login'); });
it('should login successfully', () => { cy.get('[data-testid="email"]').type('user@example.com'); cy.get('[data-testid="password"]').type('password123'); cy.get('button[type="submit"]').click();
cy.url().should('include', '/dashboard');
cy.contains('Welcome back').should('be.visible');
});
it('should show error for invalid credentials', () => { cy.get('[data-testid="email"]').type('wrong@example.com'); cy.get('[data-testid="password"]').type('wrong'); cy.get('button[type="submit"]').click();
cy.contains('Invalid credentials').should('be.visible');
}); });
Commands
// Selection cy.get('.class'); cy.get('#id'); cy.get('[data-testid="element"]'); cy.contains('text'); cy.find('.child');
// Actions cy.click(); cy.type('text'); cy.clear(); cy.check(); cy.select('option'); cy.scrollIntoView();
// Navigation cy.visit('/page'); cy.go('back'); cy.reload();
Assertions
cy.get('element') .should('be.visible') .should('have.text', 'Hello') .should('have.class', 'active') .should('have.attr', 'href', '/home') .should('have.length', 3) .should('contain', 'text') .should('not.exist');
// Chained cy.get('input').should('have.value', 'test').and('be.disabled');
Custom Commands
// cypress/support/commands.ts Cypress.Commands.add('login', (email: string, password: string) => { cy.visit('/login'); cy.get('[data-testid="email"]').type(email); cy.get('[data-testid="password"]').type(password); cy.get('button[type="submit"]').click(); });
// Usage cy.login('user@example.com', 'password');
Intercept API
cy.intercept('GET', '/api/users', { fixture: 'users.json' }).as('getUsers'); cy.visit('/users'); cy.wait('@getUsers');
cy.intercept('POST', '/api/users', { statusCode: 201 }).as('createUser');
Production Readiness
Configuration
// cypress.config.ts import { defineConfig } from 'cypress';
export default defineConfig({ e2e: { baseUrl: 'http://localhost:3000', viewportWidth: 1280, viewportHeight: 720, video: true, screenshotOnRunFailure: true, retries: { runMode: 2, // CI retries openMode: 0, // Local retries }, env: { apiUrl: 'http://localhost:3000/api', }, }, component: { devServer: { framework: 'react', bundler: 'vite', }, }, });
Authentication
// cypress/support/commands.ts Cypress.Commands.add('login', (email: string, password: string) => { // Programmatic login (faster than UI) cy.request({ method: 'POST', url: '/api/auth/login', body: { email, password }, }).then(({ body }) => { window.localStorage.setItem('token', body.token); }); });
// Preserve auth between tests Cypress.Commands.add('preserveAuth', () => { cy.getCookie('session').then(cookie => { if (cookie) { Cypress.Cookies.preserveOnce('session'); } }); });
// Usage beforeEach(() => { cy.login(Cypress.env('TEST_USER'), Cypress.env('TEST_PASS')); });
API Testing & Mocking
// Intercept and mock cy.intercept('GET', '/api/users', { fixture: 'users.json' }).as('getUsers'); cy.intercept('POST', '/api/users', (req) => { req.reply({ statusCode: 201, body: { id: '123', ...req.body }, }); }).as('createUser');
// Wait for API calls cy.wait('@getUsers').its('response.statusCode').should('eq', 200);
// Spy without mocking cy.intercept('GET', '/api/users').as('getUsers'); cy.wait('@getUsers').then((interception) => { expect(interception.response.body).to.have.length.greaterThan(0); });
CI Configuration
GitHub Actions
-
name: Cypress run uses: cypress-io/github-action@v5 with: build: npm run build start: npm start wait-on: 'http://localhost:3000' record: true env: CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
-
name: Upload screenshots uses: actions/upload-artifact@v3 if: failure() with: name: cypress-screenshots path: cypress/screenshots
Network Handling
// Handle slow networks cy.intercept('/api/**', (req) => { req.on('response', (res) => { res.setDelay(1000); // Simulate slow response }); });
// Handle offline cy.intercept('/api/**', { forceNetworkError: true });
// Retry failed requests cy.request({ url: '/api/data', retryOnStatusCodeFailure: true, retryOnNetworkFailure: true, });
Monitoring Metrics
Metric Target
E2E test pass rate
99%
Test execution time < 15min
Flaky test rate < 1%
Video review on failure 100%
Best Practices
// Use data-testid for stable selectors cy.get('[data-testid="submit-btn"]').click();
// Avoid arbitrary waits // BAD: cy.wait(5000) // GOOD: cy.get('[data-testid="result"]').should('be.visible')
// Chain assertions cy.get('form') .should('be.visible') .find('input') .should('have.length', 3);
// Custom assertions cy.get('@createUser') .its('request.body') .should('deep.include', { name: 'John' });
Checklist
-
Programmatic login (not UI)
-
API interception for isolation
-
Retry configuration for CI
-
Video recording enabled
-
Screenshots on failure
-
data-testid for selectors
-
No arbitrary cy.wait()
-
Custom commands for reuse
-
CI/CD with Cypress Dashboard
-
Environment variables secured
Anti-Patterns
Anti-Pattern Why It's Bad Solution
Arbitrary cy.wait(5000) Slow, unreliable Use cy.intercept aliases and cy.wait('@alias')
Testing login UI every test Extremely slow Use programmatic login or cy.session
Selecting by text content Brittle, breaks on copy changes Use data-testid or semantic selectors
Not using cy.intercept Tests depend on real API Mock API responses for speed and reliability
Chaining too many assertions Hard to debug which failed Break into separate assertions
Not cleaning up data Tests pollute each other Reset DB state before/after tests
Using .then() unnecessarily Breaks Cypress retry logic Use built-in commands when possible
Quick Troubleshooting
Problem Likely Cause Solution
"element is detached from DOM" Element re-rendered during action Use cy.get() again, not stored reference
"Timed out retrying" Element not found or condition not met Check selector, increase timeout if needed
Flaky test Race condition with API or DOM Use cy.intercept, wait for specific state
"CypressError: cy.visit() failed" Server not running or wrong URL Verify baseUrl in config, check server
Test passes locally, fails in CI Timing differences Add explicit waits for network requests
"Cannot read property of undefined" Async command not properly chained Ensure commands are chained with .then()
Reference Documentation
-
Commands
-
Fixtures