Lessons learned enabling code typescript coverage in cypress with rollup and babel

I spent last two evening fighting with my rollup and babel infrastructure to make code coverage work in cypress for typescript and I want to share some of my journey in case you encounter the same problems.

Let me start first by saying that if you have the simpler tasks of enabling code coverage in javascript, you should first just check this guide, I still recommend that you read this article because there is a twist in how to configure babel but go first read the other post, I’ll wait here !

Likewise if you need to do it using webpack there is this other guide.

With that out of the way let’s focus on our problem, we have typescript (and potentially javascript) code that needs to be instrumented so that coverage can work in cypress.

Cypress supports coverage but don’t support instrumenting your code so something else has to do it.

The something else is currently nyc/istanbul and there is a plugin for babel to do that: babel-plugin-istanbul, if you need typescript be sure to also install @istanbuljs/nyc-config-typescript.

Next we need to instruct nyc to use the typescript configuration that we just installed. I did that by adding this to my package.json:

"nyc": {
  "extends": "@istanbuljs/nyc-config-typescript",
  "all": true
},

Next step is babel.

Babel

Your configuration (.babelrc.json / babelrc.js) should look something like that:

{
  presets: [
    '@babel/preset-env',
    '@babel/preset-typescript',
  ];
}

I don’t recommend to add plugins: ['istanbul'], in the file as other posts suggest because otherwise you will have the instrumenting even in your release version, instead you should have a .dev rollup configuration that you use for CI / testing of your release builds.

At this point it’s good to check that things are working correctly, install @babel/cli and run:

npx babel  src --extensions ".ts,.js,.tsx,.jsx" --plugins "istantbul"

If everything works as expected you should see path: in the generated js file:

grep path: demo/cubism-es.standalone.js |grep ts
      path: "/github-cubism-es/src/context/apiCSS.ts",
      path: "/github-cubism-es/src/context/apiKeyboard.ts",

If this is working we can move to configuring rollup to properly invoke babel

Rollup

The rollup configuration that you will use for coverage should contain an entry similar to this:

babel({
  extensions: ['.js', '.jsx', '.ts', '.tsx'],
  plugins: ['istanbul'],
	// add here your additional settings like babelHelpers
}),

The first setting is to ensure that Javascript and Typescript files are properly handled by babel, I forgot to add it and that and it took me a while to figure out why the generated file from rollup wasn’t properly instrumented.

The second setting is to obviously call istanbul, re-iterating what I just said above this should go only in your .dev or .testing rollup configuration, not the one that you use to generate the artifacts for the releases.

Cypress

Add in cypress.config.js the following code in the setupNodeEvent() function:

  require('@cypress/code-coverage/task')(on, config)
  // include any other plugin code...
  //
  // It's IMPORTANT to return the config object
  // with any changed environment variables
  return config

The resulting configuration should look like:

const { defineConfig } = require('cypress');

module.exports = defineConfig({
  chromeWebSecurity: false,

  e2e: {
    setupNodeEvents(on, config) {
      require('@cypress/code-coverage/task')(on, config);
      // include any other plugin code...
      //
      // It's IMPORTANT to return the config object
      // with any changed environment variables
      return config;
    },
  },
});

And there you have it your next cypress should generate coverage for your code in javascript and typescript.