Claude-skill-registry cypress

Writes E2E and component tests with Cypress including selectors, commands, fixtures, and API testing. Use when testing web applications end-to-end, testing user flows, or writing integration tests.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/cypress" ~/.claude/skills/majiayu000-claude-skill-registry-cypress-5d91b6 && rm -rf "$T"
manifest: skills/data/cypress/SKILL.md
source content

Cypress

Fast, reliable testing for anything that runs in a browser.

Quick Start

Install:

npm install cypress --save-dev

Open Cypress:

npx cypress open

Run headless:

npx cypress run

Project Structure

cypress/
  e2e/               # E2E test files
    home.cy.ts
    auth.cy.ts
  fixtures/          # Test data
    users.json
  support/
    commands.ts      # Custom commands
    e2e.ts          # E2E support file
    component.ts    # Component support file
  downloads/         # Downloaded files
cypress.config.ts    # Configuration

Configuration

// cypress.config.ts
import { defineConfig } from 'cypress';

export default defineConfig({
  e2e: {
    baseUrl: 'http://localhost:3000',
    viewportWidth: 1280,
    viewportHeight: 720,
    defaultCommandTimeout: 10000,
    video: false,
    screenshotOnRunFailure: true,
    setupNodeEvents(on, config) {
      // Node event listeners
    },
  },
  component: {
    devServer: {
      framework: 'react',
      bundler: 'vite',
    },
  },
  env: {
    apiUrl: 'http://localhost:3001',
  },
});

Basic Test

// cypress/e2e/home.cy.ts
describe('Home Page', () => {
  beforeEach(() => {
    cy.visit('/');
  });

  it('displays the welcome message', () => {
    cy.contains('h1', 'Welcome').should('be.visible');
  });

  it('navigates to about page', () => {
    cy.get('a[href="/about"]').click();
    cy.url().should('include', '/about');
    cy.contains('About Us').should('be.visible');
  });
});

Selectors

Best Practices

// Prefer data-* attributes
cy.get('[data-testid="submit-button"]');
cy.get('[data-cy="user-name"]');

// By role (accessibility)
cy.get('button').contains('Submit');
cy.get('input[type="email"]');

// By text content
cy.contains('Sign In');
cy.contains('button', 'Submit');

// Avoid brittle selectors
// Bad: cy.get('.btn-primary-lg-submit')
// Bad: cy.get('#form > div:nth-child(3) > button')

Chaining

cy.get('[data-cy="user-form"]')
  .find('input[name="email"]')
  .type('test@example.com');

cy.get('[data-cy="user-list"]')
  .children()
  .first()
  .click();

Commands

Navigation

cy.visit('/');
cy.visit('/users/123');
cy.go('back');
cy.go('forward');
cy.reload();

Interactions

// Click
cy.get('button').click();
cy.get('button').dblclick();
cy.get('button').rightclick();

// Type
cy.get('input').type('Hello World');
cy.get('input').type('test@email.com{enter}');
cy.get('input').clear().type('New value');

// Select
cy.get('select').select('Option 1');
cy.get('select').select(['opt1', 'opt2']);

// Checkbox/Radio
cy.get('input[type="checkbox"]').check();
cy.get('input[type="checkbox"]').uncheck();
cy.get('input[type="radio"]').check('value');

// File upload
cy.get('input[type="file"]').selectFile('cypress/fixtures/image.png');

// Scroll
cy.scrollTo('bottom');
cy.get('.container').scrollTo(0, 500);

// Focus/Blur
cy.get('input').focus();
cy.get('input').blur();

Assertions

// Visibility
cy.get('button').should('be.visible');
cy.get('.modal').should('not.exist');
cy.get('.loading').should('not.be.visible');

// Content
cy.get('h1').should('contain', 'Welcome');
cy.get('h1').should('have.text', 'Welcome Home');
cy.get('p').should('include.text', 'partial');

// Attributes
cy.get('input').should('have.value', 'test');
cy.get('a').should('have.attr', 'href', '/about');
cy.get('button').should('be.disabled');
cy.get('input').should('be.enabled');

// CSS
cy.get('div').should('have.class', 'active');
cy.get('div').should('have.css', 'display', 'flex');

// Length
cy.get('li').should('have.length', 5);
cy.get('li').should('have.length.greaterThan', 3);

// Chained
cy.get('input')
  .should('be.visible')
  .and('have.value', '')
  .and('be.enabled');

Fixtures

Load Fixture Data

// cypress/fixtures/users.json
{
  "users": [
    { "id": 1, "name": "John", "email": "john@example.com" },
    { "id": 2, "name": "Jane", "email": "jane@example.com" }
  ]
}
// In test
cy.fixture('users').then((data) => {
  const user = data.users[0];
  cy.get('[data-cy="email"]').type(user.email);
});

// Or with alias
beforeEach(() => {
  cy.fixture('users').as('usersData');
});

it('uses fixture data', function() {
  cy.get('[data-cy="email"]').type(this.usersData.users[0].email);
});

API Testing

cy.request

describe('API Tests', () => {
  it('fetches users', () => {
    cy.request('GET', '/api/users').then((response) => {
      expect(response.status).to.eq(200);
      expect(response.body).to.have.length.greaterThan(0);
    });
  });

  it('creates a user', () => {
    cy.request({
      method: 'POST',
      url: '/api/users',
      body: {
        name: 'John Doe',
        email: 'john@example.com',
      },
    }).then((response) => {
      expect(response.status).to.eq(201);
      expect(response.body).to.have.property('id');
    });
  });

  it('handles auth', () => {
    cy.request({
      method: 'POST',
      url: '/api/login',
      body: { email: 'test@example.com', password: 'password' },
    }).then((response) => {
      expect(response.body).to.have.property('token');
      // Use token in subsequent requests
      cy.request({
        method: 'GET',
        url: '/api/profile',
        headers: {
          Authorization: `Bearer ${response.body.token}`,
        },
      });
    });
  });
});

Intercept Network Requests

describe('Mocking API', () => {
  it('mocks API response', () => {
    cy.intercept('GET', '/api/users', {
      statusCode: 200,
      body: [
        { id: 1, name: 'Mocked User' },
      ],
    }).as('getUsers');

    cy.visit('/users');
    cy.wait('@getUsers');
    cy.contains('Mocked User').should('be.visible');
  });

  it('mocks with fixture', () => {
    cy.intercept('GET', '/api/users', { fixture: 'users.json' }).as('getUsers');
    cy.visit('/users');
    cy.wait('@getUsers');
  });

  it('spies on requests', () => {
    cy.intercept('POST', '/api/users').as('createUser');

    cy.get('[data-cy="create-user-form"]').within(() => {
      cy.get('input[name="name"]').type('John');
      cy.get('button[type="submit"]').click();
    });

    cy.wait('@createUser').its('request.body').should('deep.equal', {
      name: 'John',
    });
  });

  it('delays response', () => {
    cy.intercept('GET', '/api/data', {
      delay: 2000,
      body: { data: 'slow response' },
    });

    cy.visit('/data');
    cy.get('.loading').should('be.visible');
    cy.get('.data').should('be.visible');
  });
});

Custom Commands

// cypress/support/commands.ts
declare global {
  namespace Cypress {
    interface Chainable {
      login(email: string, password: string): Chainable<void>;
      getByCy(selector: string): Chainable<JQuery<HTMLElement>>;
    }
  }
}

Cypress.Commands.add('login', (email, password) => {
  cy.session([email, password], () => {
    cy.visit('/login');
    cy.get('[data-cy="email"]').type(email);
    cy.get('[data-cy="password"]').type(password);
    cy.get('[data-cy="submit"]').click();
    cy.url().should('include', '/dashboard');
  });
});

Cypress.Commands.add('getByCy', (selector) => {
  return cy.get(`[data-cy="${selector}"]`);
});

export {};
// Usage
describe('Dashboard', () => {
  beforeEach(() => {
    cy.login('test@example.com', 'password');
  });

  it('shows dashboard', () => {
    cy.visit('/dashboard');
    cy.getByCy('welcome-message').should('be.visible');
  });
});

Component Testing

// cypress/component/Button.cy.tsx
import Button from '../../src/components/Button';

describe('Button Component', () => {
  it('renders with text', () => {
    cy.mount(<Button>Click me</Button>);
    cy.contains('Click me').should('be.visible');
  });

  it('handles click', () => {
    const onClick = cy.stub().as('onClick');
    cy.mount(<Button onClick={onClick}>Click me</Button>);
    cy.get('button').click();
    cy.get('@onClick').should('have.been.calledOnce');
  });

  it('shows disabled state', () => {
    cy.mount(<Button disabled>Disabled</Button>);
    cy.get('button').should('be.disabled');
  });
});

Waiting

// Wait for element
cy.get('.loading').should('not.exist');
cy.get('.data').should('be.visible');

// Wait for network
cy.intercept('GET', '/api/data').as('getData');
cy.visit('/');
cy.wait('@getData');

// Wait for multiple
cy.wait(['@getUsers', '@getPosts']);

// Explicit wait (avoid when possible)
cy.wait(1000);

Best Practices

  1. Use data- attributes* - Stable selectors
  2. Don't use cy.wait(ms) - Use assertions instead
  3. Reset state between tests - Use beforeEach
  4. Use cy.session - Cache authentication
  5. Use intercept for mocking - Control network

Common Mistakes

MistakeFix
Using arbitrary waitsUse assertions that retry
Brittle CSS selectorsUse data-testid attributes
Not cleaning up stateUse beforeEach/afterEach
Testing implementationTest user behavior
Flaky testsUse proper waiting strategies

Reference Files