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
# 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>/.
# 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/.
# Headless run for CI
npx cypress run
Output:
(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.xis 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-on | Purpose |
|---|---|
@cypress/react / @cypress/vue | Component testing mount adapters — mount React/Vue components inside the Cypress runner |
@cypress/code-coverage | Wire Istanbul coverage from instrumented app code back into Cypress reports |
@cypress/grep | Tag-and-filter tests with @smoke, @regression, etc. |
cypress-axe | Accessibility checks via axe-core in any spec |
cypress-image-snapshot | Visual regression — pixel-diff snapshots |
cypress-real-events | Native pointer events (click-through, hover) the JS event-dispatch approach can't simulate |
start-server-and-test | Boot the dev server, wait for it, run Cypress, tear down — the standard CI wrapper |
@testing-library/cypress | findBy* queries from React Testing Library, ported to Cypress chainables |
Alternatives
| Runner | Trade-off |
|---|---|
| Playwright | The 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. |
| WebdriverIO | Mature, framework-agnostic, mobile support via Appium. More boilerplate; smaller adoption than Playwright. |
| Selenium WebDriver | The grand-daddy; still dominant in enterprise QA and Java/Python shops. Verbose; slow; broader browser/OS matrix than anyone else. |
| Puppeteer | Google-maintained Chromium-only library. Lower-level than Playwright; mostly displaced by it. |
| Nightwatch | Built 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:
- Designer/non-engineer-friendly debugging. The Cypress UI's time-travel and DOM snapshots are objectively the best in the space.
- Component testing. Cypress Component Testing is competitive with Playwright Component Testing and has the better visual story.
- 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
- 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. - Async-but-not-promise chainables.
cy.get(...)returns a Cypress chainable, NOT a promise.await cy.get(...)is a common bug — it resolves withundefined. Use.then(...)orcy.wrap(...)for sync interop. cy.interceptis order-dependent. Define intercepts BEFORE the request fires. A common bug: visiting the page first, then intercepting — the request already went out.- 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). - The binary cache can desync. If
package.jsonsays v14 but the binary cache holds v13, you getCypress binary verification failed. Fix:npx cypress install --force. - iframes need
cy.iframe()plugins. Native iframe traversal is verbose; most teams installcypress-iframe. - 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.
- Component tests don't share the e2e runner's network stack.
cy.interceptworks differently in component-testing mode — there's no Node-side proxy. Stub at the import level instead.
Real-world recipes
Minimal E2E test
// 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");
});
});
npx cypress run --spec "cypress/e2e/login.cy.js"
Output:
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.
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:
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.
// cypress/fixtures/users.json
[
{ "id": 1, "name": "Alice Dev" },
{ "id": 2, "name": "Bob Build" },
{ "id": 3, "name": "Carol Test" }
]
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.
// 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");
});
});
// 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
// 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:
// cypress.config.js
import { defineConfig } from "cypress";
export default defineConfig({
component: {
devServer: { framework: "react", bundler: "vite" },
},
});
Custom command for repeated actions
// 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
# .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).
# Cypress Cloud parallel mode
npx cypress run --record --key $CYPRESS_RECORD_KEY --parallel --group "e2e"
Output:
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:
cy.intercept("GET", "/api/dashboard").as("dash");
cy.visit("/dashboard");
cy.wait("@dash"); // waits for actual response
vs the antipattern:
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:
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 → To | Highlights |
|---|---|
| 9 → 10 | Major redesign. Folder restructure (integration/ → e2e/). New cypress.config.js replaces cypress.json. |
| 10 → 11 | Component testing GA. New iframe-isolation model. |
| 11 → 12 | cy.origin() for cross-domain. Session-caching default. Test isolation enforced. |
| 12 → 13 | Node 16+. Performance improvements; new screenshot/video pipeline. |
| 13 → 14 | Node 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
CYPRESS_RECORD_KEYis a write credential. Anyone with it can submit runs to your Cypress Cloud org. Treat like an API key — never log, never commit.- Video and screenshot artefacts can leak data. Test recordings include PII, tokens shown in URLs, and form values. Strip uploads before public CI sharing.
cy.taskruns arbitrary Node code. Tasks declared incypress.config.jsexecute server-side with full process permissions; treat them like prod code.- 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
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
# CI overrides
CYPRESS_baseUrl=https://staging.example.com npx cypress run
Output:
(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 beforecy.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, notcy.wait(ms). Usecy.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 incy.origin('https://other.example.com', () => { ... }).
Ecosystem integrations
| Tool | What it adds |
|---|---|
@cypress/code-coverage | Wire Istanbul coverage from instrumented app code |
cypress-axe | Accessibility checks |
cypress-image-snapshot | Visual regression |
start-server-and-test | CI server lifecycle wrapper |
@testing-library/cypress | RTL queries inside chainables |
cypress-grep / @cypress/grep | Tag-based test filtering |
cypress-real-events | Real pointer/keyboard events |
cypress-recurse | Polling 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.interceptfor 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
- JavaScript: cypress — CLI flags, config patterns, custom commands
- Packages: npm-playwright — the modern alternative
- Packages: npm-jest — unit-level companion
- Packages: npm-vitest — modern unit-test runner
- Concept: HTTP — request/response model behind
cy.intercept