We never managed to configure code coverage with istanbul
for our angularjs project. Also this version of istanbul have been deprecated
We switched to istanbul-instrumenter-loader
webpack loader
The following configuration would generate code coverage for us
Can't find the original guide we followed, but I'll describe our configurations as best
as I can:
package.json devDependencies (relevant to code coverage)
{
"babel-loader": "^8.0.5",
"istanbul-instrumenter-loader": "^3.0.1", // webpack loader added in coverage tests
"jasmine-core": "^2.99.1",
"karma": "^3.1.3",
"karma-chrome-launcher": "^2.2.0",
"karma-cli": "^1.0.1",
"karma-coverage-istanbul-reporter": "^1.4.2", // coverage reporter used in tests
"karma-html-reporter": "^0.2.7", // html reporter used in tests
"karma-jasmine": "^1.1.1",
"karma-ng-html2js-preprocessor": "^1.0.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-spec-reporter": "0.0.32",
"karma-webpack": "^3.0.5",
"webpack": "4.28.4",
}
The test packages version are close to yours
package.json test scripts:
Our karma configs are in a ./karma
sub-folder
"scripts": {
"test": "NODE_ENV=development karma start karma/karma.conf.js",
"cover": "npm test -- --cover --reportHtml", // pass flags to karma.conf
}
karma/karma.conf.js
const path = require('path');
const makeWebpackTestConfig = require('./karma.webpack.config');
module.exports = (config) => {
const REPORTS_PATH = path.join(__dirname, '../reports/');
const cover = config.cover || process.env.COVER;
const webstorm = process.env.WEBSTORM; // Running coverage from inside the IDE
const webpack = makeWebpackTestConfig(cover);
const reporters = config.reportHtml ? ['html'] : [];
if (!webstorm) reporters.push('spec');
if (cover) reporters.push('coverage-istanbul');
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '../',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: ['src/main.tests.js'],
// list of files to exclude
exclude: [],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'src/**/*.js': ['webpack', 'sourcemap'],
'src/**/*.html': ['webpack'],
'src/**/*.less': ['webpack'],
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters,
specReporter: {
maxLogLines: 5, // limit number of lines logged per test
suppressErrorSummary: false,// do not print error summary
suppressFailed: false, // do not print information about failed tests
suppressPassed: false, // do not print information about passed tests
suppressSkipped: true, // do not print information about skipped tests
showSpecTiming: true, // print the time elapsed for each spec
failFast: false // test would finish with error when a first fail occurs.
},
htmlReporter: {
outputDir: path.join(REPORTS_PATH, 'unit-tests'), // where to put the reports
// templatePath: null, // set if you moved jasmine_template.html
focusOnFailures: true, // reports show failures on start
namedFiles: true, // name files instead of creating sub-directories
pageTitle: 'Unit Tests', // page title for reports; browser info by default
urlFriendlyName: true, // simply replaces spaces with _ for files/dirs
reportName: 'index', // report summary filename; browser info by default
// experimental
preserveDescribeNesting: true, // folded suites stay folded
foldAll: true, // reports start folded (only with preserveDescribeNesting)
},
coverageIstanbulReporter: {
reports: ['lcov', 'text-summary'],
dir: webstorm ? undefined : path.join(REPORTS_PATH, 'code-coverage'),
},
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN ||
// config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: false,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['RunnerHeadless'],
customLaunchers: {
RunnerHeadless: {
base: 'ChromeHeadless',
flags: ['--headless', '--no-sandbox', '--disable-gpu', '--disable-translate', '--disable-extensions'],
},
},
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: true,
webpack,
webpackMiddleware: {
stats: 'errors-only',
},
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity,
client: {
// Log browser console only locally
captureConsole: !!process.env.WEBSTORM,
}
});
};
Again since karma config is in a subfolder paths (base, reports etc..) are configured differently. Most of the configuration is self explanatory.
- We have an env variable
WEBSTORM
that we set when coverage is run from inside the IDE.
- Also have in mind that source maps need to be enabled in order to map correctly to the original source lines, because original source is transformed by babel.
- We are using a custom
browsers
configuration which may not be needed in your case
karma/karma.webpack.config.js
const makeWebpackConfig = require('../webpack/base-config');
module.exports = (cover) => {
const defaultConfig = makeWebpackConfig();
// Remove entry. Karma will provide the source
defaultConfig.entry = null;
// Have source maps generated so covered statements are mapped correctly
defaultConfig.devtool = 'inline-source-map';
defaultConfig.mode = 'development';
defaultConfig.optimization = {
splitChunks: false,
runtimeChunk: false,
minimize: false,
};
if (cover) {
defaultConfig.module.rules.push({
test: /\.js$/,
use: {
loader: 'istanbul-instrumenter-loader',
options: { esModules: true },
},
enforce: 'post',
exclude: /node_modules|\.spec\.js$/,
});
}
return defaultConfig;
};
The makeWebpackConfig
creates the the base config we use when running dev or production builds which have the babel-loader
and other loaders for styles, html, files etc...
- Whatever setting needed overriding is overridden in the
karma.webpack.conf.js
- Entry is removed, I think, Karam would overwrite it anyway.
- Important
devtool
is set to inline-source-map
- this turned out to be a huge struggle as it seems the external source maps are not picked up and source mapping didn't work until we set to inline
configuration. Source maps help not only with code coverage, but also when tests fail and error information is printed out - it will reference original code lines.
- And finally when doing coverage configure the loader to exclude node_modules and any external sources and also exclude the tests themselves
.babelrc config
{
"presets": [
["@babel/preset-env", { "modules": "commonjs" }],
"@babel/preset-react"
],
"plugins": [
"angularjs-annotate",
["@babel/plugin-proposal-decorators", {
"legacy": true
}],
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-syntax-import-meta",
["@babel/plugin-proposal-class-properties", {
"loose": true
}],
"@babel/plugin-proposal-json-strings",
"@babel/plugin-proposal-function-sent",
"@babel/plugin-proposal-export-namespace-from",
"@babel/plugin-proposal-numeric-separator",
"@babel/plugin-proposal-throw-expressions",
"@babel/plugin-proposal-export-default-from",
"@babel/plugin-proposal-logical-assignment-operators",
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-nullish-coalescing-operator",
"@babel/plugin-proposal-do-expressions",
"@babel/plugin-proposal-function-bind"
]
}
Should probably work with your own .babelrc
config. { "modules": "commonjs" }
was important to us for some reason but can't remember right now
test entry point - src/main.tests.js
import '@babel/polyfill';
import './appConfig';
import './main';
const testsContext = require.context('.', true, /\.spec.js$/);
testsContext.keys().forEach(testsContext);
This is similar to your configuration, though angular is imported in main
and anglar-mocks
are imported for each test as we have a lot of separate modules
path.join(__dirname, '')
like you did in webpack config, really not sure but give it a try. Another Idea change your regex in testContext by/\.spec\.js?$/
you missed a backslash – Disfigure