I have the following karma config file (relevant properties):
files: [
{ pattern: './src/*.html', watched: true, served: true, included: false },
// polyfill for es6 Promise
'node_modules/es6-promise/dist/es6-promise.auto.js',
// polyfill for fetch.js
'node_modules/whatwg-fetch/fetch.js',
'./src/**/*.ts'
],
preprocessors: {
'**/*.html': ['html2js'],
'**/*.ts': 'karma-typescript',
'karma-babel-preprocessor': ['babel']
},
I'm using an ES6 Module project (no React, Angular2+ setup) and trying to set up my tests to run properly.
The main problem is that in my .ts file (not spec.ts --> file aimed for testing only) I have a doccument.getElementById('').someAttribute which breaks at test run time because it's null. The reason this happens is because index.html hasn't been set up yet by karma.
How do I solve the problem of index.html first being built and then all other .ts files...
Error after running npm test: *
"message": "Uncaught TypeError: Cannot set property 'innerHTML' of
null\nat
Have you tried instead of 'karma-babel-preprocessor' and 'karma-typescript' to use 'webpack' with transpilation either Babel or Typescript?
Here is https://www.npmjs.com/package/awesome-typescript-loader that can be used for TypeScript handling with webpack.
There might be kind of conflict also of using both Babel and Typescript because Typescript can transpile without babel but it is something that you can handled in webpack config. Here is how 'karma.conf' might look like with Babel:
const webpack = require('webpack');
module.exports = function (config) {
var configuration = {
browsers: ['Chrome'],
files: [
{ pattern: 'test/**/*.js', watched: true },
],
frameworks: ['jasmine'],
preprocessors: {
'test/**/*.js': ['webpack'],
},
webpack: {
module: {
loaders: [
{ test: /\.js/, exclude: /node_modules/, loader: 'babel-loader' }
]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('test')
}
})
],
watch: true
},
webpackServer: {
noInfo: true
}
};
config.set(configuration);
};
Related
I have the following structure:
└── src
├── tsconfig.json
├── core
│ ├── [...].ts
└── ui
├── [...].tsx
└── tsconfig.json
In the frontend I import a small number of modules from the core. These modules, and any dependent modules, are compliant with both tsconfig files.
tsc and eslint pass with no errors and webpack builds the desired output file. So far so good.
However when webpack builds it throws loads of type errors for other backend modules.
How do I suppress these errors? I tried excluding src/core from babel-loader and including the required modules but I was still getting the same errors.
/// webpack.config.js
/* eslint-disable #typescript-eslint/no-var-requires */
const path = require('path');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/ui',
output: {
path: path.resolve(__dirname, './ui-dist'),
},
// Enable sourcemaps for debugging webpack's output.
devtool: 'source-map',
resolve: {
// Add '.ts' and '.tsx' as resolvable extensions.
extensions: ['.ts', '.tsx', '.js', '.jsx'],
},
module: {
rules: [
{
test: /\.(j|t)sx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
babelrc: false,
presets: [
[
'#babel/preset-env',
{ targets: { browsers: 'last 2 versions' } }, // or whatever your project requires
],
'#babel/preset-typescript',
'#babel/preset-react',
],
plugins: [
// plugin-proposal-decorators is only needed if you're using experimental decorators
// in TypeScript
['#babel/plugin-proposal-decorators', { legacy: true }],
['#babel/plugin-transform-runtime', { legacy: true }],
['#babel/plugin-proposal-class-properties', { loose: true }],
'react-hot-loader/babel',
],
},
},
},
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{
enforce: 'pre',
test: /\.js$/,
loader: 'source-map-loader',
},
],
},
plugins: [
new ForkTsCheckerWebpackPlugin({
tsconfig: path.resolve(__dirname, './src/ui/tsconfig.json'),
eslint: true,
/** Options to supply to eslint https://eslint.org/docs/developer-guide/nodejs-api#cliengine */
// eslintOptions: EslintOptions;
}),
new HtmlWebpackPlugin({
title: 'development',
template: './src/ui/template.html',
}),
],
// When importing a module whose path matches one of the following, just
// assume a corresponding global variable exists and use that instead.
// This is important because it allows us to avoid bundling all of our
// dependencies, which allows browsers to cache those libraries between builds.
// externals: {
// react: 'React',
// 'react-dom': 'ReactDOM',
// },
devServer: {
contentBase: path.resolve(__dirname, './ui-dist'),
},
};
EDIT: I suppose I am referencing these modules throwing an error by using import type { x } from '../core/index.ts'. Perhaps I need to find a way for babel-loader to skip scanning type imports.
removing ForkTsCheckerWebpackPlugin did the trick. Type checking is done when calling tsc in any case.
*** Edit - Ignore if you want answer only ***
Seeing as this question is still receiving views and upvotes I feel responsible to share some knowledge after going through the webpack rabbithole and coming out the other end.
If you:
are building a greenfield/early-stage modern javascript project
are considering migrating from create-react-app
don't have much experience with bundling
do not need advanced features like module federation or server side rendering (which doesn't need webpack anymore)
Consider using the next generaton bundlers such as vite/parcel (easy setup), esbuild/rollup (more setup required)
Webpack was/is a fantastic contribution to the frontend world and I'm glad I learned all its intricacies, however, the new bundlers are much faster during development and easier to mantain. It's great when it works but for those inexperienced with it; despite fantastic docs the learning curve can make it a horrible pain to debug.
To clarify, I'm not a maintainer on any of these projects - just a dev who enjoys good tooling. In today's landscape, webpack is comparable to using a sledgehammer to crack a nut.
*** End of Edit ***
Webpack newbie here, I was told by webpack cli that I needed to provide an alias for crypto as webpack no longer includes default node libraries. Now I'm getting this error, other answers haven't helped so much. crypto-browserify is trying to access process.browser. Can anyone shed more light? I was told by cli to install stream-browserify too so i did.
React v17, Babel 7.12.9, webpack 5.6.0
webpack.common.js
const paths = require('./paths');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const dotenv = require('dotenv-webpack');
module.exports = {
entry: [paths.src + '/index.js'],
output: {
path: paths.build,
filename: '[name].bundle.js',
publicPath: '/',
},
plugins: [
new dotenv(),
new CleanWebpackPlugin(),
new CopyWebpackPlugin({
patterns: [
{
from: paths.public,
to: 'assets',
globOptions: {
ignore: ['*.DS_Store'],
},
},
],
}),
new HtmlWebpackPlugin({
title: 'Webpack Boilerplate',
// favicon: paths.src + '/images/favicon.png',
template: paths.src + '/template.html',
filename: 'index.html',
}),
],
resolve: {
fallback: {
crypto: require.resolve('crypto-browserify'),
stream: require.resolve('stream-browserify'),
},
},
module: {
rules: [
// javascript
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
// images
{
test: /\.(?:ico|gif|png|jpg|jpeg)$/i,
type: 'asset/resource',
},
// Fonts and SVGs
{
test: /\.(woff(2)?|eot|ttf|otf|svg|)$/,
type: 'asset/inline',
},
// CSS, PostCSS, and Sass
{
test: /\.(scss|css)$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
esModule: true,
sourceMap: true,
importLoaders: 1,
modules: {
auto: true,
namedExport: true,
},
},
},
{ loader: 'postcss-loader', options: { sourceMap: true } },
{ loader: 'sass-loader', options: { sourceMap: true } },
],
},
],
},
};
webpack.dev.js
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const common = require('./webpack.common');
module.exports = merge(common, {
mode: 'development',
// Control how source maps are generated
devtool: 'inline-source-map',
// Spin up a server for quick development
devServer: {
historyApiFallback: true,
contentBase: paths.build,
open: true,
compress: true,
hot: true,
port: 8080,
},
plugins: [
// Only update what has changed on hot reload
new webpack.HotModuleReplacementPlugin(),
],
});
In webpack 5 automatic node.js polyfills are removed. In the migration docs it is mention that
Try to use frontend-compatible modules whenever possible.
It's possible to manually add a polyfill for a node.js core module.
An error message will give a hint on how to achieve that.
Package authors: Use the browser field in package.json to make a
package frontend-compatible. Provide alternative
implementations/dependencies for the browser.
See this issue.
Now you can refer this PR and check the libs that were removed and install them.
Next add alias for the lib in your webpack config.
For ex.
resolve: {
alias: {
process: "process/browser"
}
}
Update:
This can also be done using ProvidePlugin
package.json
"devDependencies": {
...
"process": "0.11.10",
}
webpack.config.js
module.exports = {
...
plugins: [
new webpack.ProvidePlugin({
process: 'process/browser',
}),
],
}
npm i process was all I needed.
Hope the correction I proposed will be accepted and released soon
I have this problem for HtmlWebpackPlugin, I added 'templateParameters' parameter to HtmlWebpackPlugin and it was fixed for me:
new HtmlWebpackPlugin({
baseUrl: '/',
template: 'app/index.html',
templateParameters(compilation, assets, options) {
return {
compilation,
webpack: compilation.getStats().toJson(),
webpackConfig: compilation.options,
htmlWebpackPlugin: {
files: assets,
options,
},
process,
}
},
chunksSortMode: 'auto',
minify: {
collapseWhitespace: false,
},
cache: true,
}),
1. npm i dotenv-webpack
2. //Define dotenv in your webpack config
const Dotenv = require('dotenv-webpack');
plugins: [
new Dotenv({
path: './.env', // Path to .env file (this is the default)
safe: true, // load .env.example (defaults to "false" which does not use dotenv-safe)
})
],
Using babel 7.5.5, core-js 3.1.4 and webpack 4.38.0, how can I exclude core-js from transpiling?
I do not want to exclude node_modules altogether since I have libs that need transpiling
If I use exclude: /node_modules\/(core-js)/, a core-js module throws
TypeError: $ is not a function
This leaves me with two other options.
Use includes instead, include my src directory and every dependency that needs transpiling one by one
Use useBuiltIns: entry instead of usage, since in this case exclude: /node_modules/\(core-js)/ works, and import core.js at the top of main.js
Both of these options don't really seem like good solutions to me since usage is "no longer experimental" since 7.4.
Is there any way to make it work using usage? Is it a bug in either babel-loader or babel? Or is my configuration at fault?
This is my Webpack config:
const path = require('path');
const webpack = require('webpack');
module.exports = {
mode: 'production',
entry: {
main: ['./src/main'],
},
output: {
path: path.resolve(__dirname, './build/'),
publicPath: '/build/'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules\/(core-js)/,
use: {
loader: 'babel-loader'
},
},
{
test: require.resolve('jquery'),
use: [
{
loader: 'expose-loader',
options: 'jQuery'
},
{
loader: 'expose-loader',
options: '$'
}
]
}
]
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
})
],
};
This is my babel config:
module.exports = function (api) {
api.cache(true);
return {
presets: [
[
'#babel/preset-env',
{
corejs: {
version: 3,
},
useBuiltIns: 'usage',
}
]
],
};
};
You can reproduce the error with the following repository: https://github.com/tomm1996/usebuiltins-exclude-test
You need to exclude both core-js and webpack/buildin from the Babel transpilation.
You can use the folling exclude Regexes:
exclude : [
/\bcore-js\b/,
/\bwebpack\/buildin\b/
]
Here is also a complete babel-loader configuration with some useful comments:
{
module : {
rules : [{
test : /\.js$/,
// Some module should not be transpiled by Babel
// See https://github.com/zloirock/core-js/issues/743#issuecomment-572074215
exclude : [
/\bcore-js\b/,
/\bwebpack\/buildin\b/
],
loader : "babel-loader",
options : {
babelrc : false,
// Fixes "TypeError: __webpack_require__(...) is not a function"
// https://github.com/webpack/webpack/issues/9379#issuecomment-509628205
// https://babeljs.io/docs/en/options#sourcetype
sourceType : "unambiguous",
presets : [
["#babel/preset-env", {
// Webpack supports ES Modules out of the box and therefore doesn’t require
// import/export to be transpiled resulting in smaller builds, and better tree
// shaking. See https://webpack.js.org/guides/tree-shaking/#conclusion
modules : false,
// Adds specific imports for polyfills when they are used in each file.
// Take advantage of the fact that a bundler will load the polyfill only once.
useBuiltIns : "usage",
corejs : {
version : "3",
proposals : true
}
}]
]
}
}
}
}
See https://github.com/zloirock/core-js/issues/743#issuecomment-572074215
Edit: Also if you try to use #babel/plugin-transform-runtime:
plugins : [
// Require the Babel runtime as a separate module to avoid the duplication
// https://webpack.js.org/loaders/babel-loader/#babel-is-injecting-helpers-into-each-file-and-bloating-my-code
["#babel/plugin-transform-runtime", {
// Requires #babel/runtime-corejs3
// https://babeljs.io/blog/2019/03/19/7.4.0#migration-from-core-js-2
corejs : { version: 3, proposals: true }
}],
}
You may run into a similar error:
Uncaught TypeError: _typeof2 is not a function
at _typeof (typeof.js:8)
at eval (sockjs.js:123)
at Object.eval (sockjs.js:131)
at eval (sockjs.js:6565)
at Object../node_modules/sockjs-client/dist/sockjs.js (main.js:13790)
at __webpack_require__ (main.js:70)
at eval (webpack://PUBLIC_ENGINE/(:8000/webpack)-dev-server/client/clients/SockJSClient.js?:110:14)
at Object../node_modules/webpack-dev-server/client/clients/SockJSClient.js (main.js:13874)
at __webpack_require__ (main.js:70)
at eval (webpack://PUBLIC_ENGINE/(:8000/webpack)-dev-server/client/socket.js?:56:41)
This can be solved by excluding #babel/runtime-corejs3 from the transpilation:
exclude : [
/\bcore-js\b/,
/\bwebpack\/buildin\b/,
/#babel\/runtime-corejs3/
]
I am using Karma with the karma-webpack plugin for bundling and transpiling with babel. When a test has an error, I get a nice message with a line number for the bundle, like the following:
Service: DocumentService
✗ gets the correct number of advisors clients
TypeError: undefined is not an object (evaluating 'GLOBALS.TESTING_ENV') (line
37)
This is great, but I cannot find where to access the bundle file and inspect the lines described.
I tried using the output option in the webpack config, but that doesn't seem to do anything.
Here is my karma.conf:
module.exports = function(config) {
config.set({
basePath: __dirname + '/',
frameworks: ['phantomjs-shim', 'jasmine'],
files: [
'./test/**/*spec.js'
],
preprocessors: {
'src/**/*.js': ['webpack'],
'test/**/*.js': ['webpack']
},
webpack: {
mode: 'development',
output: {
path: path.resolve(__dirname, './build/'),
filename: 'app-[name].js',
chunkFilename: 'app-vendors.[chunkhash].js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader']
}
]
}
},
reporters: ['spec'],
specReporter: {
suppressErrorSummary: false,
suppressFailed: false,
suppressPassed: false,
suppressSkipped: false,
showSpecTiming: false,
failFast: false
},
autoWatch: false,
browsers: ['PhantomJS'],
singleRun: true,
...
})
}
Where does the test bundle file build to? Is there a way I can configure it so that I can inspect the bundle?
Struggled with this as well and after revisiting this issue I finally solved it for me.
The files are not written to the disk because karma uses webpack-dev-middleware by default which keeps all files in memory. If you want the files to be emitted to the filesystem try
webpackMiddleware: {
writeToDisk: true,
}
Though in its current version karma-webpack overrides the output path with path.join(os.tmpdir(), '_karma_webpack_', indexPath, '/').
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?
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]
}
});
};
From your terminal screenshot, it looks like you're getting coverage on your individual files src/app.js (12.5%), src/environment.js (0%) and src/main.js (12.5%) in addition to the bundle file. It has to do with that extra line you added including the files that are in src/.
I suspect that if you go to your coverage directory and view that in the browser, you'll see more detailed results.
I'm not sure about the Aurelia app specifically, but to get individual file coverage, you want to have your tests run against your original files instead of the bundled version and you want that same path to your original files to also be in preprocessors.
files: [
...
'src/**/*.js',
'path/to/tests/*.spec.js'
...
],
preprocessors: {
'src/**/*.js': ['coverage']
}
This pattern (which you've done) is what tells Karma to load the individual files, and include them in the coverage. At this point, I wouldn't bother loading the bundled application code since you have all the raw stuff being loaded.
Also be sure to load them in the order they're probably bundled in so that you can be sure Karma can initialize the app in the first place.
files: [
'src/app.js',
'src/environment.js',
'src/main.js'
]
And if you are putting your specs named myTest.spec.js next to the files they test, you can use src/!(*.spec).js to make sure that you are including application code without the tests.
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 ;)
#lebolo These were the adjustments I had to make to get things working here:
aurelia_project/tasks/cover.js
Included a constant to vendors path in the beginning of cover function:
const VENDORS_PATH = __dirname + '/../../node_modules/';
Used VENDORS_PATH to include libs I depend on, in files:
files: [
{pattern: VENDORS_PATH + 'moment/min/moment.min.js', included: false},
{pattern: 'src/**/*.js', included: false},
{pattern: 'test/unit/**/*.js', included: false},
'test/aurelia-karma-cover.js'
]
Included test files in preprocessors:
preprocessors: {
'src/**/*.js': ['babel'],
'test/unit/**/*.js': ['babel']
}
test/unit/aurelia-karma-cover.js
Change the way url is built in requirejs.load function:
url = '/base/' + url; => url = ['/base', root, url].join('/');
Included one more function to config requirejs paths and calling it after patchRequireJS() call:
function configRequire() {
var VENDORS_PATH = '../node_modules/';
requirejs.config({
paths: {
'moment': VENDORS_PATH + 'moment/min/moment.min'
}
});
}