Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to include all source files for code coverage reporting? #135

Closed
bennycode opened this issue Feb 14, 2019 · 4 comments
Closed

How to include all source files for code coverage reporting? #135

bennycode opened this issue Feb 14, 2019 · 4 comments

Comments

@bennycode
Copy link
Contributor

I am using electron-mocha to run the test suite of my Electron app. To get code coverage information I execute istanbul instrument beforehand. After the tests finish I am creating a report using remap-istanbul.

The report looks good. I can see which lines of code have been tested and mapping to my TypeScript source files works. 🍾

The only thing that is a little off are the code coverage metrics (% lines of codes tested) because in my report I only see how much of the code, that has been imported by the tests, has been tested. I would like to include all my source files in the report (even if the test code doesn't run through this code).

I know that istanbul has an --include-all-sources flag for this purpose but how does this work with electron-mocha? Is there a similar flag?

@bennycode
Copy link
Contributor Author

As a workaround I require all my code in a global before hook from mocha:

const glob = require('glob');
const path = require('path');

/**
 * Workaround to include all source code files in the report:
 * https://github.com/jprichardson/electron-mocha/issues/135
 *
 * Code needs to be imported from the instrumented sources:
 * https://github.com/jprichardson/electron-mocha/issues/19#issuecomment-193374439
 */
const loadSourceCode = () => {
  const intrumentedCode = path.join(__dirname, '..', '..', 'instrumented-code');

  glob(`${intrumentedCode}/**/*.js`, {
    sync: true,
  }).forEach(file => require(path.resolve(file)));
};

before(() => loadSourceCode());

@inukshuk
Copy link
Collaborator

Yes, I was going to suggest something like that. You could also add an additional --require when you want to generate a coverage report.

I don't know if something similar works in your setup, but if you're using babel, for example, coverage reports for both processes have become really easy to setup. This is what I'm currently doing:

@bennycode
Copy link
Contributor Author

Thank you for your input. It helped a lot. I now have a similar approach to yours:

  1. Running electron-mocha which requires Babel to transpile my TypeScript test code into JavaScript and to instrument this code using babel-plugin-istanbul
  2. Binding to mocha's global after hook to manually write the coverage output from the renderer process and the main process into the .nyc_output directory
  3. Merge both coverage output files (coverage.browser.json & coverage.renderer.json) into one report using nyc report

Here is what it looks like:

package.json

...
  "devDependencies": {
    "@babel/core": "7.3.3",
    "@babel/plugin-proposal-class-properties": "7.3.3",
    "@babel/preset-env": "7.3.1",
    "@babel/preset-typescript": "7.3.3",
    "@babel/register": "7.0.0",
    "@types/mocha": "5.2.6",  
    "babel-plugin-istanbul": "5.1.1",
    "electron": "4.0.5",
    "electron-mocha": "6.0.4",
    "fs-extra": "7.0.1",
    "rimraf": "2.6.3",
    "typescript": "3.3.3"
  },
...
  "scripts": {
    "clean:coverage": "rimraf .nyc_output coverage",
    "coverage": "yarn clean:coverage && yarn test && nyc report",
    "test": "yarn test:main && yarn test:renderer",
    "test:main": "electron-mocha --require ./babel-register.js \"src/**/*.test?(.main).ts\"",
    "test:renderer": "electron-mocha --renderer --require ./babel-register.js \"src/**/*.test?(.renderer).ts\""
  }
...

babel-register.js

require('@babel/register')({
  cache: false,
  extensions: ['.ts'],
  plugins: [
    '@babel/proposal-class-properties',
    [
      'istanbul',
      {
        exclude: ['**/*.test?(.main|.renderer).ts'],
      },
    ],
  ],
  presets: [
    [
      '@babel/preset-env',
      {
        targets: {
          node: 'current',
        },
      },
    ],
    '@babel/preset-typescript',
  ],
});

src/__tests__/after.test.ts

import {remote} from 'electron';

const resolveDependency = (name: string) => {
  return remote ? remote.require(name) : require(name);
};

const path = resolveDependency('path');
const fs = resolveDependency('fs-extra');

const writeCoverageReport = (coverage: Object) => {
  const outputFile = path.resolve(process.cwd(), `.nyc_output/coverage.${process.type}.json`);
  fs.outputJsonSync(outputFile, coverage);
};

after(() => {
  if (process.type && process.type === 'renderer') {
    writeCoverageReport((window as any).__coverage__);
  } else {
    writeCoverageReport((global as any).__coverage__);
  }
});

src/logger/LogUtil.renderer.ts

import * as assert from 'assert';
import {enableLogging} from './LogUtil';

describe('enableLogging', () => {
  beforeEach(() => window.localStorage.clear());

  it('writes a logger namespace into the localStorage API', () => {
    enableLogging();
    assert.strictEqual(window.localStorage.getItem('namespace'), '@myorg/*');
  });
});

Thanks again! My problem is now solved. Just need to find out how to have all my source (even the one that is not required by my tests) listed up in the final report but this is discussed here.

@kontrollanten
Copy link

Thanks for the guidance! I tried to sum the discussions if there's more people who's interested in how to solve this; https://dev.to/kontrollanten/combine-coverage-reports-from-electron-tests-1if6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants