Coverage reports with karma and a mix of javascript and typescript src files - javascript

I have a project where I use webpack for development/testing and karma as my test runner. This project has source files written half in js and half in ts/tsx. The test suite is written completely in js. I currently use karma-coverage, which shows coverage reports for all my js source files, but it does not support typescript files. All my tests run, there is no problem there, I just would like coverage reports for all my test files. Can anyone point me in the right direction?
Here is my karma.conf.js if this helps.
'use strict';
const webpackCfg = require('./webpack.config')('test');
module.exports = function karmaConfig(config) {
config.set({
browsers: ['Chrome'],
files: [
'test/loadtests.js'
],
port: 8080,
captureTimeout: 60000,
frameworks: [
'mocha',
'chai',
'sinon'
],
client: {
mocha: {}
},
singleRun: true,
reporters: ['mocha', 'coverage', 'junit'],
mochaReporter: {
output: 'autowatch'
},
preprocessors: {
'test/loadtests.js': ['webpack', 'sourcemap']
},
webpack: webpackCfg,
webpackServer: {
noInfo: true
},
junitReporter: {
outputDir: 'coverage',
outputFile: 'junit-result.xml',
useBrowserName: false
},
coverageReporter: {
dir: 'coverage/',
watermarks: {
statements: [70, 80],
functions: [70, 80],
branches: [70, 80],
lines: [70, 80]
},
reporters: [
{ type: 'text' },
{
type: 'html',
subdir: 'html'
},
{
type: 'cobertura',
subdir: 'cobertura'
},
{
type: 'lcovonly',
subdir: 'lcov'
}
]
}
});
};
And the relevant part of my webpack test config
{
devtool: 'inline-source-map',
externals: {
cheerio: 'window',
'react/lib/ExecutionEnvironment': true,
'react/addons': true,
'react/lib/ReactContext': true,
},
module: {
preLoaders: [
{
test: /\.(js|jsx)$/,
loader: 'isparta-loader',
include: [
this.srcPathAbsolute
]
}
],
loaders: [
{
test: /\.cssmodule\.css$/,
loaders: [
'style',
'css?modules&importLoaders=1&localIdentName=[name]-[local]-[hash:base64:5]'
]
},
{
test: /^.((?!cssmodule).)*\.css$/,
loader: 'null-loader'
},
{
test: /\.(sass|scss|less|styl|png|jpg|gif|mp4|ogg|svg|woff|woff2)$/,
loader: 'null-loader'
},
{
test: /\.json$/,
loader: 'json'
},
{
test: /\.ts(x?)$/,
exclude: /node_modules/,
loader: ['babel', 'ts-loader']
},
{
test: /\.(js|jsx)$/,
loader: 'babel-loader',
query: {
presets: ['airbnb']
},
include: [].concat(
this.includedPackages,
[
this.srcPathAbsolute,
this.testPathAbsolute
]
)
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"test"'
})
]
}

After a couple days of soul and google searching amongst hundreds of browser tabs, I have found a working solution. This is using TypeScript 2.x and Webpack 2.x. test.js is my entry point. It could just as easily be test.ts (and it will be eventually). In that entry point, I load my *.spec.js and *.spec.ts files. And then those files import whichever source they need to test from. I have put all the webpack config in the karma.conf.js so it's easier to see:
let myArgs = require('yargs').argv;
let path = require('path');
let webpack = require('webpack');
module.exports = function(config) {
const REPORTS_PATH = myArgs.reportsPath ? myArgs.reportsPath :path.join(__dirname, 'build');
config.set({
basePath: '',
frameworks: ['jasmine', 'es6-shim'],
files: [
'./test.js'
],
exclude: [],
reporters: ['progress', 'spec', 'coverage', 'junit', 'coverage-istanbul'],
preprocessors: {
'./test.js': ['webpack', 'sourcemap']
},
webpackServer: {
noInfo: true // prevent console spamming when running in Karma!
},
webpack: {
devtool: 'inline-source-map',
resolve: {
modules: [
path.resolve('./node_modules'),
path.resolve('./')
],
extensions: ['.js', '.ts', '.css', '.scss']
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
})
],
module: {
rules: [
{
enforce: 'pre',
test: /\.js$/,
use: 'source-map-loader',
exclude: [/node_modules/]
},
{
test: /\.ts$/,
use: [{
loader: 'awesome-typescript-loader',
options: {
module: 'commonjs'
},
}]
},
{
test: /\.js$/,
use: [{
loader: 'awesome-typescript-loader',
options: {
entryFileIsJs: true,
transpileOnly: true
}
}],
exclude: [/node_modules/],
},
{
enforce: 'post',
test: /\.(js|ts)$/,
use: [{
loader: 'istanbul-instrumenter-loader',
options: {
esModules: true
}
}],
exclude: [/node_modules/, /\.spec\.(js|ts)$/, /test/]
},
{ test: /\.html/, use: 'raw-loader' },
{ test: /\.(s)?css$/, use: 'null-loader' },
{ test: /\.(png|jpg|jpeg|gif|svg|pdf)$/, use: 'null-loader' },
{ test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, use: 'null-loader' },
{ test: /\.(ttf|eot)(\?v=[0-9]\.[0-9]\.[0-9])?$/, use: 'null-loader' },
{ test: /\.json$/, use: 'null-loader' }
]
}
},
coverageReporter: {
type: 'in-memory'
},
coverageIstanbulReporter: {
//TODO: Figure out why the 'html' reporter blows up with istanbul-reports (something with absolute path copying files)
reports: ['text-summary', 'cobertura'],
// base output directory
dir: REPORTS_PATH,
fixWebpackSourcePaths: true,
'report-config': {
cobertura: {
file: 'coverage.xml'
},
'text-summary': {
file: null
}
}
},
junitReporter: {
outputDir: `${REPORTS_PATH}/junit/`,
outputFile: 'jasmine-results.xml'
},
// Hide webpack build information from output
webpackMiddleware: {
stats: {
chunkModules: false,
colors: true
},
noInfo: 'errors-only'
},
colors: true,
logLevel: config.LOG_ERROR,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
autoWatchBatchDelay: 400
});
};
So, the key pieces here are awesome-typescript-loader, karma-coverage-istanbul-reporter, source-map-loader, and in the tsconfig.json, you want to set these in compilerOptions:
"inlineSourceMap": true,
"sourceMap": false,
I indicated a TODO about the html report. It DOES work but I couldn't get it to output to a custom directory (subdir) with TypeScript files as a part of it. JavaScript only worked fine. Could be a windows-specific problem with istanbul-reports. If you add html to the reports array under coverageIstanbulReporter, you should see it in your project dir but may have problems putting it in REPORTS_PATH.
It's also worth noting that I had a lot of luck using karma-remap-coverage instead of karma-coverage-istanbul-reporter but the former would not correctly generate cobertura reports for coverage which is what I needed for Jenkins.

Related

How to build react js with only one chunk

I have a react js project and im actually dont know how to set webpack settings. After i run "npm run build" i get 10 js files names 1.js 2.js 3.js with main.js. I dont know how to bundle them. I searched some questions but couldnt solve it. Its not my first project in react but i didnt try to
build with webpack before. Thanks for helps.
My webpack.prod.js ;
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ResourceManifestPlugin = require('webpack-fivem-manifest');
module.exports = require('./webpack.common')({
mode: 'production',
// In production, we skip all hot-reloading stuff
entry: [path.join(process.cwd(), 'src/app.js')],
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html',
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
inject: true,
}),
new ResourceManifestPlugin(),
],
performance: {
assetFilter: assetFilename =>
!/(\.map$)|(^(main\.|favicon\.))/.test(assetFilename),
},
});
My webpack.common.js :
const path = require('path');
const webpack = require('webpack');
module.exports = options => ({
mode: options.mode,
entry: options.entry,
output: {
path: path.resolve(process.cwd(), 'html'),
filename: '[name].js',
},
module: {
rules: [
{
test: /\.jsx?$/, // Transform all .js and .jsx files required somewhere with Babel
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
// Preprocess our own .css files
// This is the place to add your own loaders (e.g. sass/less etc.)
// for a list of loaders, see https://webpack.js.org/loaders/#styling
test: /\.css$/,
exclude: /node_modules/,
use: ['style-loader', 'css-loader'],
},
{
// Preprocess 3rd party .css files located in node_modules
test: /\.css$/,
include: /node_modules/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(eot|otf|ttf|woff|woff2)$/,
use: 'file-loader',
},
{
test: /\.svg$/,
use: [
{
loader: 'svg-url-loader',
options: {
// Inline files smaller than 10 kB
limit: 10 * 1024,
noquotes: true,
},
},
],
},
{
test: /\.(jpg|png|gif)$/,
use: [
{
loader: 'url-loader',
options: {
// Inline files smaller than 10 kB
limit: 10 * 1024,
},
},
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
enabled: false,
// NOTE: mozjpeg is disabled as it causes errors in some Linux environments
// Try enabling it in your environment by switching the config to:
// enabled: true,
// progressive: true,
},
gifsicle: {
interlaced: false,
},
optipng: {
optimizationLevel: 7,
},
pngquant: {
quality: '65-90',
speed: 4,
},
},
},
],
},
{
test: /\.html$/,
use: 'html-loader',
},
{
test: /\.(mp4|webm)$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
},
},
},
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: `ifdef-loader`,
options: {
DEBUG: options.mode !== 'production',
version: 3,
'ifdef-verbose': true, // add this for verbose output
'ifdef-triple-slash': true, // add this to use double slash comment instead of default triple slash
},
},
],
},
],
},
plugins: options.plugins.concat([
// Always expose NODE_ENV to webpack, in order to use `process.env.NODE_ENV`
// inside your code for any environment checks; Terser will automatically
// drop any unreachable code.
new webpack.EnvironmentPlugin({
NODE_ENV: 'development',
}),
]),
resolve: {
modules: ['src', 'node_modules'],
extensions: ['.js', '.jsx', '.react.js'],
},
});

How can I get hot reloading (HMR) running with Webpack 5?

I am trying to get HMR running with webpack v5, but it does not work. When I modify and save a file, webpack re-compiles the project correctly, but the frontend does not update.
I read this article and followed the instructions: https://webpack.js.org/guides/hot-module-replacement/
This is my webpack config:
{
mode: 'development',
entry: {
babelpoly: 'babel-polyfill',
app: [ './src/index.js', './src/app.js' ]
},
plugins: [
BundleStatsWebpackPlugin { ... },
DefinePlugin { ... },
HtmlWebpackPlugin { ... }
],
stats: { ... },
output: {
path: '[pathTo]/dist',
filename: '[name].bundle.js',
chunkFilename: '[name].bundle.js'
},
optimization: {
splitChunks: { chunks: 'all' },
runtimeChunk: 'single',
usedExports: true,
mergeDuplicateChunks: true
},
module: {
rules: [
{
test: /\.(s[ac]ss)$/i,
include: path.resolve(ROOT, 'src'),
use: [
'style-loader', // Creates `style` nodes from JS strings
{
loader: 'css-loader', // Translates CSS into CommonJS
options: {
modules: {
mode: 'local',
localIdentName: devMode
? '[name]_[local]-[hash:base64:3]'
: '[local]-[hash:base64:5]',
},
},
},
{
loader: 'sass-loader', // compiles Sass to CSS
options: {
implementation: require('sass'),
},
},
],
},
{
test: /\.css$/,
include: [path.join(ROOT, 'node_modules')],
use: ['style-loader', 'css-loader'],
},
{
test: /\.(png|svg|jpg|gif)$/,
use: ['file-loader'],
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ['file-loader'],
},
{
test: /\.m?jsx?$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'#babel/preset-env',
{
useBuiltIns: 'entry',
corejs: 3,
},
],
'#babel/preset-react',
],
plugins: [['#babel/plugin-proposal-class-properties', {loose: true}]],
},
},
},
],
},
resolve: {
extensions: [ ... ],
alias: {
...
},
modules: [
...
]
},
devtool: 'inline-source-map',
devServer: {
port: 3003,
contentBase: '[pathTo]/dist',
host: '0.0.0.0',
hot: true,
compress: true,
disableHostCheck: true,
historyApiFallback: true,
overlay: { warnings: true, errors: true },
stats: { ... }
}
}
I am using:
webpack 5.7.0
webpack-cli 4.2.0
react 16.13
node 14.15.1
npm 6.14.8
I start webpack with this command: webpack serve --host 0.0.0.0 --config config/webpack.dev.js
What do I do wrong? Thanks for your help. :)
I believe it's a bug in webpack-dev-server v3 https://github.com/webpack/webpack-dev-server/issues/2758 when you're using it with webpack 5 and browserslist. You can wait for webpack-dev-server v4 https://github.com/webpack/webpack-dev-server/pull/2592#issuecomment-734400875, it will come soon, or use target: 'web' for the moment under your development mode.

fetch-mock Response Does Not Have JSON Method

I'm trying to get fetch-mock to work in my testing setup, but the response object looks different than expected.
Specifically, it does not have a json method.
Here's the class I'm calling with the actual call to fetch:
import 'whatwg-fetch';
export class Store {
load() {
return fetch("/api", {
method: "get",
headers: {'Content-Type': 'application/json'}
});
}
}
Here is a snippet from the test code:
import fetchMock from 'fetch-mock';
fetchMock.get("*", {code: "1", name: "cde", image: 'hello.jpeg'})
this.store = new Store;
this.store.load().then(response => console.log(response._bodyInit()));
console.log('hello', this.brandStore.brands[0]);
fetchMock.restore();
The console will print:
Response{
type: 'default',
status: 200,
ok: true,
statusText: 'OK',
headers: Headers{map: Object{content-type: ...}},
url: '/api',
_bodyInit: '{"code":"1","name":"cde","image":"hello.jpeg"}',
_bodyText: '{"code":"1","name":"cde","image":"hello.jpeg"}'
}
This error also gets output:
'Unhandled promise rejection', TypeError{line: 4005, sourceURL: 'http://localhost:9876/base/test/stores/BrandStore.spec.js?572679e0c28b63839a5f5829ef1a60b8d62f8b84', stack: 'http://localhost:9876/base/test/stores/BrandStore.spec.js?572679e0c28b63839a5f5829ef1a60b8d62f8b84:4005:168
run#http://localhost:9876/base/node_modules/babel-polyfill/dist/polyfill.js?00cf5c53ec5ebd52d0521aed551c593eef05a0d6:3911:29
http://localhost:9876/base/node_modules/babel-polyfill/dist/polyfill.js?00cf5c53ec5ebd52d0521aed551c593eef05a0d6:3924:31
flush#http://localhost:9876/base/node_modules/babel-polyfill/dist/polyfill.js?00cf5c53ec5ebd52d0521aed551c593eef05a0d6:1209:11'}
My Karma (with Webpack config) looks like:
const webpack = require('webpack');
var argv = require('yargs').argv;
var path = require('path');
let srcPath = path.join(__dirname, '/../src/');
const webpackConfig = {
devtool: 'inline-source-map',
resolve: {
// allow us to import components in tests like:
// import Example from 'components/Example';
root: path.resolve(__dirname, './src'),
// allow us to avoid including extension name
extensions: ['', '.js', '.jsx', '.css', '.scss', '.json'],
// required for enzyme to work properly
alias: {
'sinon': 'sinon/pkg/sinon',
}
},
module: {
// don't run babel-loader through the sinon module
noParse: [
/node_modules\/sinon\//
],
isparta: {
embedSource: true,
noAutoWrap: true,
// these babel options will be passed only to isparta and not to babel-loader
babel: {
presets: ['es2015', 'stage-0', 'react']
}
},
// run babel loader for our tests
loaders: [
{
test: /\.(js|jsx)$/,
loader: 'babel',
exclude: path.resolve(__dirname, 'node_modules'),
query: {
plugins: ['transform-decorators-legacy'],
presets: ['es2015', 'airbnb', 'stage-1', 'react']
}
},
{
test: /\.json$/, loader: 'json'
},
{
test: /\.scss$/,
exclude: /[\/\\](node_modules|bower_components|public)[\/\\]/,
loaders: [
'style?sourceMap',
'css?modules&importLoaders=1&localIdentName=[local]',
'postcss',
'sass'
]
},
{
test: /\.css$/,
exclude: /[\/\\](node_modules|bower_components|public)[\/\\]/,
loaders: [
'style?sourceMap',
'css?modules&importLoaders=1&localIdentName=[local]'
]
}
],
preLoaders: [ { //delays coverage til after tests are run, fixing transpiled source coverage error
test: /\.(jsx|js)$/,
include: path.resolve('src/'),
loader: 'isparta',
} ]
},
plugins: [
new webpack.IgnorePlugin(/node-fetch/)
],
// required for enzyme to work properly
externals: {
'jsdom': 'window',
'react/lib/ExecutionEnvironment': true,
'react/lib/ReactContext': 'window',
'react/addons': true
},
};
module.exports = function(config) {
config.set({
browsers: ['PhantomJS'],
singleRun: !argv.watch,
frameworks: ['mocha', 'chai'],
reporters: ['spec', 'coverage'],
// include some polyfills for babel and phantomjs
files: [
'node_modules/whatwg-fetch/fetch.js',
'node_modules/babel-polyfill/dist/polyfill.js',
'./node_modules/phantomjs-polyfill/bind-polyfill.js',
'test/**/*.js'
],
preprocessors: {
// add webpack as preprocessor
'src/**/*.js': ['webpack', 'sourcemap'],
'test/**/*.js': ['webpack', 'sourcemap']
},
// A lot of people will reuse the same webpack config that they use
// in development for karma but remove any production plugins like UglifyJS etc.
// I chose to just re-write the config so readers can see what it needs to have
webpack: webpackConfig,
webpackMiddleware: {
noInfo: true
},
coverageReporter: {
dir: 'coverage/',
reporters: [
{ type: 'html' },
{ type: 'text' }
]
},
// tell karma all the plugins we're going to be using to prevent warnings
plugins: [
'karma-mocha',
'karma-chai',
'karma-webpack',
'karma-phantomjs-launcher',
'karma-spec-reporter',
'karma-sourcemap-loader',
'karma-coverage'
]
});
};

Why doesn't my karma support for...of statement?

I have setup karma as my unit test framework. But it doesn't compile below code:
let {btnData} = this.props
let buttons = []
for (let btn of btnData) {
btn = {...btnCategories[btn.type], ...btn}
buttons.push(btn)
}
I got below error on the line of for loop. Why doesn't it recognise for...of statement?
Can't find variable: Symbol
Below is my karma config file. My project includes webpack, reactjs and use karma, mocha, chai, enzyme as test libraries.
var path = require('path')
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['mocha', 'chai'],
files: [
'../test/**/*.js'
],
preprocessors: {
// add webpack as preprocessor
'../src/**/*.js': ['webpack', 'sourcemap'],
'../test/**/*.js': ['webpack', 'sourcemap'],
'../src/**/*.less': ['webpack', 'sourcemap']
},
webpack: { //kind of a copy of your webpack config
devtool: 'inline-source-map', //just do inline source maps instead of the default
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel',
exclude: /node_modules/,
query: {
presets: ['airbnb']
}
},
{
test: /\.json$/,
loader: 'json',
},{
test: /\.less$/,
loader: "style!css!less",
},
]
},
externals: {
'react/lib/ExecutionEnvironment': true,
'react/lib/ReactContext': true,
'react/addons': true
},
resolve: {
alias: {
app: path.join(__dirname, '../src/js')
}
}
},
webpackServer: {
noInfo: true //please don't spam the console when running in karma!
},
plugins: [
'karma-webpack',
'karma-jasmine',
'karma-sourcemap-loader',
'karma-chrome-launcher',
'karma-phantomjs-launcher',
'karma-mocha',
'karma-chai',
'karma-mocha-reporter'
],
babelPreprocessor: {
options: {
presets: ['airbnb']
}
},
reporters: ['mocha'],
// reporter options
mochaReporter: {
colors: {
success: 'blue',
info: 'bgGreen',
warning: 'cyan',
error: 'red'
},
symbols: {
success: '+',
info: '#',
warning: '!',
error: 'x'
}
},
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
})
};

How Can I Exclude Spec Files from Code Coverage with Karma using Webpack and Babel?

Problem
I'm working on a small react-redux project configured with webpack, babel, and karma. I added code coverage to karma, but I couldn't find a way to exclude the test files from coverage. So my code coverage has spec files.
How can I exclude these spec files from coverage?
I tried to use a regex expression to exclude the spec files, but since it was being loaded by webpack, it didn't work.
tests.webpack.js
const context = require.context('./src', true, /.+\Spec\.js$/);
context.keys().forEach(context);
module.exports = context;
webpack.config.js
module.exports = {
entry: './src/index.js',
output: {
path: __dirname,
filename: 'dist/bundle.js'
},
devtool: 'source-map',
resolve: {
extensions: ['', '.js', '.scss'],
modulesDirectories: [
'node_modules',
'src'
]
},
module: {
preLoaders: [
{
test: /\.js$/,
loader: 'eslint-loader',
exclude: /node_modules/
}
],
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
],
},
};
karma.config.js
var path = require('path');
module.exports = function (config) {
config.set({
browsers: ['PhantomJS'],
singleRun: true,
frameworks: ['mocha', 'sinon-chai'],
files: [
'tests.webpack.js'
],
preprocessors: {
'tests.webpack.js': ['webpack', 'sourcemap']
},
reporters: ['mocha', 'osx', 'coverage'],
webpack: {
module: {
preLoaders: [
{
test: /\.js$/,
exclude: [
path.resolve('src/'),
path.resolve('node_modules/')
],
loader: 'babel'
},
{
test: /\.js$/,
include: path.resolve('src/'),
loader: 'isparta'
}
]
}
},
webpackServer: {
noInfo: true
},
coverageReporter: {
type: 'html',
dir: 'coverage/'
}
});
};
This is how I do it on my project, where I have all my tests in __test__ folders included with each component. You should be able to change this to a regex something like this /\.spec.js$/.
karmaConfig.webpack.module.preLoaders = [{
test : /\.(js|jsx)$/,
include : new RegExp(config.dir_client),
loader : 'isparta',
exclude : [
/node_modules/,
/__test__/,
],
}];
In your case you need to add exclude to this bit of your config.
{
test: /\.js$/,
include: path.resolve('src/'),
loader: 'isparta'
}
I solved this by filtering out the results I didn't want by placing a .filter() before the .forEach(context).
const includes = require('lodash/includes');
const context = require.context('./src', true, /\.test\.js$/);
context.keys()
.filter(file => includes(file, './api/') === false)
.forEach(context);
You could also write it directly into the .forEach().
const includes = require('lodash/includes');
const context = require.context('./src', true, /\.test\.js$/);
context.keys()
.forEach(file => {
if (includes(file, './api/') === false) {
context(file);
}
});
If you don't use Lodash, you can use:
file.indexOf('./api/') === -1

Categories

Resources