If you’ve ever started a new automation project, you know the first few weeks feel magical — everything works locally, your first few tests pass, and you think, “This is easy.”
But fast forward a few months, and things start to fall apart:
⚙️ Inconsistent results.
🧩 Messy folder structures.
🐛 Flaky tests.
📉 CI pipeline chaos.
This happens because most teams treat Cypress as a tool, not a framework.
Let’s change that.
In this guide, we’ll go end-to-end — from project setup to a scalable, CI-ready Cypress framework with TypeScript, linting, reporting, and pre-commit hooks.
Step 1: Initialize the Project
Start from a clean workspace.
mkdir cypress-framework
cd cypress-framework
npm init -y
npm install cypress --save-dev
npx cypress open
This creates the default structure:
cypress/
├── e2e/
├── fixtures/
├── support/
cypress.config.js
Run your first sample test to make sure Cypress boots up correctly.
Step 2: Add TypeScript Support
Cypress supports TypeScript out of the box, but we’ll set it up properly for scalability.
npm install typescript ts-node @types/node --save-dev
Create a tsconfig.json:
{
"compilerOptions": {
"target": "es2018",
"lib": ["es2018", "dom"],
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"types": ["cypress"]
},
"include": ["cypress/**/*.ts"]
}Rename your .js files under /e2e and /support to .ts.
✅ Benefit: Strong typing for commands, utilities, and fixtures.
✅ Prevents silly runtime errors.
Step 3: Linting + Code Quality
Good frameworks enforce standards.
npm install eslint eslint-plugin-cypress --save-dev
Create .eslintrc.js:
module.exports = {
env: {
browser: true,
es2021: true,
'cypress/globals': true,
},
extends: ['eslint:recommended', 'plugin:cypress/recommended'],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['cypress'],
rules: {
semi: ['error', 'always'],
quotes: ['error', 'single'],
},
};Run lint:
npx eslint cypress/**/*.ts
Step 4: Pre-Commit Hooks (Prevent Bad Commits)
Install Husky and lint-staged to enforce quality before every git commit.
npm install husky lint-staged --save-dev
npx husky install
npm set-script prepare "husky install"
Add a pre-commit hook:
npx husky add .husky/pre-commit "npx lint-staged"
Now configure lint-staged in your package.json:
"lint-staged": {
"*.ts": "eslint --fix"
}✅ Ensures no one pushes broken code.
✅ Keeps your repo clean and consistent.
Step 5: Framework Folder Structure
Let’s make it maintainable.
cypress/
├── e2e/
│ ├── auth/
│ │ ├── login.cy.ts
│ │ ├── signup.cy.ts
│ ├── dashboard/
│ ├── analytics.cy.ts
│ ├── reports.cy.ts
│
├── support/
│ ├── commands/
│ │ ├── login.ts
│ │ ├── api.ts
│ ├── utils/
│ │ ├── dateHelper.ts
│ │ ├── intercepts.ts
│ ├── e2e.ts
│
├── fixtures/
│ ├── users.json
│ ├── products.json
│
├── config/
│ ├── dev.json
│ ├── staging.json
│ ├── prod.json
│
├── plugins/
│ ├── index.ts
│
├── reports/
├── screenshots/
├── videos/
✅ Domain-based separation
✅ Easy scalability
✅ Clear ownership
Step 6: Reporting Integration (Allure / Mochawesome)
Example using Mochawesome:
npm install mochawesome mochawesome-merge mochawesome-report-generator --save-dev
Add reporter to config:
// cypress.config.ts
import { defineConfig } from 'cypress'
export default defineConfig({
e2e: {
reporter: 'mochawesome',
reporterOptions: {
reportDir: 'cypress/reports',
overwrite: false,
html: false,
json: true,
},
},
});
Merge + generate report:
npx mochawesome-merge cypress/reports/*.json > cypress/reports/output.json
npx marge cypress/reports/output.json
Boom 💥 — a clean HTML report for management.
Step 7: Parallel Execution in CI/CD
Use GitHub Actions or GitLab CI to parallelize tests by folders:
jobs:
cypress-run:
runs-on: ubuntu-latest
strategy:
matrix:
group: [auth, dashboard, api]
steps:
- uses: actions/checkout@v3
- uses: cypress-io/github-action@v6
with:
spec: cypress/e2e/${{ matrix.group }}/**
record: true
✅ Parallel runs
✅ Auto artifacts
✅ Consistent pipelines
Bonus: Auto Retry Flaky Tests
npm install cypress-retries --save-dev
// cypress.config.ts
import 'cypress-retries';
// Example usage
it('should load dashboard', { retries: 2 }, () => {
cy.visit('/dashboard');
cy.contains('Analytics');
});
🧠 Final Thoughts
The difference between test scripts and a test framework is engineering.
If you want your automation to scale like your app does:
- Use TypeScript for type safety
- Use linting and hooks for quality
- Use proper reporting for visibility
- Design like a developer, test like an engineer
💬 Pro tip: Don’t aim for “tests that pass.”
Aim for a system that sustains — clean, maintainable, and production-ready.


