On this page14 sections
Cypress + TypeScript E2E Automation
A production-structured Cypress + TypeScript project with custom commands, intercept-based network mocking, fixture files, and a GitHub Actions pipeline.
Repository
View repository ↗Overview
This project showcases a well-structured Cypress + TypeScript test suite against Sauce Demo (saucedemo.com), a static e-commerce practice site. It demonstrates the Cypress-idiomatic approach to test organisation: typed custom commands that wrap multi-step UI flows, fixture files for test data, and a Page Object-light structure for locator management. The CI pipeline validates every push using the Cypress GitHub Action and uploads Mochawesome HTML reports as artifacts.
Project goals
- ›Build a custom command library that abstracts repetitive UI interactions (login, addToCart, checkout) into single-line calls
- ›Demonstrate cy.intercept() usage by stubbing any third-party analytics or external scripts (the practice site is static and has no backend API to mock)
- ›Organise fixture files for user accounts, product data, and order payloads to separate test data from test logic
- ›Implement TypeScript strictly: type the custom commands in cypress/support/commands.ts and extend Cypress.Chainable
- ›Run the full suite in parallel in CI and merge results into a single Mochawesome report
- ›Pass TSC with strict mode — zero any, no implicit returns
Architecture
Custom Commands + Fixture-Driven Data
Tests call high-level custom commands that encapsulate multi-step UI sequences. Fixture files supply all test data, keeping test files focused on assertions. A small Page Object-light layer in cypress/pages/ owns the locators per page so test files stay focused on user-flow assertions.
cypress/e2e/— Test specs (.cy.ts); one file per feature or user journeycypress/support/— commands.ts (custom commands) and e2e.ts (global hooks)cypress/fixtures/— JSON fixture files: users.json, products.json, orders.jsoncypress/pages/— Optional Page Objects for complex multi-step flowscypress.config.ts— Base URL, specPattern, video/screenshot settings, env varsPrerequisites
- ✓Node.js 18 or later
- ✓npm 9 or later
- ✓Git
- ✓Chrome (or any Chromium-based browser) for local headed runs
Folder structure
cypress.config.ts # Root Cypress configuration: baseUrl, specPattern, viewportSize, env
cypress.env.json # Local-only secrets (gitignored); CI uses GitHub Actions env vars
cypress.env.json.example # Checked-in template showing required env var keys
cypress/e2e/ # All test specs (.cy.ts) — one file per feature
cypress/e2e/auth/login.cy.ts # Login: happy path, wrong password, lockout, session persistence
cypress/e2e/shop/product-listing.cy.ts # Sort and filter on the product catalogue
cypress/e2e/shop/checkout.cy.ts # Full UI checkout flow: add to cart, fill info, complete order
cypress/support/commands.ts # Custom commands: cy.login(), cy.addToCart(), cy.fillCheckout()
cypress/support/e2e.ts # Global hooks (beforeEach screenshot reset) and command import
cypress/fixtures/users.json # Test user accounts (standard, locked, admin)
cypress/fixtures/products.json # Product payloads for intercept stubs
cypress/fixtures/orders.json # Expected order confirmation data for assertion
tsconfig.json # TypeScript config — extends the base, adds Cypress type references
.github/workflows/cypress.yml # CI workflow: install, run, upload artifactsSetup & run
Installation
- 1.
Clone the repository: git clone <repo-url> && cd cypress-typescript - 2.
Install dependencies: npm install - 3.
Copy environment config: cp cypress.env.json.example cypress.env.json - 4.
Set baseUrl and credentials in cypress.env.json - 5.
Verify Cypress opens correctly: npx cypress open
Commands
Open Cypress Test Runner (interactive)
npx cypress openOpens the GUI test runner — choose E2E Testing, pick a browser, then click a spec
Run all tests headlessly (CI mode)
npx cypress runRuns all specs headlessly in Electron; generates video and screenshots on failure
Run tests in Chrome headlessly
npx cypress run --browser chromeRun a single spec file
npx cypress run --spec 'cypress/e2e/checkout.cy.ts'Run with environment variable override
npx cypress run --env baseUrl=https://staging.example.comGenerate and open Mochawesome HTML report
npm run reportMerges individual JSON results and opens the combined HTML report
Environment
| Variable | Description | Example | Required |
|---|---|---|---|
BASE_URL | Root URL of the application under test | https://www.saucedemo.com | Yes |
TEST_USER_USERNAME | Username of the standard test account | standard_user | Yes |
TEST_PASSWORD | Password of the standard test account | secret_sauce | Yes |
CYPRESS_RECORD_KEY | Cypress Dashboard record key (optional — only needed if recording to Cypress Cloud) | — | No |
Test data strategy
- ›JSON fixture files supply all user, product, and order data — tests never hardcode values inline
- ›cy.intercept() is configured to stub any third-party analytics or external scripts so tests aren't slowed by them; the practice site itself is static and has no API to mock
- ›cy.login() is a UI-level custom command that performs a fresh login per test — Sauce Demo's auth state lives in sessionStorage (per-test scope), so per-test login is the simplest correct approach
- ›Unique email addresses for sign-up tests use `Date.now()` suffix to prevent conflicts in parallel runs
- ›Fixture data is versioned alongside test code so tests and data always move together
Reporting
- ›Cypress records video of every headless run; failing tests also generate a screenshot at the point of failure
- ›Mochawesome reporter is configured to output per-spec JSON files; `npm run report` merges them into a single HTML report
- ›GitHub Actions uploads the merged report and the videos directory as artifacts on every run
- ›Test run summary (pass/fail counts, duration) is printed to stdout by the default `spec` reporter for quick CI feedback
CI/CD
- ›.github/workflows/cypress.yml triggers on push to main and on pull_request events
- ›uses: cypress-io/github-action@v6 handles install, browser setup, and run in one step
- ›BASE_URL, TEST_USER_USERNAME, and TEST_USER_PASSWORD are injected as GitHub Actions secrets and exposed as env vars
- ›Mochawesome reports and video artifacts are uploaded with actions/upload-artifact@v4
- ›The workflow fails fast: if any spec fails, the workflow reports failure and does not proceed to deployment
Common issues
cy.get() returns detached or stale element after XHR completes
Cause: The DOM re-rendered after the intercept resolved, invalidating the chained reference
Fix: Re-query the element after the intercept wait: use `cy.wait('@alias').then(() => cy.get(selector))`
TypeScript error: Property 'login' does not exist on type Chainable
Cause: Custom command is defined in commands.ts but the type declaration is missing
Fix: Add a `declare namespace Cypress { interface Chainable { login(email: string, password: string): void } }` block to commands.ts
Intercept stub is not matched — real API is called instead
Cause: The intercept URL pattern does not match (query string, trailing slash, or protocol mismatch)
Fix: Use a glob pattern: `cy.intercept('POST', '**/api/checkout**', { fixture: 'orders' })` and inspect the network tab to confirm the URL
Tests pass locally but fail in CI with 'Timed out retrying after 4000ms'
Cause: CI environment is slower; default command timeout is too low for slower network/render
Fix: Increase `defaultCommandTimeout` in cypress.config.ts to 8000–10000ms for CI, or use `cy.get(selector, { timeout: 8000 })`
Best practices
- ✓Use `data-testid` attributes for locators that must survive visual redesigns; document them in a locator registry comment
- ✓Keep cy.intercept() stubs in the test file they support, not in global support hooks, so the test is self-documenting
- ✓Avoid cy.wait(number) for arbitrary delays; use cy.wait('@alias') with a named intercept instead
- ✓One spec file per user journey — avoid mega-spec files that test 10+ flows and take 5 minutes to run
- ✓Use fixture files for all test data; never hardcode user credentials or product IDs in test code
- ✓Run `npx tsc --noEmit` in CI before the Cypress step to catch type errors early
Next steps
- →Add component tests using Cypress Component Testing for React or Vue components that are difficult to test at the E2E level
- →Integrate with cypress-axe to run accessibility audits on each page during the test run
- →Set up Cypress Cloud to enable parallelisation across multiple CI machines and get historical analytics
- →Add API-only tests using cy.request() to cover the backend contract without launching a browser
- →Extend the custom command library to support roles (admin vs standard user) using session-scoped login