Skip to content

A complete, scalable Cypress testing framework showcasing best practices with Page Object Model, reusable element wrappers, step definitions, Allure reporting, ESLint + Prettier integration, Husky pre-commit hooks, environment configs, and clean CI-ready structure.

License

Notifications You must be signed in to change notification settings

pkorolyshyn/cypress-ultimate-architecture

Repository files navigation

Cypress Ultimate Architecture is a ready-to-use, production-level Cypress test automation framework designed to kick-start your next QA automation project. It provides a clean, scalable structure that you can easily customize and extend according to your project’s needs.

πŸš€ Quick Start

git clone https://github.com/pkorolyshyn/cypress-ultimate-architecture.git
cd cypress-ultimate-architecture
npm install
npx cypress open

Happy Testing! 😊

Why Use This Framework?

This framework gives you a head-start by providing a scalable and flexible testing foundation, built on widely used best practices. It's designed for real projectsβ€”whether you're running small personal apps or scaling enterprise applications with thousands of tests.

Instead of struggling to figure out "the best way," you’ll immediately benefit from a proven, maintainable Cypress setup.

✨ Features Included:

🧱Short Project Architecture Overview

This section explains how the framework is structured, what each folder or file is responsible for, and how it all connects. You can use this as a reference when modifying or extending the project.

ARCHITECTURE
β”‚
β”œβ”€β”€ .github/                 β†’ GitHub Actions workflows (CI, Allure, Pages)
β”œβ”€β”€ .husky/                  β†’ Git hooks for linting/formatting before commits
β”‚
β”œβ”€β”€ cypress/
β”‚   β”œβ”€β”€ components/          β†’ Reusable UI parts (e.g., dialogs, header)
β”‚   β”œβ”€β”€ core/                β†’ Base classes for POM structure
β”‚   β”‚   β”œβ”€β”€ BaseObject.js          β†’ Wrapper selector helpers
β”‚   β”‚   β”œβ”€β”€ BaseComponent.js       β†’ Shared base for components
β”‚   β”‚   └── BasePage.js            β†’ Abstract class for page objects
β”‚   β”œβ”€β”€ downloads/           β†’ Auto-generated downloaded files (e.g. CSV, PDF)
β”‚   β”œβ”€β”€ e2e/                 β†’ Actual Cypress test files
β”‚   β”œβ”€β”€ fixtures/            β†’ Static test data and style constants
β”‚   β”œβ”€β”€ pages/               β†’ Page objects (LoginPage, CalendarPage, etc.)
β”‚   β”œβ”€β”€ steps/               β†’ Reusable high-level test steps
β”‚   └── support/
β”‚       β”œβ”€β”€ elements/        β†’ Reusable element wrappers
β”‚       β”‚   β”œβ”€β”€ SingleElement.js
β”‚       β”‚   β”œβ”€β”€ InputElement.js
β”‚       β”‚   └── MultiElement.js
β”‚       β”œβ”€β”€ utils/           β†’ Test utilities
β”‚       β”‚   β”œβ”€β”€ DateUtils.js
β”‚       β”‚   └── AllureMetaUtils.js
β”‚       └── e2e.js           β†’ Global setup (Allure, grep, log cleanup)
β”‚
β”œβ”€β”€ cypress.env1.js          β†’ Environment-specific config (baseUrl, creds)
β”œβ”€β”€ cypress.env2.js
β”œβ”€β”€ .env                     β†’ Example local secrets (⚠️ Do not commit real data)
β”œβ”€β”€ Dockerfile               β†’ Run Cypress + Allure in a container
β”œβ”€β”€ eslint.config.js         β†’ Code quality rules (Flat config)
β”œβ”€β”€ .prettierrc              β†’ Code formatting rules
β”œβ”€β”€ package.json             β†’ Project scripts, dependencies, metadata
└── README.md                β†’ Project overview and documentation

.github/

Contains GitHub-specific configurations, including CI workflows like running Cypress tests, generating Allure reports, and publishing them to GitHub Pages.

This is where CI/CD magic happens.


.husky/

Git hook configurations using Husky. Runs automatic checks (like linting and formatting) before commits to keep the codebase clean.


cypress/components/

This folder contains reusable UI components used inside pages β€” like dialogs, modals, or headers. They’re composed inside pages/ to represent smaller parts of a full page.


cypress/core/

Core base classes shared across the framework.

  • BaseObject.js – Provides .select() and .selectInput() helpers to return wrapped Cypress elements.
  • BaseComponent.js – A minimal extension of BaseObject used for common logic component classes.
  • BasePage.js – The abstract class that all page objects inherit from. All common pages logic should be here.

cypress/e2e/

Your actual test files live here. Each test file corresponds to a feature or page and imports both:

  • Page objects (from pages/)
  • Step definitions (from steps/)

cypress/fixtures/

Static or shared test data. Can be used to store expected results, constants, styles, or mock API responses.


cypress/pages/

Contains Page Object classes. Each file represents a specific screen or route in the app, exposing elements and composed components for test use.

Examples:

  • LoginPage.js
  • CalendarPage.js
  • SmartTablePage.js

Each page extends BasePage and defines selectors + UI composition.


cypress/steps/

Reusable test steps organized by page or feature. They keep your test logic clean by hiding repetitive Cypress calls behind readable actions.


cypress/support/

Shared utilities and wrappers that enhance Cypress behavior.

elements/

Custom wrappers that add helpful assertion and action methods to elements.

  • SingleElement.js – Used for single DOM elements. Wraps Cypress methods.
  • InputElement.js – Extends SingleElement and adds input-specific helpers like .type() and .shouldHavePlaceholder().
  • MultiElement.js – Used for collections of elements (e.g., table rows). Wraps Cypress methods for working with multiple elements.

utils/

Project-wide helpers.

  • DateUtils.js – Formats or returns the current date.
  • AllureMetaUtils.js – A helper to inject Allure metadata like tags, severity, and ticket links for each test.

.env

βœ… This file is committed only as an example. You should not commit real passwords or secrets.

It's used to demonstrate how you can manage environment-specific variables (like login credentials) locally via dotenv.


cypress.env1.js / cypress.env2.js

Configuration files for switching between environments (env1, env2, etc). Define your baseUrl, test user credentials, and any custom env values for Cypress.

You can run a specific config with:

npx cypress run --env envName=env2

Dockerfile

Defines a complete containerized environment for running Cypress and generating Allure reports in CI. Includes:

  • Cypress with browsers
  • Java (for Allure)
  • All dependencies and commands to run tests + generate reports

eslint.config.js

Configures ESLint with:

  • Prettier integration
  • Best practice rules
  • Cypress-specific linting
  • CI-friendly restrictions (e.g., no it.only)

It helps maintain consistent, high-quality code across the framework.

πŸ“Š Allure Reporting

This project uses Allure to generate clean, interactive, and shareable test reports after every Cypress run.

  • After each CI test run, the framework generates an Allure HTML report.

  • The report is attached as a downloadable artifact inside the GitHub Actions pipeline.

  • The same report is also published to the gh-pages branch so it’s viewable online.

🌐 Allure Live Demo:

βš™οΈ How Allure is Configured

Configuration (in cypress.config.js):

import { allureCypress } from 'allure-cypress/reporter';
import os from 'node:os';

export default {
  e2e: {
    setupNodeEvents(on, config) {
      allureCypress(on, config, {
        resultsDir: 'allure-results',
        environmentInfo: {
          os_platform: os.platform(),
          os_release: os.release(),
          os_version: os.version(),
          node_version: process.version,
          env: envName,
        },
      });

      return config;
    },
  },
};

Enabling Allure in Cypress (in e2e.js):

import 'allure-cypress';

πŸ“ˆ Making Reports Meaningful with Metadata

Instead of plain test names, each test can include rich metadata like feature name, story, ticket, tags, and severity using the helper utility:

AllureMetaUtils.js β†’

This utility simplifies adding metadata to tests. You just pass an object and call .apply():

import AllureMeta from '../support/utils/AllureMetaUtils';

it('calendar should display the current date as selected', () => {
  new AllureMeta({
    feature: 'Calendar',
    story: 'Default selected date should be today',
    ticket: 'CAL-101',
    tags: ['calendar', 'ui', 'smoke'],
    severity: 'critical',
  }).apply();

  // Test steps...
});

πŸ–₯️ Generating Report Locally

After running Cypress tests locally, you can manually generate the Allure report with:

npx allure generate --single-file --clean -o allure-report allure-results

This command will create a single HTML file inside the allure-report directory.

⚠️ Note: You need to have Java version 8 or above installed on your system to generate the report.

πŸ“š Allure Installation Guide for Windows:

πŸ“ Adding a New Environment

This framework supports running tests across multiple environments (e.g. dev, staging, production) using dynamic configuration files.


How to Add a New Environment

  1. Create a new environment config file in the project root.

    Example: cypress.myEnv.js

  2. Populate it with environment-specific values:

// cypress.myEnv.js
import dotenv from 'dotenv';
dotenv.config();

export default {
  baseUrl: 'https://your-environment-url.com',
  env: {
    email: '[email protected]',
    password: process.env.MY_ENV_PASSWORD, // pulled from .env or GitHub Secret
    envName: 'myEnv',
    // Add any other custom environment variables here
  },
};
  1. Store sensitive credentials like passwords in the .env file locally (do not commit them), use GitHub Secrets CI:
# .env
MY_ENV_PASSWORD=yourStrongPasswordHere!
  1. Access these values anywhere in your test project using Cypress.env():
cy.get('input[type="email"]').type(Cypress.env('email'));

▢️ Run Tests on the New Environment

To execute tests using your new config:

npx cypress open --env envName=myEnv

Or run headlessly in CI:

npx cypress run --env envName=myEnv

The system will automatically load the correct baseUrl and env values from your cypress.myEnv.js file.

πŸ” Running Tests with Cypress Grep

This project uses @cypress/grep plugin to filter and run only the tests you care about β€” based on tags defined in your specs.

Each test (or suite) can be tagged using the tags object in the test declaration:

describe('Calendar behavior', { tags: '@calendar' }, () => {
  it('should display the current date as selected', { tags: '@smoke' }, () => {
    // ...
  });
});

βœ… Running Tagged Tests Locally

Use --env grepTags= to specify which tags you want to run.

Require Both Tags:

npx cypress run --env grepTags=@smoke+@calendar

Runs only tests that are tagged with both @smoke and @calendar.

OR Between Tags:

npx cypress run --env grepTags='@smoke @calendar'

Runs tests tagged with either @smoke or @calendar.

⚠️ Don’t forget quotes when using OR (space between tags).

Exclude Tags (Invert):

npx cypress run --env grepTags=-@calendar

Skips all tests tagged with @calendar.

Include and Exclude Tags:

npx cypress run --env grepTags=@smoke+-@calendar

Runs all @smoke tests excluding those also tagged with @calendar.


πŸš€ Running Tagged Tests in CI (GitHub Actions)

When triggering Cypress via GitHub Actions:

  1. Go to the project’s Actions tab.
  2. Choose the Cypress E2E Tests workflow.
  3. Click "Run workflow".
  4. In the "Cypress tags to run" input field, enter your desired grep tag(s) only.

βœ… Example:

@smoke+@calendar

No need to include --env or any other flags β€” just the grep string itself.

πŸ› οΈ Additional Features

πŸ”• Optional: Hide XHR Logs in Cypress Command Log

Cypress logs every XHR, fetch, and new URL request in the command log, which can sometimes make it hard to focus on what actually matters during debugging.

πŸ’‘ To reduce noise, this framework provides an easy toggle to hide XHR and request logs from the Cypress UI.


βœ… How It Works

In e2e.js, this block dynamically injects CSS to hide specific log entries:

if (Cypress.config('hideXHRInCommandLog')) {
  const app = window.top;
  const doc = app?.document;

  if (doc && !doc.head.querySelector('[data-hide-command-log-request]')) {
    const style = doc.createElement('style');
    style.setAttribute('data-hide-command-log-request', '');
    style.textContent = `
      .command-name-request,
      .command-name-xhr,
      .command-name-new-url {
        display: none !important;
      }
    `;
    doc.head.appendChild(style);
  }
}

πŸ§ͺ This injection only runs if the config flag is explicitly set.


βš™οΈ How to Enable

In your cypress.config.js, set the flag:

hideXHRInCommandLog: true;

πŸ” By default, this option is set to false so you'll see everything.

🧼 Set it to true if you want cleaner logs β€” especially useful for visual debugging.

πŸ“ Related Projects

πŸ› οΈ External Tools and Plugins

Framework


Reporting


CI/CD


Test Tags


Formatting

πŸ’› Contributing

Contributions are welcome! If you’d like to improve this framework, feel free to fork it, create a branch, and open a pull request.

Some ideas you could help with:

  • Implement Allure history

  • Any advanced documentation

  • Add more extensive tests for Dialog, Calendar, Echarts, Smart Table, or Auth

  • Add MS Teams chat reporting integration

  • Add more data-testid selectors to the E-commerce page: https://github.com/pkorolyshyn/testing-env1

  • Or anything you think will be suitable and useful

Note: Please avoid writing tests for the E-commerce section β€” it is meant as a playground for users to try the framework and write their own tests.

If you’re unsure how to start, feel free to open an issue or discussion.

Check the full contribution guidelines here: CONTRIBUTING.md

About

A complete, scalable Cypress testing framework showcasing best practices with Page Object Model, reusable element wrappers, step definitions, Allure reporting, ESLint + Prettier integration, Husky pre-commit hooks, environment configs, and clean CI-ready structure.

Topics

Resources

License

Stars

Watchers

Forks