Use this skill when working with Cypress tests.
Requires
- test-automation-guidelines
See Also
test-accessibility— apply when the suite should include automated a11y scanning (cypress-axe) or when reviewing/writing accessible selector strategy.
Cypress Rules
Command Model
- Use Cypress command chaining
- Do not use async/await
- Avoid excessive use of
cy.then()
Selectors
Prefer stable, intentional selectors (attributes or accessibility hooks) as your primary choice. Text-based selectors are brittle — they can break with copy changes, translations, or small wording edits — so treat them as a fallback only when no stable attributes exist.
Primary (preferred):
cy.get('[data-testid="..."]');
cy.get("#elementId");
Fallback (use only if no stable attributes are available):
cy.contains("Submit"); // text selector - brittle
Waiting
Do not use:
cy.wait(1000);
Use:
cy.wait('@alias').should(...)- DOM assertions
Network Control
- Use
cy.intercept()for spying and stubbing - Prefer stubbing for deterministic tests
- Always use aliases for intercepted requests
cy.intercept("GET", "/api/...", { fixture: "user.json" }).as("getUsers");
cy.wait("@getUsers");
Authentication
- Prefer programmatic login
- Use
cy.session() - Avoid UI login in every test
cy.session("user", () => {
cy.login();
});
State Management
- Use
Cypress.envfor configuration and static data - Do not use
Cypress.envto share mutable state between tests - Reset cookies/localStorage when required
Assertions
cy.get('[data-testid="cart-count"]').should("have.text", "1");
Custom Commands & Fixtures
- Use custom commands sparingly for repetitive actions
- Use
cy.fixture()to manage large data sets
API Waiting Pattern
cy.intercept("POST", "/checkout").as("checkout");
cy.wait("@checkout");
Anti-Patterns
- Mixing async/await with Cypress
- Overusing
cy.then() - UI login in every test