3
votes

I have an app that uses Webpack, Angular 4 and ES6 modules. The app works as expected.

I also have unit tests setup using Karma and Jasmine. As part of the test setup I have a test-main.js which is as follows:

Error.stackTraceLimit = Infinity;

import 'core-js/es6';
import 'core-js/es7/reflect';
import 'reflect-metadata';
import 'zone.js';
import 'zone.js/dist/long-stack-trace-zone';
import 'zone.js/dist/sync-test';
import 'zone.js/dist/proxy';
import 'zone.js/dist/jasmine-patch';
import 'zone.js/dist/async-test';
import 'zone.js/dist/fake-async-test';

import {TestBed} from '@angular/core/testing';
import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing';

let appContext = require.context('./', true, /\.spec\.js/);

appContext.keys().forEach(appContext);

TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());

The unit tests run as expected.

I am trying to setup a coverage report, but the best I have been able to achieve is a coverage report where the entire Webpack-generated bundle is instrumented and reported in the coverage report. This is not entirely useful, as you can imagine.

I've tried using the babel-plugin-istanbul and also the istanbul-instrumenter-loader and I either get my entire bundle file in the coverage report, or no report at all. I don't see any errors, and my tests continue to run as expected.

Here is my karma.conf.js file (with everything to do with coverage removed as it was not working):

// Karma configuration
// Generated on Thu May 04 2017 13:00:28 GMT+0100 (GMT Daylight Time)
'use strict';

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

let reporters = ['progress'],
    singleRunSwitch = true,
    browsers = ['ChromeHeadless'];

function isDebug(argument) {
    return argument === 'debug';
}

if (process.argv.some(isDebug)) {        
    reporters = ['progress'];
    singleRunSwitch = false;
    browsers = ['Chrome'];
}

module.exports = function(config) {
    config.set({
        basePath: '',
        frameworks: ['jasmine'],
        files: [
            { pattern: './test-main.js' }
        ],
        exclude: [],
        preprocessors: {
            './test-main.js': ['webpack', 'sourcemap']
        },
        reporters: reporters,
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        autoWatch: false,
        browsers: browsers,

        customLaunchers: {
            ChromeHeadless: {
                base: 'Chrome',
                flags: [
                  '--headless',
                  ' --remote-debugging-port=9222',
                  '--disable-gpu',
                  '--disable-plugins',
                  '--window-size=0,0',
                  '--window-position=-9999,0'
                ],
            },
        },
        singleRun: singleRunSwitch,
        concurrency: Infinity,

        webpack: {
            module: {
                rules: [{
                    test: /\.js$/,
                    exclude: path.join(__dirname, '../node_modules/'),
                    use: [{
                        loader: 'babel-loader',
                        options: {
                            presets: ['es2015']
                        }
                    }]
                }]
            },
            devtool: 'inline-source-map',
            plugins: [
                new webpack.ContextReplacementPlugin(/angular(\\|\/)core(\\|\/)@angular/, path.join(__dirname))
            ]
        },

        webpackMiddleware: {
            stats: 'errors-only'
        },

        webpackServer: {
            noInfo: true
        }

    });
}

Can anyone advise which plugins I need to use and how I should configure them please?

1
Hey, I faced with the same situation. Did you solve the issue?Butsaty
Yep, see the answer belowdanwellman
Ok, I thought you have another solution cuz it looks a little bit heavyButsaty

1 Answers

1
votes

I got it working, I used Istanbul and istanbul-instrumenter-loader - both installed via NPM. My karma.conf.js file now looks like this:

// Karma configuration
// Generated on Thu May 04 2017 13:00:28 GMT+0100 (GMT Daylight Time)
'use strict';

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

let reporters = ['progress', 'coverage'],
    singleRunSwitch = true,
    browsers = ['ChromeHeadless'];

function isDebug(argument) {
    return argument === 'debug';
}

if (process.argv.some(isDebug)) {
    reporters = ['progress'];
    singleRunSwitch = false;
    browsers = ['Chrome'];
}

module.exports = function(config) {
    config.set({
        basePath: '',
        frameworks: ['jasmine'],
        files: [
            { pattern: './test-main.js' }
        ],
        exclude: [],
        preprocessors: {
            './test-main.js': ['webpack', 'sourcemap']
        },
        reporters: reporters,            
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        autoWatch: false,
        browsers: browsers,
        customLaunchers: {
            ChromeHeadless: {
                base: 'Chrome',
                flags: [
                  '--headless',
                  '--remote-debugging-port=9222',
                  '--disable-gpu',
                  '--disable-plugins',
                  '--window-size=0,0',
                  '--window-position=-9999,0'
                ],
            },
        },
        singleRun: singleRunSwitch,
        concurrency: Infinity,
        webpack: {
            module: {
                rules: [{
                    test: /\.spec.js$/,
                    exclude: [path.join(__dirname, '../node_modules/'), /\.html$/],
                    use: [{
                        loader: 'babel-loader',
                        options: { presets: ['es2015'] }
                    }]
                }, {
                    test: /\.js$/,
                    exclude: [path.join(__dirname, '../node_modules/'), /\.spec.js$/, /\.html$/, /test-main.js/],
                    use: [{
                        loader: 'istanbul-instrumenter-loader',
                    }, {
                        loader: 'babel-loader',
                        options: { presets: ['es2015'] }
                    }]
                }]
            },
            devtool: 'inline-source-map',
            plugins: [
                new webpack.ContextReplacementPlugin(/angular(\\|\/)core(\\|\/)@angular/, path.join(__dirname))
            ]
        },
        webpackMiddleware: {
            stats: 'errors-only'
        },
        webpackServer: {
            noInfo: true
        }
    });
}

The configuration was actually quite straight-forward; I had to add a 'coverage' reporter (but not when debugging), and had to add a new module.rules Webpack configuration - there are actually two configs here; I had to run the spec files through Babel separately from the rest of the .js files, and then add the instanbul-intrumenter to the rules array for .js files except *.spec.js (plus exclude a bunch of things!)