Install
$ agentstack add skill-0xdarkmatter-claude-mods-cypress-ops ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Cypress Operations
Version context (verified against docs.cypress.io, 2026-06): Cypress 14.x, Test Replay (v13+), cy.session with cacheAcrossSpecs. APIs move — confirm against the live docs when a detail is load-bearing.
End-to-end and component testing with Cypress (cypress, TS/JS). The runner executes tests inside a real browser via the Cypress App (cypress open) or headlessly (cypress run). The defining mental model: cy.* commands are not promises — they enqueue onto an async command chain that Cypress drains for you. Internalise that and the agentic gotchas below disappear.
Quick Start
npm install -D cypress
npx cypress open # launch the Cypress App: pick E2E or Component, real browser
npx cypress run # headless run, all specs (CI default)
npx cypress run --spec "cypress/e2e/auth/*.cy.ts"
npx cypress run --component # run component specs
npx cypress run --browser chrome --headed
npx cypress run --record --key # upload to Cypress Cloud (enables Test Replay, v13+)
Specs live in cypress/e2e/**/*.cy.ts (E2E) and beside components or cypress/component/ (component). Config is a single cypress.config.ts at the repo root.
The Async Command Queue (read this first)
cy.get(...) returns a Chainer, not the element and not a Promise. Commands are scheduled, then run in order after the test function returns. This is the source of nearly every Cypress mistake an agent makes.
// WRONG — cy.get does not return a value; `el` is a Chainer, this is meaningless
const el = cy.get('[data-test=total]');
if (el.text() === '$0') { /* never works */ }
// WRONG — async/await does nothing useful; cy commands aren't awaitable promises
const text = await cy.get('[data-test=total]'); // do NOT do this
// RIGHT — yield the value into a callback; assertions inside .should() retry
cy.get('[data-test=total]').should('have.text', '$0');
// RIGHT — need the raw value? use .then() (but it does NOT retry — see below)
cy.get('[data-test=total]').invoke('text').then((text) => {
// text is a string here; runs after the queue reaches this point
});
Rules that follow from this:
- No
const/letto "store" a command result. Use.as()aliases +cy.get('@alias'). - No
async/awaitoncy.*. The queue handles ordering. Mixing in real promises?
wrap them with cy.then(() => promise) or cy.wrap(promise).
- No
if/elseon element state read synchronously. Conditional testing is an
anti-pattern in Cypress (the DOM may not have settled); make the app deterministic, or drive the branch off a server/cy.intercept state you control. Deep dive: [references/network-and-auth.md](references/network-and-auth.md).
Retry-ability (why you almost never need waits)
Cypress retries queries and assertions until they pass or the command times out (default 4s). It does not retry actions (.click(), .type(), .select()) — those fire once, though the queries leading up to them retry until the element is actionable (visible, not disabled, not animating).
| Construct | Retries? | Use for | |-----------|----------|---------| | cy.get / .find / .contains / .its / .invoke (queries) | Yes — whole chain re-queries | Locating/reading DOM that may not be ready | | .should(...) / expect inside it | Yes — the callback re-runs | Assertions; conditional waits on settled state | | .click / .type / .select (actions) | No — fire once | Interactions (leading queries still retry) | | .then(cb) | No — runs once, no retry protection | Extracting a value; NOT for assertions |
// .should(callback) retries the whole callback — safe for racy DOM
cy.get('[data-test=rows] li').should(($li) => {
expect($li).to.have.length(3);
expect($li.first()).to.contain('Alice');
});
// .then() does NOT retry — capturing $el here then asserting later races the render
If you reach for cy.wait(3000), you're missing an assertion or an aliased intercept. The only legitimate cy.wait takes an alias (cy.wait('@getUsers')), never a number.
Selector Strategy
Prefer a dedicated test attribute over CSS classes, IDs, or tag names — the latter are brittle and change with styling/refactors. Cypress recommends data-cy or data-test (the Cypress Real World App standardises on data-test); pick one and enforce it.
// GOOD — decoupled from styling and structure
cy.get('[data-test=submit]').click();
// AVOID — couples the test to CSS/markup that changes for non-test reasons
cy.get('.btn-primary').click();
cy.get('#submit').click();
Wrap the convention in a custom command so specs stay terse:
// cypress/support/commands.ts
Cypress.Commands.add('getBySel', (sel, ...args) =>
cy.get(`[data-test=${sel}]`, ...args));
Cypress.Commands.add('getBySelLike', (sel, ...args) =>
cy.get(`[data-test*=${sel}]`, ...args)); // substring match
// usage: cy.getBySel('submit').click();
Reserve cy.contains('Log In') for when the visible text itself is what you're asserting; otherwise it couples tests to copy.
Network Stubbing — cy.intercept
cy.intercept is the single API for spying on and stubbing network traffic. Set it up before the action that triggers the request, alias it, then wait on the alias.
// Stub with a fixture, alias, wait
cy.intercept('GET', '/api/users', { fixture: 'users.json' }).as('getUsers');
cy.visit('/users');
cy.wait('@getUsers'); // resolves when the request fires
// Inline body / status
cy.intercept('POST', '/api/login', { statusCode: 401, body: { error: 'nope' } }).as('login');
// routeMatcher object (method + glob/regex url) + dynamic reply
cy.intercept({ method: 'GET', url: '/api/orders/*' }, (req) => {
req.reply((res) => { res.body.hasMore = false; }); // tweak the real response
}).as('orders');
// Assert against the captured request/response
cy.wait('@login').its('response.statusCode').should('eq', 401);
// Wait on several at once
cy.wait(['@getUsers', '@orders']);
Stub what you don't own, exercise what you do. Stubbing third-party/slow endpoints makes tests fast and deterministic; hitting your real backend (seeded via cy.request) verifies the client↔server contract. Decide per endpoint. GraphQL, request modification, and seed-via-cy.request patterns: [references/network-and-auth.md](references/network-and-auth.md).
Authentication — cy.session
Log in once, cache the session, restore it across tests (and optionally specs). This is the biggest suite-speed win after stubbing.
// cypress/support/commands.ts
Cypress.Commands.add('login', (username: string, password: string) => {
cy.session(
[username, password], // cache key — array/object is stringified
() => { // setup: runs only on cache miss
cy.visit('/login');
cy.get('[data-test=name]').type(username);
cy.get('[data-test=password]').type(password);
cy.get('form').contains('Log In').click();
cy.url().should('contain', '/dashboard'); // assert logged-in before caching!
},
{
validate() { // runs after setup AND after each restore
cy.getCookie('auth_token').should('exist'); // invalid -> setup re-runs
},
cacheAcrossSpecs: true, // default false; true = reuse in every spec
},
);
});
Critical behaviour: cookies, localStorage, and sessionStorage across all domains are cleared before setup runs, regardless of testIsolation. Faster still: skip the UI and log in via cy.request inside setup, persisting the token. Patterns (API login, token priming, cy.origin for cross-origin SSO): [references/network-and-auth.md](references/network-and-auth.md).
Component vs E2E Testing
Same runner, two testing types. E2E drives a deployed app through cy.visit. Component mounts a single component in a real browser via cy.mount — no server, no navigation, props/events under direct control.
| | E2E | Component | |---|---|---| | Entry | cy.visit('/path') | cy.mount() | | Needs running app server | Yes | No (bundler dev server only) | | Spec location | cypress/e2e/**/*.cy.ts | beside the component / cypress/component/ | | Support file | cypress/support/e2e.ts | cypress/support/component.ts (registers cy.mount) | | Best for | User flows, integration, auth | Props/events/slots, edge states, visual |
// cypress/support/component.ts (React example)
import { mount } from 'cypress/react';
Cypress.Commands.add('mount', mount);
// Button.cy.tsx
cy.mount();
cy.get('[data-test=button]').click();
cy.get('@onClick').should('have.been.calledOnce');
Frameworks: React 18–19, Vue 3, Angular 18–21, Svelte 5. Bundlers: Vite 5–8 (React/Vue/ Svelte) or webpack 5 (all + Next.js). Configured under component.devServer.{framework,bundler}. Mounting per framework, store/router mocking, slots: [references/component-testing.md](references/component-testing.md).
Test Isolation, Fixtures, Custom Commands
testIsolation: true(default, E2E) clears cookies/storage and resets toabout:blank
before each test. Each test must pass run in isolation (it.only to verify) — never rely on a previous test's state. Reset server-side state in beforeEach, not afterEach (an after hook may not run if you refresh mid-test).
- Multiple assertions per test are fine — don't split into one-assertion tests; state
reset between tests costs more than extra assertions.
- Fixtures are static JSON in
cypress/fixtures/, loaded bycy.fixture('users.json')
or referenced directly in cy.intercept(..., { fixture: 'users.json' }).
- Custom commands (
Cypress.Commands.add) live incypress/support/commands.ts; add
the cypress/react (etc.) types and a declare global block for TS autocomplete.
CI
# GitHub Actions — the official cypress-io/github-action handles install + cache + run
- uses: actions/checkout@v5
- uses: cypress-io/github-action@v6
with:
build: npm run build
start: npm start # boots app, waits on baseUrl before running
wait-on: 'http://localhost:3000'
browser: chrome
record: true # upload to Cypress Cloud (Test Replay)
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
| Decision | Guidance | |----------|----------| | Start the app | Start it before Cypress (start + wait-on), kill after — never cy.exec a server mid-test | | Parallelism | cypress run --record --parallel splits specs across machines — requires Cypress Cloud (paid). Free alternative: shard specs manually across matrix jobs with --spec | | Retries | Config retries: { runMode: 2, openMode: 0 } — surface flakes as a queue, don't paper over them | | Debugging CI failures | Test Replay (v13+, Chromium-only) over video: captures DOM, network, console, errors for time-travel debugging in Cloud |
Full workflows (matrix sharding, containers, artifact upload): [references/ci-and-flake.md](references/ci-and-flake.md).
Flake Diagnosis
Most Cypress flake traces to one of: an action chained where a query/assertion belonged, a missing aliased cy.wait, conditional logic on un-settled DOM, or leaked state between tests.
| Symptom | Likely cause | Fix | |---------|-------------|-----| | "element detached from DOM" | re-render between query and action | split the chain; let the action's leading query retry | | passes alone, fails in suite | inter-test state coupling | reset server state in beforeEach; it.only to confirm | | cy.wait(number) "fixes" it | racing the network | replace with cy.intercept(...).as() + cy.wait('@alias') | | value read with .then() is stale | .then doesn't retry | move the assertion into .should(cb) |
Diagnosis tooling (Test Replay, cypress run --headed, time-travel in the App, screenshots/ video), retry config, and a systematic playbook: [references/ci-and-flake.md](references/ci-and-flake.md).
Cypress vs Playwright (one-table decision)
| Factor | Cypress | Playwright | |--------|---------|-----------| | Execution model | In-browser, async command queue (no await) | Out-of-process, real async/await | | Browsers | Chrome-family, Firefox, Electron; WebKit experimental | Chromium, Firefox, WebKit (real Safari) | | Parallelism | Cypress Cloud (paid) or manual sharding | Free, built-in, shardable | | Multi-tab / multi-origin | Constrained (cy.origin for cross-origin) | Native | | Component testing | Mature, first-class | Experimental | | Interactive DX | The original benchmark (Cypress App, time-travel) | UI mode (excellent) | | API testing | cy.request / cy.intercept | Built-in request context |
Reach for Cypress when component-testing maturity, an existing Cypress investment, or its in-browser DX dominate. Default to Playwright for new E2E needing WebKit, free parallelism, or heavy multi-tab/multi-origin work. Sibling skill: playwright-ops.
Config Skeleton
Full commented production template: [assets/cypress.config.template.ts](assets/cypress.config.template.ts)
import { defineConfig } from 'cypress';
export default defineConfig({
e2e: {
baseUrl: 'http://localhost:3000', // cy.visit('/path') resolves against this
specPattern: 'cypress/e2e/**/*.cy.{ts,tsx}',
retries: { runMode: 2, openMode: 0 }, // retry in CI only
setupNodeEvents(on, config) { return config; },
},
component: {
devServer: { framework: 'react', bundler: 'vite' },
},
// testIsolation defaults true; viewportWidth/Height, defaultCommandTimeout tunable here
});
References
| File | Contents | |------|----------| | [references/network-and-auth.md](references/network-and-auth.md) | cy.intercept matching/modifying/GraphQL, cy.session deep dive, API login, cy.origin, seed-via-request | | [references/component-testing.md](references/component-testing.md) | Per-framework cy.mount, store/router/context mocking, slots/events, Vite vs webpack config | | [references/ci-and-flake.md](references/ci-and-flake.md) | Full GH Actions workflows, sharding, Test Replay, retry config, systematic flake playbook | | [assets/cypress.config.template.ts](assets/cypress.config.template.ts) | Commented production config template (E2E + component) |
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: 0xDarkMatter
- Source: 0xDarkMatter/claude-mods
- License: MIT
- Homepage: https://github.com/0xDarkMatter/claude-mods#readme
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.