cypress-testing
Authors and improves Cypress E2E tests - installs Cypress, configures `cypress.config.ts`, authors `cy.*` command chains, refactors existing specs (`cy.wait(ms)` sleeps into assertions, repeated flows into `cy.session` custom commands), and debugs with the time-travel GUI; Cypress Cloud for parallel runs and recording. Use for both greenfield test authoring and improving hand-written specs already in the codebase. For automated refactor of raw Cypress Studio recordings specifically, see cypress-codegen-reviewer.
cypress-testing
Overview
Per cy-overview:
"Cypress is described as 'a next generation front end testing tool built for the modern web.'"
Differentiators (cy-overview):
When to use
For cross-browser including WebKit, see playwright-testing.
Step 1 - Install
npm install --save-dev cypress
npx cypress open # first run scaffolds the projectThe interactive setup creates cypress.config.ts + cypress/ directory.
Step 2 - Configure
// cypress.config.ts
import { defineConfig } from 'cypress';
export default defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
specPattern: 'cypress/e2e/**/*.cy.{ts,tsx}',
video: true,
screenshotOnRunFailure: true,
retries: { runMode: 2, openMode: 0 },
},
component: {
devServer: {
framework: 'react',
bundler: 'vite',
},
},
});Step 3 - Author E2E tests
// cypress/e2e/checkout.cy.ts
describe('Checkout flow', () => {
beforeEach(() => {
cy.visit('/login');
cy.get('[data-testid="email"]').type('user@example.com');
cy.get('[data-testid="password"]').type('test-password');
cy.get('[data-testid="signin-btn"]').click();
cy.contains('Welcome').should('be.visible');
});
it('completes checkout end-to-end', () => {
cy.visit('/products/BOOK-001');
cy.contains('button', /add to cart/i).click();
cy.get('[data-testid="cart-count"]').should('have.text', '1');
cy.visit('/checkout');
cy.get('[name="card"]').type('4242 4242 4242 4242');
cy.contains('button', /place order/i).click();
cy.contains('Order confirmed', { timeout: 10000 }).should('be.visible');
});
});Per cy-overview, assertions auto-wait - no cy.wait(2000) needed.
Step 4 - Use cypress-testing-library
For accessibility-first selectors (per the team's e2e-selector-quality-critic convention):
npm install --save-dev @testing-library/cypress// cypress/support/commands.ts
import '@testing-library/cypress/add-commands';
// Now in tests:
cy.findByRole('button', { name: /sign in/i }).click();
cy.findByLabelText('Email').type('user@example.com');findByRole (from cypress-testing-library) is the Cypress equivalent of Playwright's getByRole - preferred for accessibility-aware testing.
Step 5 - Custom commands
// cypress/support/commands.ts
declare global {
namespace Cypress {
interface Chainable {
login(email: string, password: string): Chainable<void>;
}
}
}
Cypress.Commands.add('login', (email, password) => {
cy.session([email, password], () => {
cy.visit('/login');
cy.findByLabelText('Email').type(email);
cy.findByLabelText('Password').type(password);
cy.findByRole('button', { name: /sign in/i }).click();
cy.url().should('not.include', '/login');
});
});
// Usage in tests:
beforeEach(() => {
cy.login('user@example.com', 'pwd');
});cy.session(...) caches the auth state across tests - reuse the login result, avoid re-running the login flow.
Step 6 - Run
# Open the GUI (interactive; great for development)
npx cypress open
# Headless (CI)
npx cypress run
# Single spec
npx cypress run --spec "cypress/e2e/checkout.cy.ts"
# Specific browser
npx cypress run --browser firefox
npx cypress run --browser chromeStep 7 - Time-travel debugger
Per cy-overview: "Hover over commands in the Command Log to see exactly what happened at each step."
In cypress open mode:
This is Cypress's killer feature - debugging by visually replaying the test.
Step 8 - Cypress Cloud (paid; optional)
# Record run to Cypress Cloud
npx cypress run --record --key <CYPRESS_RECORD_KEY>
# Parallel
npx cypress run --record --parallelCloud provides:
OSS alternative: currents-integration covers similar analytics for both Cypress + Playwright.
Step 9 - CI integration
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: cypress-io/github-action@v6
with:
start: npm start
wait-on: 'http://localhost:3000'
browser: chrome
record: true
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
- uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-screenshots
path: cypress/screenshotsAnti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
cy.wait(2000) between actions | Defeats Cypress's auto-wait; flaky. | Trust assertions; chain commands. |
cy.get('.button-class') (CSS class) | Brittle; defeats findByRole patterns. | cypress-testing-library + data-testid (Steps 3-4). |
| Cross-test state via global variables | Tests order-dependent. | cy.session() for auth; per-test fresh state. |
| Mixing Cypress + plain xUnit assertions | Confusing; two assertion styles. | Cypress chains throughout. |
| Running Cypress against production | Cypress can mutate state; pollutes prod data. | Local / staging only. |