Angular 18 and Cypress Code Coverage Setup Guide

This post describes how to setup Cypress code coverage in Angular 18

Pawan ghising

3/11/20252 min read

This document outlines the code coverage setup for an Angular 18 application using Cypress for E2E testing. This approach uses a manual coverage data collection method with newer Angular versions.

  • Prerequisites

    • Angular 18 project

    • Cypress has already installed and configured

    Installation

    Install the necessary packages:

    npm install -D @cypress/code-coverage nyc @istanbuljs/nyc-config-typescript rimraf

    Configuration Files

    1. Create a .nycrc file in the project root

    {
    "extends": "@istanbuljs/nyc-config-typescript",
    "all": true,
    "include": ["src/**/*.ts"],
    "exclude": [
    "src/test.ts",
    "src/**/*.spec.ts",
    "src/app/**/*.module.ts",
    "src/main.ts",
    "src/environments/**"
    ]
    }

    2. Update your Cypress support file (cypress/support/e2e.ts)

    Add the following to the end of your existing support file:

    // Import code coverage plugin
    import '@cypress/code-coverage/support';

    // Define type for window.__coverage__
    declare global {
    namespace Cypress {
    interface Window {
    coverage?: Record<string, unknown>;
    }
    }
    }

    // Define saveCoverage command
    Cypress.Commands.add('saveCoverage', () => {
    cy.window().then((win) => {
    const coverage = win.__coverage__;
    if (coverage) {
    cy.task('ensureDirectoryExists', '.nyc_output')
    .then(() => {
    cy.writeFile('.nyc_output/out.json', JSON.stringify(coverage));
    });
    }
    });
    });

    // Global afterEach hook to save coverage after each test
    afterEach(() => {
    cy.saveCoverage();
    });

    3. Update your Cypress configuration file (cypress.config.ts)

    import { defineConfig } from 'cypress';
    import registerCodeCoverageTasks from '@cypress/code-coverage/task';
    import * as fs from 'fs';

    export default defineConfig({
    e2e: {
    setupNodeEvents(on, config) {
    // Register code coverage tasks
    registerCodeCoverageTasks(on, config);

    // Add task to ensure directory exists
    on('task', {
    ensureDirectoryExists(dirPath) {
    if (!fs.existsSync(dirPath)) {
    fs.mkdirSync(dirPath, { recursive: true });
    }
    return null;
    }
    });

    return config;
    },
    // Your other Cypress configuration...
    },
    });

    4. Add scripts to your package.json

    "scripts": {
    "cypress:open": "cypress open",
    "cypress:run": "cypress run",
    "coverage:clean": "rimraf .nyc_output coverage",
    "coverage:report": "nyc report --reporter=lcov --reporter=text-summary",
    "coverage:html": "nyc report --reporter=html",
    "test:coverage": "npm run coverage:clean && cypress run && npm run coverage:report",
    "test:coverage:open": "npm run coverage:clean && cypress open"
    }

    Usage

    Normal Development

    For regular development, you can continue to use:

    ng serve

    This starts your Angular application without any coverage instrumentation.

    Running Tests with Coverage

    To run tests and generate coverage reports:

    npm run test:coverage

    This command:

    1. Cleans previous coverage data

    2. Runs Cypress tests headlessly

    3. Generates coverage reports

    Interactive Testing with Coverage

    To open the Cypress UI for interactive testing with coverage:

    npm run test:coverage:open

    After running your tests through the UI, generate reports with:

    npm run coverage:report

    Viewing Coverage Reports

    After generating reports:

    1. Text Summary: Displayed in the console after running coverage:report

    2. HTML Report:
      npm run coverage:html

      Then open coverage/index.html in your browser

    Cleaning Coverage Data

    To clean all coverage data:

    npm run coverage:clean

    How It Works

    1. The Cypress code coverage plugin instruments your Angular code during test runs

    2. Our custom saveCoverage command saves the coverage data to .nyc_output/out.json after each test

    3. NYC (Istanbul) processes this data to generate human-readable reports

    Troubleshooting

    No Coverage Data

    If .nyc_output directory is empty or not created:

    1. Verify the global afterEach hook is properly set up in your support file

    2. Check the Cypress task registration in cypress.config.ts

    3. Manually create the directory: mkdir .nyc_output

    Low Coverage Percentages

    If you're seeing unexpectedly low coverage:

    1. Check the .nycrc file includes and excludes

    2. Ensure your tests are interacting with the components you want to measure

    3. Look at the detailed HTML report to identify untested areas

    Notes

    This approach was designed specifically for Angular 18, where traditional instrumentation methods may not work due to changes in the Angular build system. It uses a manual approach to save coverage data rather than modifying the Angular build process.