lebolo lebolo - 16 days ago 11
Javascript Question

How can I get code coverage results in my Aurelia app using karma?

I created an Aurelia app using the Aurelia CLI (

au new
) and would like to set up code coverage (preferably with karma-coverage, but if that's not possible I'll use whatever).

I first
npm install karma-coverage --save-dev
then copy the
test.js
task over to a
cover.js
(so that I can run
au cover
).

cover.js



import {Server as Karma} from 'karma';
import {CLIOptions} from 'aurelia-cli';
// import project from "../aurelia.json";

export function cover(done) {
new Karma({
// This is the same as what's in karma.conf.js after running
// Except I added the 'src\\**\\*.js' part
files: [
'scripts\\vendor-bundle.js',
{pattern: 'test\\unit\\**\\*.js', included: false},
'test/aurelia-karma.js',
'scripts\\app-bundle.js',
'scripts\\materialize-bundle.js',
{pattern: 'src\\**\\*.js', included: false}
],
configFile: __dirname + '/../../karma.conf.js',
singleRun: !CLIOptions.hasFlag('watch'),
reporters: ['progress', 'coverage'],
//logLevel: 'debug',
preprocessors: {
// [project.unitTestRunner.source]: [project.transpiler.id], // Is this actually needed? Nothing changes if I add or remove this...
'src/**/*.js': ['babel', 'coverage']
},
coverageReporter: {
includeAllSources: true,
reporters: [
{type: 'html', dir: 'coverage'},
{type: 'text'}
]
}
}, done).start();
}

export default cover;


This... gets me somewhere?

Console output of <code>au cover</code>

But I don't think the tests are being linked to the individual src files (they're instead being linked to
app-bundle.js
).

Is there any way to get code coverage at the src file level (i.e. not bundle level) for an Aurelia app?

Other Files of Interest



app.js



export class App {
constructor() {
this.message = 'Hello World!';
}
}


karma.conf.js



"use strict";
const path = require('path');
const project = require('./aurelia_project/aurelia.json');

let testSrc = [
{ pattern: project.unitTestRunner.source, included: false },
'test/aurelia-karma.js'
];

let output = project.platform.output;
let appSrc = project.build.bundles.map(x => path.join(output, x.name));
let entryIndex = appSrc.indexOf(path.join(output, project.build.loader.configTarget));
let entryBundle = appSrc.splice(entryIndex, 1)[0];
let files = [entryBundle].concat(testSrc).concat(appSrc); console.log(files);

module.exports = function(config) {
config.set({
basePath: '',
frameworks: [project.testFramework.id],
files: files,
exclude: [],
preprocessors: {
[project.unitTestRunner.source]: [project.transpiler.id]
},
'babelPreprocessor': { options: project.transpiler.options },
reporters: ['progress'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
// client.args must be a array of string.
// Leave 'aurelia-root', project.paths.root in this order so we can find
// the root of the aurelia project.
client: {
args: ['aurelia-root', project.paths.root]
}
});
};

Answer

I finally got it working, but it required a lot of under-the-hood modifications =\

First I had to install the following packages

npm install karma-coverage --save-dev
npm install karma-requirejs --save-dev
npm install babel-plugin-istanbul --save-dev

aurelia_project/tasks/cover.js

This is based on test.js.

  • Add requirejs to the list of frameworks (from the karma-requirejs package)
  • Add the individual src/test files (with included: false) as well as test/aurelia-karma-cover.js, which does the actual requireing of test files.
  • Modified aurelia-karma.js into aurelia-karma-cover.js to not include /test/unit/setup.js (it was giving me trouble with aurelia-browser-pal dependencies)
  • Remove "coverage" from src file preprocessing (babel-plugin-istanbul will now handle instrumentation of code - see statement at the end for details).
import {Server as Karma} from 'karma';
import {CLIOptions} from 'aurelia-cli';
import project from "../aurelia.json";

export function cover(done) {
  new Karma({
    configFile: __dirname + '/../../karma.conf.js',
    frameworks: [project.testFramework.id, 'requirejs'],
    files: [
      {pattern: 'src\\**\\*.js', included: false},
      {pattern: 'test\\unit\\**\\*.js', included: false},
      // This file actually loads the spec files via require - it's based on aurelia-karma.js
      // but removes setup.js and its dependencies
      'test/aurelia-karma-cover.js'
    ],
    exclude: [
      'src/environment.js',
      'src/main.js',
      'src/resources/index.js'
    ],
    preprocessors: {
      'src/**/*.js': ['babel'],
    },
    reporters: ['progress', 'coverage'],
    singleRun: !CLIOptions.hasFlag('watch'),
    coverageReporter: {
      includeAllSources: true,
      reporters: [
        {type: 'html', dir: 'coverage'},
        {type: 'text'}
      ]
    }
  }, done).start();
}

export default cover;

test/unit/aurelia-karma-cover.js

Just change var allTestFiles = ['/base/test/unit/setup.js']; to var allTestFiles = []; to avoid aurelia-pal-browser dependency errors.

aurelia_project/aurelia.json

Add "istanbul" to the transpiler.options.plugins list if using babel-plugin-istanbul.

* Without babel-plugin-istanbul, code coverage works on post-transpiled code, which adds boilerplate that can't really be tested. This allows you to get to 100% code coverage ;)

Comments