How to Combine Coverage from Cypress (E2E and Component Test) and Jest in an Nx Project

Share on facebook
Share on google
Share on twitter
Share on linkedin

Coverage helps us see what part of our app is covered with tests and can help us determine if new code is covered in pull requests with tools such as SonarQube or Coveralls. My recommended testing strategy for testing Angular apps is to focus on covering the use cases with Cypress component tests and add some unit tests around edge cases and calculation while having a few e2e tests as a smoke test thus needing to combine test coverage from all these different sources to get a clear picture of what code is tested.

Getting coverage for Jest comes out of the box but setting up coverage with Cypress E2E and component tests and merging the coverage results all together requires more manual intervention and this blog post is covering how to get coverage from the different testing techniques and combine the coverage results into one report in an Nx project.

How coverage works

Before we dive in, let’s cover how code coverage is calculated in the first place.

A process known as instrumentation is performed on the code, adding counters for each statement to count when statements are executed. The tests would then run against the instrumented code and after that, the counters can be read from the different statements and conducted into a coverage report.

To do the instrumentation, it’s most commonly done with a webpack/babel loader such as @jsdevtools/coverage-istanbul-loader. Alternatively, the instrumented code can be generated with the NYC the command line interface for the coverage tool Istanbul.

Setting up coverage for Jest tests

Setting up coverage with Jest is the most straightforward as it is covered out of the box.

In the Jest config, we just set the properties for enabling coverage, so it becomes:

export default {
  displayName: 'jest-cypress-coverage',
  preset: './jest.preset.js',
  setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
  coverageDirectory: './dist/coverage',
  coverageReporters: ['json', 'text', 'cobertura', 'lcov'],
  transform: {
    '^.+\\.(ts|mjs|js|html)$': [
        tsconfig: '<rootDir>/tsconfig.spec.json',
        stringifyContentPathRegex: '\\.(html|svg)$',
  transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
  snapshotSerializers: [
  testMatch: [

In particular, we are setting coverageDirectory and coverageReporters.

Then we make sure to run our jest tests with coverage by setting the coverage flag in the Nx project.json:

    "test": {
      "executor": "@nx/jest:jest",
      "outputs": ["{workspaceRoot}/coverage/{projectName}"],
      "options": {
        "jestConfig": "jest.config.ts",
        "codeCoverage": true,
        "passWithNoTests": true
      "configurations": {
        "ci": {
          "ci": true,
          "codeCoverage": true

And we can now run nx test and we will see the coverage files being generated:

Setting up coverage for Cypress component tests

For Cypress component test coverage we need a bit more manual configuration. We will need to use a custom webpack config that use the loader: @jsdevtools/coverage-istanbul-loader for instrumentation.

The webpack config looks like this:

import * as path from 'path';

export default {
  module: {
    rules: [
        test: /\.(js|ts)$/,
        loader: '@jsdevtools/coverage-istanbul-loader',
        options: { esModules: true },
        enforce: 'post',
        include: [path.join(__dirname, 'src')],
        exclude: [

The webpack config is consumed by builder @angular-builders/custom-webpack:browser which allows us to extend the Angular webpack config.

First, we install the custom webpack builder:

npm i -D @angular-builders/custom-webpack

We set up Cypress component tests and generate component tests files with:

nx generate @nx/angular:cypress-component-configuration --project=jest-cypress-coverage

Lastly, we need to configure our Cypress config to run tests on the instrumented code and generate our coverage reports.

For Cypress to generate a coverage report we need to use the @cypress/code-coverage package.

We install it with: npm i -D @cypress/code-coverage.

We add this to our component.ts file:

import './commands';

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

And this to our cypress.config.ts file:

import { nxComponentTestingPreset } from '@nx/angular/plugins/component-testing';
import { defineConfig } from 'cypress';
import coverageWebpack from './coverage.webpack';

const nxPreset = nxComponentTestingPreset(__filename);

export default defineConfig({
  component: {
    devServer: {
      webpackConfig: coverageWebpack,
    setupNodeEvents(on, config) {
      // eslint-disable-next-line @typescript-eslint/no-var-requires
      require('@cypress/code-coverage/task')(on, config);

      // It's IMPORTANT to return the config object
      // with any changed environment variables
      return config;
  includeShadowDom: true,

And we can run our component tests like nx run component-test and see the generated coverage reports

Setting up coverage for Cypress E2E tests

Setting up coverage for E2E tests is similar. We will also set up the instrumentation with the custom webpack extension including the same Istanbul loader as for component tests and we will consume them in project.json by serving the app using the @angular-builders/custom-webpack:browser executor.

In the e2e project.json we will have:

"e2e": {
  "executor": "@nx/cypress:cypress",
  "options": {
    "cypressConfig": "e2e/cypress.config.ts",
    "devServerTarget": "jest-cypress-coverage:serve-coverage",
    "testingType": "e2e"
  "configurations": {
    "production": {
      "devServerTarget": "jest-cypress-coverage:serve-coverage"

And likewise, we are configuring Cypress to generate the coverage results.

We config the e2e.ts file like this:

import './commands';

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

And our cypress.config.ts is configured like:

import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset';
import { defineConfig } from 'cypress';

export default defineConfig({
  e2e: {
    setupNodeEvents(on, config) {
      // eslint-disable-next-line @typescript-eslint/no-var-requires
      require('@cypress/code-coverage/task')(on, config);
      return config;

And then we can run nx e2e e2e and see the generated result:

Merging the coverage results

Now all of our different testing methods can generate individual coverage results but we would like to merge them so we can upload combined result to a coverage tool like SonarQube or Coveralls.

We will do this by copying the individual coverage results to the same folder and use the NYC (instanbul CLI) tool to merge the coverage results.

We will write a complete script to copy the individual coverage results, merge the results and generate the combined coverage report.

 * This script merges the coverage reports from Cypress and Jest into a single one,
 * inside the "coverage" folder

const { execSync } = require('child_process');
const fs = require('fs-extra');

const REPORTS_FOLDER = 'reports';
const FINAL_OUTPUT_FOLDER = 'combined-coverage';

const { program } = require('commander');

    '-e --e2e-cov-dir <dir>',
    'Directory for e2e coverage',
    '-c --ct-cov-dir <dir>',
    'Directory for cypress-ct coverage',
    '-u --unit-cov-dir <dir>',
    'Directory for unit test coverage',

const options = program.opts();

console.log('Running merge with options:', options);

const run = (commands) => {
  commands.forEach((command) => execSync(command, { stdio: 'inherit' }));

// Create the reports folder and move the reports from cypress and jest inside it
  options.ctCovDir + '/coverage-final.json',
  options.unitCovDir + '/coverage-final.json',


// Run "nyc merge" inside the reports folder, merging the two coverage files into one,
// then generate the final report on the coverage folder
  // "nyc merge" will create a "coverage.json" file on the root, we move it to .nyc_output
  `npx nyc merge ${REPORTS_FOLDER} && mv coverage.json .nyc_output/out.json`,
  `npx nyc report --reporter lcov --report-dir ${FINAL_OUTPUT_FOLDER}`,

This can then be run as:

node ./tools/scripts/merge-coverage.js

And we get the combined coverage report:

We can now see how our app is covered with unit, component and e2e tests!

Thanks to this combined report we see that todo-list needs some more tests…

Demo repo

A full demo of this can be found on my Github here.


In conclusion, by setting up coverage for Jest, Cypress component tests, and Cypress E2E tests, and combining the coverage results, you can gain insights into the test coverage of your Angular app. This enables you to make informed decisions about the quality of your codebase and ensure that new code is adequately covered by tests.

If you want to learn more about testing and my high ROI testing strategy, I recommend you sign up for my training in the next cohort here.

Do you want to become an Angular architect? Check out Angular Architect Accelerator.

Related Posts and Comments

How to Handle Errors in a Reactive Angular App

In this post, we will cover how to handle errors in a reactive Angular app. To provide a good user experience you should always let your users know what state your application is in. That includes showing a loading spinner when it’s loading and showing error messages if there are any errors. It is a

Read More »

How to Set Up Git Hooks in an Nx Repo

Git hooks can be used to automate tasks in your development workflow. The earlier a bug is discovered, the cheaper it is to fix (and the less impact it has). Therefore it can be helpful to run tasks such as linting, formatting, and tests when you are e.g. committing and pushing your code, so any

Read More »

The Stages of an Angular Architecture with Nx

Long gone are the times when the frontend was just a dumb static website. Frontend apps have gotten increasingly complex since the rise of single-page application frameworks like Angular. It comes with the price of increased complexity and the ever-changing frontend landscape requires you to have an architecture that allows you to scale and adapt

Read More »

The Best Way to Use Signals in Angular Apps

Since Angular 16, Angular now has experimental support for signals and there is a lot of confusion in the community about whether this is going to replace RxJS or how it should be used in an app in combination with RxJS. This blog post sheds some light on what I think is the best way

Read More »

High ROI Testing with Cypress Component Testing

Testing is one of the most struggled topics in Angular development and many developers are either giving up testing altogether or applying inefficient testing practices consuming all their precious time while giving few results in return. This blog post will change all this as we will cover how I overcame these struggles the hard way

Read More »