Cypress logo Cypress Cheat Sheet

Basics 👇

Advanced 👇


bun add --dev cypress

Equivalent: npm install --save-dev cypress

{
  "scripts": {
    "cypress:open": "cypress open",
    "cypress:run": "cypress run"
  },
  "devDependencies": {
    "cypress": "^15"
  }
}
bun run cypress:open   # interactive runner
bun run cypress:run    # headless CI
import { defineConfig } from 'cypress';

export default defineConfig({
  e2e: {
    baseUrl: 'http://localhost:3000',
    setupNodeEvents(on, config) {},
  },
});

describe('Dashboard', () => {
  context('when logged in', () => {
    it('shows the welcome message', () => {
      // ...
    });
  });
});

before(() => { /* once per describe */ });
beforeEach(() => { cy.viewport(1536, 960); });
afterEach(() => { /* cleanup */ });
after(() => { /* once per describe */ });

cy.visit('/');              // relative to baseUrl
cy.visit('/login', { qs: { next: '/' } });
cy.go('back');
cy.reload();
cy.url().should('include', '/dashboard');
cy.viewport(1536, 960);
cy.viewport('iphone-x');

Prefer stable selectors: data-test and data-testid (not data-cy).

cy.get('[data-testid=submit]');
cy.contains('Sign in');
cy.get('[data-test=form]').find('[data-testid=email]');
cy.get('[data-testid=modal]').within(() => {
  cy.get('[data-testid=confirm]').click();
});

cy.get('[data-testid=submit]').click();
cy.get('[data-testid=email]').type('user@example.com');
cy.get('[data-testid=email]').clear();
cy.get('[data-testid=terms]').check();
cy.get('[data-testid=country]').select('Switzerland');
cy.get('[data-testid=search]').focus();

cy.get('[data-testid=banner]').should('exist');
cy.get('[data-testid=banner]').should('be.visible');
cy.get('[data-testid=title]').should('have.text', 'Home');
cy.get('[data-testid=email]').should('have.value', 'user@example.com');
cy.get('[data-testid=list]').should('contain', 'Item 1');
cy.get('[data-testid=submit]').should('be.disabled');
cy.get('[data-testid=link]').should('have.attr', 'href', '/about');
cy.get('[data-testid=card]').should('have.class', 'active');
cy.get('[data-testid=row]').should('have.length', 3);

// chain assertions
cy.get('[data-testid=link]')
  .should('be.visible')
  .and('have.attr', 'href', '/home');
cy.intercept('GET', '/api/users', {
  statusCode: 200,
  body: [{ id: 1, name: 'Ada' }],
}).as('getUsers');

cy.visit('/users');
cy.wait('@getUsers');
cy.get('[data-testid=user-row]').should('have.length', 1);
cy.intercept('GET', '/api/users', { fixture: 'users.json' });
cy.fixture('users.json').then((users) => {
  expect(users).to.have.length(2);
});

cy.request('GET', '/api/health').its('status').should('eq', 200);

cy.request({
  method: 'POST',
  url: '/api/login',
  body: { email: 'user@example.com', password: 'secret' },
}).then((res) => {
  expect(res.status).to.eq(200);
});

cy.session('logged-in-user', () => {
  cy.request('POST', '/api/login', {
    email: 'user@example.com',
    password: 'secret',
  });
}, {
  validate() {
    cy.request('/api/me').its('status').should('eq', 200);
  },
});

cy.visit('/dashboard');

// cypress/support/e2e.ts
Cypress.Commands.add('login', (email: string, password: string) => {
  cy.session([email, password], () => {
    cy.request('POST', '/api/login', { email, password });
  });
});

declare global {
  namespace Cypress {
    interface Chainable {
      login(email: string, password: string): Chainable<void>;
    }
  }
}

// in a spec
cy.login('user@example.com', 'secret');
cy.visit('/dashboard');

Wait on network aliases, not arbitrary delays. Use data-test / data-testid selectors. Avoid testing implementation details.

// prefer
cy.wait('@getUsers');

// avoid
cy.wait(5000);

cy.get('[data-testid=submit]', { timeout: 10000 }).click();
cy.get('[data-testid=hidden-menu]').click({ force: true }); // last resort