cheat sheet

cypress

Package-level reference for cypress on npm — install size, binary caching, peer-dep landscape, playwright competition, and component-testing add-ons.

cypress

What it is

cypress is an end-to-end testing framework that runs inside the browser (rather than driving it via WebDriver / CDP). The test code, assertion library, and DOM-interaction primitives all execute in the same JS context as the page under test, giving deterministic synchronous assertions, automatic retries, and a live-reloading UI that shows every command as it runs.

It ships a hefty Electron binary (cached separately from the npm package) plus a Node-side daemon that proxies network requests. The pitch: tests you can debug visually, with built-in time-travel, snapshots of every step, and CI video recording out of the box.

Install

bash
# npm / pnpm / yarn / bun
npm install -D cypress
pnpm add -D cypress
yarn add -D cypress
bun add -d cypress

Output: cypress binary on PATH under node_modules/.bin/cypress. The post-install hook downloads a platform-specific Electron + Cypress binary (~200 MB) into ~/.cache/Cypress/<version>/.

bash
# First-run scaffold
npx cypress open

Output: opens the Cypress Launchpad UI; on first run, generates cypress.config.js, cypress/e2e/, cypress/support/, cypress/fixtures/.

bash
# Headless run for CI
npx cypress run

Output:

text
  (Run Starting)
  Cypress:    14.0.0
  Browser:    Electron 124 (headless)

  Tests:      12 passing
  Duration:   23 seconds

Versioning & Node support

  • Current major line is 14.x (released 2025) — adds Node 22+ test-runner support, dropped older runtime support, improved component testing isolation.
  • 13.x is widely deployed and still patched.
  • Recent releases require Node 18+ or 20+; macOS 11+, Ubuntu 20.04+, Windows 10/11.
  • Always a dev dependency; the cached binary lives outside node_modules.
  • The binary cache is keyed by version — sharing the cache directory across CI jobs (e.g. ~/.cache/Cypress) saves ~200 MB per job.

Package metadata

  • Maintainer: Cypress.io, Inc. (commercial company; runner is MIT-licensed; the Cypress Cloud service is the paid offering).
  • Project home: github.com/cypress-io/cypress
  • Docs: docs.cypress.io
  • npm: npmjs.com/package/cypress
  • License: MIT (runner), commercial (Cypress Cloud / Test Replay).
  • First released: 2017 (1.0); breakout adoption 2018-2020 as the React/SPA E2E choice.
  • Downloads: millions weekly. Growth has plateaued while Playwright has nearly caught up; Cypress retains its niche through the visual debugger and lower learning curve.

Peer dependencies & extras

cypress has no required peer deps — the binary is self-contained. Common add-ons:

Add-onPurpose
@cypress/react / @cypress/vueComponent testing mount adapters — mount React/Vue components inside the Cypress runner
@cypress/code-coverageWire Istanbul coverage from instrumented app code back into Cypress reports
@cypress/grepTag-and-filter tests with @smoke, @regression, etc.
cypress-axeAccessibility checks via axe-core in any spec
cypress-image-snapshotVisual regression — pixel-diff snapshots
cypress-real-eventsNative pointer events (click-through, hover) the JS event-dispatch approach can't simulate
start-server-and-testBoot the dev server, wait for it, run Cypress, tear down — the standard CI wrapper
@testing-library/cypressfindBy* queries from React Testing Library, ported to Cypress chainables

Alternatives

RunnerTrade-off
PlaywrightThe new default in 2024-2026 — multi-browser (Chromium/Firefox/WebKit), faster, parallel by default, better trace viewer. Architecturally driver-based (out-of-process), avoiding Cypress's same-origin and same-tab limitations.
WebdriverIOMature, framework-agnostic, mobile support via Appium. More boilerplate; smaller adoption than Playwright.
Selenium WebDriverThe grand-daddy; still dominant in enterprise QA and Java/Python shops. Verbose; slow; broader browser/OS matrix than anyone else.
PuppeteerGoogle-maintained Chromium-only library. Lower-level than Playwright; mostly displaced by it.
NightwatchBuilt on WebDriver. Component testing recently added. Niche today.

Honest take (2026): Playwright has overtaken Cypress in greenfield adoption and is the default recommendation for new E2E suites. Cypress retains a real niche:

  1. Designer/non-engineer-friendly debugging. The Cypress UI's time-travel and DOM snapshots are objectively the best in the space.
  2. Component testing. Cypress Component Testing is competitive with Playwright Component Testing and has the better visual story.
  3. Established suites. Cypress's cy.* chainable API is loved by teams who have invested years in it; migration to Playwright costs real engineering time.

For a new project starting today with no preference, pick Playwright.

Common gotchas

  1. Same-origin restriction (legacy). Pre-v12, Cypress could only test one origin per spec. v12+ added cy.origin() to cross domains, but auth-redirect flows still trip up new users.
  2. Async-but-not-promise chainables. cy.get(...) returns a Cypress chainable, NOT a promise. await cy.get(...) is a common bug — it resolves with undefined. Use .then(...) or cy.wrap(...) for sync interop.
  3. cy.intercept is order-dependent. Define intercepts BEFORE the request fires. A common bug: visiting the page first, then intercepting — the request already went out.
  4. Tests don't share state by design. Each spec file gets a fresh browser context. State sharing between specs requires cy.session() (or repeated login per file).
  5. The binary cache can desync. If package.json says v14 but the binary cache holds v13, you get Cypress binary verification failed. Fix: npx cypress install --force.
  6. iframes need cy.iframe() plugins. Native iframe traversal is verbose; most teams install cypress-iframe.
  7. Cypress Cloud is paid and opt-in. The free runner is fully featured; record-mode and parallel-orchestration require a Cloud account. Easy to misread the docs and assume parallel-by-default.
  8. Component tests don't share the e2e runner's network stack. cy.intercept works differently in component-testing mode — there's no Node-side proxy. Stub at the import level instead.

Real-world recipes

Minimal E2E test

javascript
// cypress/e2e/login.cy.js
describe("login", () => {
  it("logs in with valid credentials", () => {
    cy.visit("/login");
    cy.get('[data-cy="email"]').type("alice@example.com");
    cy.get('[data-cy="password"]').type("hunter2");
    cy.get('[data-cy="submit"]').click();
    cy.url().should("include", "/dashboard");
    cy.contains("Welcome, Alice").should("be.visible");
  });
});
bash
npx cypress run --spec "cypress/e2e/login.cy.js"

Output:

text
  Running:  login.cy.js
    login
      ✓ logs in with valid credentials (842ms)

  Tests:        1
  Passing:      1
  Duration:     1 second

Network mocking with cy.intercept

cy.intercept mocks any XHR or fetch from the browser. Define BEFORE the request fires. Combine with cy.wait('@alias') to assert that a call happened and grab its body.

javascript
it("renders the user list from the API", () => {
  cy.intercept("GET", "/api/users", { fixture: "users.json" }).as("getUsers");
  cy.visit("/users");
  cy.wait("@getUsers");
  cy.get('[data-cy="user-row"]').should("have.length", 3);
});

For error simulation:

javascript
cy.intercept("POST", "/api/login", { statusCode: 500, body: { error: "Server error" } });

Fixtures for test data

A fixture is a JSON file under cypress/fixtures/ that you load into intercepts or assertions. Keeps response shapes versioned alongside the tests.

json
// cypress/fixtures/users.json
[
  { "id": 1, "name": "Alice Dev" },
  { "id": 2, "name": "Bob Build" },
  { "id": 3, "name": "Carol Test" }
]
javascript
cy.fixture("users").then((users) => {
  cy.intercept("GET", "/api/users", users).as("getUsers");
});

cy.session for auth reuse

Before cy.session(), every spec needed a full login. cy.session() caches the browser's auth state (cookies, localStorage, sessionStorage) per-session-id; subsequent tests in the same run restore it instantly.

javascript
// cypress/support/commands.js
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");
  });
});
javascript
// cypress/e2e/profile.cy.js
beforeEach(() => {
  cy.login("alice@example.com", "hunter2");
  cy.visit("/profile");
});

it("shows the user name", () => {
  cy.contains("Alice Dev").should("be.visible");
});

Component testing

javascript
// src/components/Counter.cy.jsx
import { Counter } from "./Counter";

describe("<Counter />", () => {
  it("increments on click", () => {
    cy.mount(<Counter initial={0} />);
    cy.get('[data-cy="increment"]').click();
    cy.get('[data-cy="count"]').should("have.text", "1");
  });
});

Config:

javascript
// cypress.config.js
import { defineConfig } from "cypress";
export default defineConfig({
  component: {
    devServer: { framework: "react", bundler: "vite" },
  },
});

Custom command for repeated actions

javascript
// cypress/support/commands.js
Cypress.Commands.add("dataCy", (value) => cy.get(`[data-cy="${value}"]`));

// Usage:
cy.dataCy("email").type("alice@example.com");

Production deployment

Cypress is a CI tool — never run it as a production artefact. Common CI shapes:

GitHub Actions

yaml
# .github/workflows/e2e.yml
- uses: actions/setup-node@v4
  with: { node-version: "20", cache: "npm" }
- uses: actions/cache@v4
  with:
    path: ~/.cache/Cypress
    key: cypress-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
- run: npm ci
- name: Run E2E
  run: npx start-server-and-test "npm run dev" http://localhost:3000 "npx cypress run --browser chrome"
- uses: actions/upload-artifact@v4
  if: failure()
  with:
    name: cypress-failures
    path: |
      cypress/screenshots
      cypress/videos

Parallelism

The free CLI does not parallelise across machines. To split a 30-minute suite across 4 machines, you need Cypress Cloud (--record --parallel) or a third-party orchestrator like cypress-parallel (file-level splitting).

bash
# Cypress Cloud parallel mode
npx cypress run --record --key $CYPRESS_RECORD_KEY --parallel --group "e2e"

Output:

text
   Recorded Run: https://cloud.cypress.io/projects/abc/runs/42
   Tests:        48 (split across 4 machines)
   Duration:     6 minutes 12 seconds

Performance tuning

E2E tests are inherently slow; Cypress amplifies this with its Electron startup cost. Mitigations:

Pin to one browser

Default uses Electron. Chrome is faster on CI: --browser chrome --headless.

Avoid cy.wait(number)

Replace timing-based waits with aliased intercept waits:

javascript
cy.intercept("GET", "/api/dashboard").as("dash");
cy.visit("/dashboard");
cy.wait("@dash");                      // waits for actual response

vs the antipattern:

javascript
cy.visit("/dashboard");
cy.wait(2000);                          // flaky; either too long or not enough

Bypass UI for setup

Don't drive the login form in 50 tests if you can set the session cookie directly:

javascript
cy.request("POST", "/api/login", { email, password }).then((res) => {
  cy.setCookie("session", res.body.token);
});

Component testing for the bulk

Component tests run in 100-500 ms each; e2e in 2-10 s. Move logic-heavy tests to component, save e2e for happy-path user flows.

Version migration guide

From → ToHighlights
9 → 10Major redesign. Folder restructure (integration/e2e/). New cypress.config.js replaces cypress.json.
10 → 11Component testing GA. New iframe-isolation model.
11 → 12cy.origin() for cross-domain. Session-caching default. Test isolation enforced.
12 → 13Node 16+. Performance improvements; new screenshot/video pipeline.
13 → 14Node 18+. Test Replay improvements. Component testing isolation by default.

Migrating to Playwright

A practical script can rewrite ~70-80% of a Cypress suite mechanically; the residue (custom commands, cy.intercept semantics, plugin-based features) needs manual work. Budget 1-2 days per 100 tests.

Security considerations

  1. CYPRESS_RECORD_KEY is a write credential. Anyone with it can submit runs to your Cypress Cloud org. Treat like an API key — never log, never commit.
  2. Video and screenshot artefacts can leak data. Test recordings include PII, tokens shown in URLs, and form values. Strip uploads before public CI sharing.
  3. cy.task runs arbitrary Node code. Tasks declared in cypress.config.js execute server-side with full process permissions; treat them like prod code.
  4. The bundled Chromium can be outdated. Cypress ships a pinned Electron version — patch CVEs by upgrading Cypress, not by overriding Chromium.

Configuration patterns

cypress.config.js

javascript
import { defineConfig } from "cypress";

export default defineConfig({
  e2e: {
    baseUrl: "http://localhost:3000",
    specPattern: "cypress/e2e/**/*.cy.{js,ts}",
    setupNodeEvents(on, config) {
      // Plugin registration
      return config;
    },
    retries: { runMode: 2, openMode: 0 },
    video: false,
    screenshotOnRunFailure: true,
  },
  component: {
    devServer: { framework: "react", bundler: "vite" },
  },
});

Per-environment overrides

bash
# CI overrides
CYPRESS_baseUrl=https://staging.example.com npx cypress run

Output:

text
  (Run Starting)
  baseUrl:    https://staging.example.com

  Tests:      12 passing

Troubleshooting common errors

  • Cypress binary verification failed — cache desync. npx cypress install --force.
  • cy.intercept never matched — intercept declared after the request fired. Move before cy.visit.
  • cy.session() failed; the session state was empty — the callback didn't set cookies/storage. Add an assertion at the end of the session callback to confirm the auth state was applied.
  • Element not found, but you can see it — Cypress waits up to defaultCommandTimeout (4 s); flaky animations need a longer timeout, not cy.wait(ms). Use cy.get(sel, { timeout: 10000 }).
  • CypressError: cy.visit() failed because you are attempting to visit a URL that is of a different origin — wrap the second-origin code in cy.origin('https://other.example.com', () => { ... }).

Ecosystem integrations

ToolWhat it adds
@cypress/code-coverageWire Istanbul coverage from instrumented app code
cypress-axeAccessibility checks
cypress-image-snapshotVisual regression
start-server-and-testCI server lifecycle wrapper
@testing-library/cypressRTL queries inside chainables
cypress-grep / @cypress/grepTag-based test filtering
cypress-real-eventsReal pointer/keyboard events
cypress-recursePolling helpers for hard-to-stabilise flows

When NOT to use this

  • New E2E suites without strong Cypress preference. Use Playwright — multi-browser, faster, parallel-by-default, free for parallelism.
  • Multi-browser parity testing. Cypress is Chromium-centric (with experimental Firefox). Playwright covers WebKit and Firefox first-class.
  • Mobile-app testing. Cypress is web-only. Use Appium / Detox / Maestro.
  • Cross-tab or multi-window flows. Cypress is single-tab by design. Playwright handles these natively.
  • Heavy network-record-and-replay. Polly.js, MSW, or Playwright's HAR record/replay are stronger than cy.intercept for snapshot-heavy flows.
  • Pure unit/component tests with no DOM concerns. Use vitest or jest — they start in ~100 ms instead of ~5 seconds.

Cypress is still excellent at one thing better than anyone else: visual debugging of an in-browser test step-by-step. If that workflow is core to your team's QA culture, the speed gap with Playwright is acceptable.

See also