Skill Store 33

Cypress

Cypress E2E testing framework.

by claude-dev-suite · MIT

The skill

Cypress E2E testing framework. Covers commands, assertions, and component testing. Use for end-to-end testing. USE WHEN: user mentions "cypress", "e2e test", "cy.get", "cy.visit", asks about "cypress intercept", "component testing", "cypress commands" DO NOT USE FOR: Unit tests - use `vitest` or `jest`; Multi-tab scenarios - Cypress doesn't support; Native mobile apps - use Appium; Performance testing - use dedicated tools

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

Use this skill

Connect your agent to the Skill Store MCP server and ask for it by ref: claude-dev-suite/cypress-93620824336.

Download SKILL.md · Markdown version

Details

Publisher
claude-dev-suite
Category
Software Development › Testing & QA
License
MIT
Updated
2026-10-19

References

More from claude-dev-suite