nrwl / nx

Smart Monorepos · Fast CI
https://nx.dev
MIT License
23.62k stars 2.36k forks source link

How do you generate a combined code coverage report? #622

Closed stijnvn closed 6 years ago

stijnvn commented 6 years ago

How do you generate a combined code coverage report with Nx 6?

vsavkin commented 6 years ago

There is no build-in way to do it.

Having one report doesn't really work for most organizations. Imagine you have two teams building two apps in the same repo. The fact that one team failed to meet a certain threshold shouldn't affect the other team.

So we can have a report per team (app+all its libs), but then the situation tricky if you have a lot of shared libs.

I think the simplest thing to do is to have a threshold per project (app or lib). In this case you can set custom rules depending on your setup (e.g., shared libs' threshold can be higher).

aifrim commented 5 years ago

But if you want to have a combined code coverage report? How do you do it?

twittwer commented 5 years ago

You can output your coverage via json reporter per lib/app and use something like this script to combine them and build a html report at the end. - That is the approach we took, but have no solution for result combination yet.

alexus85 commented 5 years ago

Here's a script that demonstrates how I generate the summarized report of all libs in my coverage directory.

const fs = require( 'fs-extra');
const glob = require('glob');
const { createReporter } = require('istanbul-api');
const istanbulCoverage = require('istanbul-lib-coverage');

const reporter = createReporter();

/* [ Configuration ] */
const rootDir = './coverage/libs';
const reportOut = './coverage/report';

const normalizeJestCoverage = ( obj ) => {
  const result = { ...obj };

  Object
  .entries( result )
  .filter( ([k, v] ) => v.data )
  .forEach( ([k, v] ) => {
    result[k] = v.data;
  });

  return result;
};

const mergeAllReports = ( coverageMap, reports ) => {
  if ( Array.isArray( reports ) === false ) {
    return;
  }

  reports.forEach( reportFile => {
    const coverageReport = fs.readJSONSync( reportFile );
    coverageMap.merge( normalizeJestCoverage( coverageReport ) );
  })
};

const findAllCoverageReports = ( path, callback ) => {
  glob( path, {}, ( err, reports )=>{
    callback( reports, err );
  });
};

const generateReport = ( coverageMap, types ) => {
  reporter.dir = reportOut;
  reporter.addAll(types || ['html', 'text'] );
  reporter.write( coverageMap );
};

async function main () {
  const coverageMap = istanbulCoverage.createCoverageMap( {} );

  findAllCoverageReports( rootDir + '/**/coverage-final.json', ( reports, err ) => {
    if ( Array.isArray( reports ) ) {
      mergeAllReports( coverageMap, reports );
      generateReport( coverageMap, [ 'text' ]  )
    }
  });
}

main().catch(err => {
  console.error(err);
  process.exit(1);
});
LMFinney commented 4 years ago

istanbul-combine is the best solution I've found for this. It uses an old version of istanbul internally, but it worked for me.

YonatanKra commented 3 years ago

I've written a short blog post about it with my solution: https://yonatankra.com/how-to-create-a-workspace-coverage-report-in-nrwl-nx-monorepo/ Hope this helps

laurentsd commented 2 years ago

here is an updated version of @alexus85 code above, using the newer 'istanbul-reports' since 'istanbul-api' is depracated:

const fs = require('fs-extra');
const glob = require('glob');
const istanbulReports = require('istanbul-reports');
const libReport = require('istanbul-lib-report');
const istanbulCoverage = require('istanbul-lib-coverage');

/* [ Configuration ] */
const rootDir = './coverage';
const reportOut = './coverage/report';

const configWatermarks = {
   statements: [50, 80],
   functions: [50, 80],
   branches: [0, 80],
   lines: [50, 80],
};

const normalizeJestCoverage = (obj) => {
   const result = { ...obj };

   Object.entries(result)
      .filter(([k, v]) => v.data)
      .forEach(([k, v]) => {
         result[k] = v.data;
      });

   return result;
};

const mergeAllReports = (coverageMap, reports) => {
   if (Array.isArray(reports) === false) {
      return;
   }

   reports.forEach((reportFile) => {
      const coverageReport = fs.readJSONSync(reportFile);
      coverageMap.merge(normalizeJestCoverage(coverageReport));
   });
};

const findAllCoverageReports = (path, callback) => {
   glob(path, {}, (err, reports) => {
      callback(reports, err);
   });
};

const generateReport = (coverageMap, type) => {
   const context = libReport.createContext({
      dir: reportOut,
      // The summarizer to default to (may be overridden by some reports)
      // values can be nested/flat/pkg. Defaults to 'pkg'
      defaultSummarizer: 'nested',
      watermarks: configWatermarks,
      coverageMap,
   });
   const report = istanbulReports.create(type, {
      skipEmpty: false,
      skipFull: true, // skip text lines with 100%
      verbose: true, // verbose html report
   });
   report.execute(context);
};

async function main() {
   const coverageMap = istanbulCoverage.createCoverageMap({});

   findAllCoverageReports(rootDir + '/**/coverage-final.json', (reports, err) => {
      if (Array.isArray(reports)) {
         mergeAllReports(coverageMap, reports);
         generateReport(coverageMap, 'text');
         generateReport(coverageMap, 'html');
      }
   });
}

main().catch((err) => {
   console.error(err);
   process.exit(1);
});
github-actions[bot] commented 1 year ago

This issue has been closed for more than 30 days. If this issue is still occuring, please open a new issue with more recent context.