Istanbul Coverage 不适用于 Karma、Mocha、Webpack

Istanbul Coverage does not work with Karma, Mocha, Webpack

我按照代码设置了 webpack 和 karama 配置。

webpack.config.js

/* global module, process, require, __dirname */

const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const fs = require('fs');

const gc = require('./global.config.js');

// extract style
const extractCss = new ExtractTextPlugin({
    filename: 'css/[name].css'
});
const extractSass = new ExtractTextPlugin({
    filename: 'css/[name].css'
});

// dir path
const sourceDir = gc.basePath+ gc.baseDir + gc.resourceDir;
const outputDir = gc.baseDir + gc.resourceDir + gc.distDir;

const devPort = 9090;
const devOutputDir = gc.resourceDir + gc.distDir;

// generate entry
const fileDir = sourceDir + '/js';
const read = (dir) =>
    fs.readdirSync(dir)
        .reduce((files, file) => {
            const filePath = dir + '/' +file;
            if (fs.statSync(filePath).isDirectory()) {
                return files.concat(read(filePath));
            }

            if (file !== 'entry.js') {
                return files;
            }

            return files.concat(filePath);
        }, []);
const getEntry = (files) => {
    const entry = {};
    files.forEach(file => {
        const key = file.replace(fileDir + '/', '').replace('.js', '');
        entry[key] = ['babel-polyfill', file];
    });
    return entry;
};
const entry = getEntry(read(fileDir));

// webpack config
const config = {
    entry: Object.assign({}, gc.vendors.entry, entry),
    output: {
        path: __dirname + outputDir,
        filename: 'js/[name].bundle.js',
        publicPath: '/',
        libraryTarget: 'umd', // export itself to a global var
        library: 'sc' // name of the global var: 'sc'
    },
    resolve: {
        modules: ['node_modules'],
        extensions: ['.js', '.jsx', '.css', '.scss']
    },
    devtool: 'source-map',
    cache: true,
    plugins: [
        extractCss,
        extractSass,
        new webpack.ProvidePlugin(gc.vendors.providePlugin)
    ],
    devServer: {
        host: '0.0.0.0',
        publicPath: 'http://localhost:' + devPort + '/',
        port: devPort,
        disableHostCheck: true,
        noInfo: true,
        inline: true,
        proxy: {
            '**': 'http://localhost:8080'
        }
    },
    module: {
        rules: [{
            enforce: 'pre',
            test: /\.(js|jsx)?$/,
            loader: 'eslint-loader',
            exclude: /(node_modules|bower_components)/
        }, {
            test: /\.(js|jsx)?$/,
            loader: 'babel-loader',
            exclude: /(node_modules|bower_components)/,
            query: {
                cacheDirectory: true,
                presets: ['es2015', 'react']
            }
        }, {
            test: /\.css$/,
            use: extractCss.extract({
                fallback: 'style-loader',
                use: 'css-loader'
            })
        }, {
            test: /\.scss$/,
            use: extractSass.extract({
                use: [{
                    loader: 'css-loader', options: {
                        sourceMap: true
                    }
                }, {
                    loader: 'sass-loader', options: {
                        sourceMap: true
                    }
                }],
                // use style-loader in development
                fallback: 'style-loader'
            })
        }]
    }
};

const env = process.env.NODE_ENV;

// develop phase
if (env === 'development') {
    config.output.publicPath = 'http://localhost:' + devPort + devOutputDir;
}

// production phase
if (env === 'production') {
    config.devtool = '#source-map';
    config.plugins = (config.plugins || []).concat([
        new webpack.optimize.CommonsChunkPlugin({
            name: 'vendors'
        }),
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: '"production"'
            }
        }),
        new webpack.optimize.UglifyJsPlugin({
            sourceMap: true,
            compress: {
                warnings: false
            }
        }),
        new webpack.LoaderOptionsPlugin({
            minimize: true
        })
    ])
}

module.exports = config;

karma.config.js

/* global module, require */

const path = require('path');
const webpackConfig = require('./webpack.config.js');
const gc = require('./global.config.js');

// dir path
const sourceDir = gc.baseDir + gc.resourceDir;
const sourcePattern = gc.basePath + gc.baseDir + gc.resourceDir + '/js/**/*.js';
const testPattern = gc.basePath + gc.baseDir + gc.testDir + '/**/*.spec.js';

const preprocessor = {};
preprocessor[sourcePattern] = ['webpack', 'coverage'];
preprocessor[testPattern] = ['webpack'];

// add webpack test config
const rules = [{
    test: /sinon.*\.js$/,
    loader: "imports-loader?define=>false,require=>false"
}, {
    enforce: 'post',
    test: /\.js/,
    exclude: /(node_modules|bower_components)/,
    include: path.join(sourceDir),  // instrument only testing sources with Istanbul, after ts-loader runs
    loader: 'istanbul-instrumenter-loader'
}];
webpackConfig.module.rules = webpackConfig.module.rules.concat(rules);
webpackConfig.module['noParse'] = [/sinon/];
webpackConfig.resolve['alias'] = {sinon: 'sinon/pkg/sinon'};

module.exports = function (config) {
    config.set({
        frameworks: [
            'mocha',
            'chai',
            'sinon'
        ],

        files: [
            {pattern: sourcePattern, watched: false},
            {pattern: testPattern, watched: false}
        ],

        browsers: ['Chrome', 'Firefox'],

        preprocessors: preprocessor,

        // report
        reporters: ['mocha', 'coverage'],
        coverageReporter: {
            dir: path.join(__dirname, 'coverage'),
            reporters: [
                {
                    type: 'html',
                    subdir: 'report-html'
                },
                {
                    type: 'lcov',
                    subdir: 'report-lcov'
                },
                {
                    type: 'cobertura',
                    subdir: '.',
                    file: 'cobertura.txt'
                },
                {
                    type: 'text',
                    subdir: '.',
                    file: 'report-text.txt'
                },
                {
                    type: 'text-summary'
                }
            ],
            fixWebpackSourcePaths: true
        },

        // client
        client: {
            mocha: {
                // change Karma's debug.html to the mocha web reporter
                reporter: 'html',

                // require specific files after Mocha is initialized
                require: [require.resolve('bdd-lazy-var/bdd_lazy_var_global')],

                // custom ui, defined in required file above
                ui: 'bdd-lazy-var/global',
            }
        },

        // webpack
        webpack: webpackConfig,
        webpackMiddleware: {
            stats: 'errors-only'
        },
    });
};

但它没有像我预期的那样工作。它都是用 babel 转译的,所以它的覆盖范围是错误的。它检查了包含的库,例如 jQuery.

[INFO] =============================== Coverage summary ===============================
[INFO] Statements   : 19.59% ( 704/3593 )
[INFO] Branches     : 6.11% ( 208/3402 )
[INFO] Functions    : 17.32% ( 111/641 )
[INFO] Lines        : 19.59% ( 703/3588 )
[INFO] ================================================================================

我的配置有什么问题?这是我的全部代码。 https://github.com/egaoneko/maven-spring-webpack-scaffold

伊斯坦布尔对 ES6 不是很好。创建一个构建文件夹,将翻译后的 ES6 或 JSX 代码以普通 JS 放入伊斯坦布尔可以理解的构建中。这是我开始使用 Istanbul 进行代码覆盖时了解到的缺点之一。此外,用 vanilla JavaScript 编写你的测试,这样伊斯坦布尔就能理解你的测试。我在 ES6 中编写我的函数并在 vanilla Javascript 中使用 mocha 和 chai 进行测试。

因为我在我的 package.json 中使用 npm run 进行测试,所以我有我的测试

nyc --reporter=html --reporter=text mocha test/**/*.test.js

这行得通吗?