End-to-End Tests (Cypress)
Detection
Run in parallel:
- Check
package.jsonforcypressversion - Read
cypress.config.tsfor baseUrl and test file patterns - Read 1-2 existing test files in
cypress/e2e/to match conventions
Workflow
- Read existing tests and config to match project style
- Understand the user flow — ask if unclear
- Identify selectors (see selectors.md)
- Write tests in
cypress/e2e/— one file per flow or feature
Format
describe('login flow', () => {
beforeEach(() => {
cy.visit('/login')
})
it('logs in with valid credentials', () => {
cy.findByLabelText('Email').type('user@example.com')
cy.findByLabelText('Password').type('password')
cy.findByRole('button', { name: 'Sign in' }).click()
cy.url().should('include', '/dashboard')
cy.findByRole('heading', { name: 'Dashboard' }).should('be.visible')
})
it('shows error with invalid credentials', () => {
cy.findByLabelText('Email').type('wrong@example.com')
cy.findByLabelText('Password').type('wrong')
cy.findByRole('button', { name: 'Sign in' }).click()
cy.findByRole('alert').should('contain.text', 'Invalid credentials')
})
})
Selector priority
Prefer @testing-library/cypress commands when installed:
cy.findByRole('button', { name: 'Submit' })— bestcy.findByLabelText('Email')— formscy.findByText('Welcome')— text contentcy.get('[data-testid="submit"]')— last resort
See selectors.md for full guide.
Rules
- Use
@testing-library/cypressselectors when available, elsecy.get - Use
data-testidonly as last resort - One logical outcome per
itblock - Test behavior, not implementation details
- Never use
cy.wait(<number>)— usecy.findBy*auto-retry instead
Error Handling
- If
cypressis not inpackage.json→ stop and ask user to install Cypress first - If
cypress.config.tsis missing → ask user to runnpx cypress opento initialize config - If
baseUrlis unreachable → verify the dev server is running before writing tests that require it