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, '/').
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)
})
],
I have a Node Js server that I bundled with webpack with the following config:
module.exports = {
entry: './build/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
devtoolModuleFilenameTemplate: '../[resource-path]',
},
target: 'node',
node: {
__dirname: true,
},
externals: {
kcors: 'kcors',
'koa-bodyparser': 'koa-bodyparser'
},
module: {
rules: [
{
test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre"
},
],
},
resolve: {
extensions: ['.js'],
},
// plugins: [
// new webpack.DefinePlugin({
// "process.env": JSON.stringify(dotenv.parsed)
// }),
// ],
devtool: 'eval-source-map'
};
This created a single file main.js inside dist
Everything looks good so far. I manually tested the server and it works.
Now, before I used webpack, I have a set of unit tests that I named with the .spec.js suffix inside my entry folder (the build/ folder) and each .js file has each own .spec file sibling located in the same directory of the .js.
To run theses unit test, I used mocha with the command: mocha build/**/*.spec.js --recursive --timeout 20000
Since I am new to webpack and the concept of bundling, how do I run the same tests from the main.js file ? I want to make sure that all tests are still passing in the bundled file
use
npm install mocha mocha-loader
config your webpack.config.js file like:
module.exports = {
entry: './entry.js',
output: {
path: __dirname,
filename: 'bundle.js',
},
module: {
rules: [
{
test: /test\.js$/,
use: 'mocha-loader',
exclude: /node_modules/,
},
],
},
};
then, call your test file from your main file like:
(into app.js)
import test from 'allMyTests';
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);
};
I am trying to setup unit testing for a JavaScript plugin that is based on AngularJS. The plugin is bundled with Browserify via Gulp. It depends on external libraries that are injected with wiredep and gulp-inject from the bower_components folder. This is all working beautifully in the generated bundle, but if I try to run a Karma unit test via gulp, I get the following error:
Uncaught TypeError: angular.module is not a function at /tmp/94dbea5947f4758ab1ee6935e2f4b3f1.browserify:365 <- app/js/services/index.js:9:0
In this file, angular is loaded with var angular = require('angular');, and a console.log(angular) gives an empty object.
My karma.conf.js:
'use strict';
const istanbul = require('browserify-istanbul');
const isparta = require('isparta');
const mainBowerFiles = require('main-bower-files');
const karmaBaseConfig = {
basePath: '../',
frameworks: ['jasmine', 'browserify'],
preprocessors: {
'app/js/**/*.js': ['browserify', 'coverage'],
'**/*.html': ['ng-html2js']
},
browserify: {
debug: true,
extensions: ['.js'],
transform: [
[["babelify", {"ignore": "/\/bower_components\//"}]],
'browserify-ngannotate',
'bulkify',
'debowerify',
'brfs',
istanbul({
instrumenter: isparta,
ignore: ['**/bower_components/**', '**/node_modules/**', '**/test/**']
})
]
},
ngHtml2JsPreprocessor: {
stripPrefix: 'app/',
moduleName: 'templates'
},
plugins: [
'karma-jasmine',
'karma-coverage',
'karma-browserify',
'karma-ng-html2js-preprocessor',
'karma-chrome-launcher'
],
files: mainBowerFiles({
filter: '**/*.js',
paths: {
bowerDirectory: 'bower_components',
bowerrc: '.bowerrc',
bowerJson: 'bower.json'
}
}).concat([
// app-specific code
'app/js/main.js',
// 3rd-party resources
'node_modules/angular-mocks/angular-mocks.js',
// test files
'test/unit/**/*.js'
]),
exclude: [],
reporters: ['progress', 'coverage'],
port: 9876,
colors: true,
autoWatch: false,
browsers: ['Chrome'],
singleRun: true
};
const customLaunchers = {
chrome: {
base: 'SauceLabs',
browserName: 'chrome'
}
};
const ciAdditions = {
sauceLabs: {
testName: 'Karma Unit Tests',
startConnect: false,
build: process.env.TRAVIS_BUILD_NUMBER,
tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
},
browsers: Object.keys(customLaunchers),
customLaunchers: customLaunchers,
reporters: ['progress', 'coverage', 'saucelabs']
};
module.exports = function (config) {
const isCI = process.env.CI;
config.set(isCI ? Object.assign(karmaBaseConfig, ciAdditions) : karmaBaseConfig);
};
All main application files are located under app/, bower files in bower_components/, node modules in node_modules/ and test specs in test/unit/.
It is based on this boilerplate: https://github.com/jakemmarsh/angularjs-gulp-browserify-boilerplate.
The error occurs just after Karma has launched Chrome, but before any unit test are executed (I checked with console.log in the unit test).
Any help would be greatly appreciated.
Finally solved it.
unit.js (gulp unit task):
'use strict';
import config from '../config';
import path from 'path';
import gulp from 'gulp';
import {Server} from 'karma';
gulp.task('unit', ['copy-bower-components',
'styles',
'images',
'fonts',
'api',
'views',
'browserify',
'inject'
], function (done) {
new Server({
configFile: path.resolve(__dirname, '../..', config.test.karma),
singleRun: true
}, function (exitCode) {
console.log('Karma has exited with ' + exitCode);
done();
}).start();
});
The key here was to run browserify before the unit tests are started. karma.conf.js:
// Karma configuration
// Generated on Sat Jan 23 2016 16:43:48 GMT+0100 (CET)
module.exports = function (config) {
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', 'browserify'],
// list of files / patterns to load in the browser
files: [
"bower_components/tether/dist/js/tether.js",
"bower_components/jquery/jquery.js",
"bower_components/bootstrap/dist/js/bootstrap.js",
"bower_components/jquery-ui/jquery-ui.js",
"bower_components/rangy/rangy-core.js",
"bower_components/rangy/rangy-classapplier.js",
"bower_components/rangy/rangy-highlighter.js",
"bower_components/rangy/rangy-selectionsaverestore.js",
"bower_components/rangy/rangy-serializer.js",
"bower_components/rangy/rangy-textrange.js",
"bower_components/angular/angular.js",
"bower_components/textAngular/dist/textAngular.js",
"bower_components/textAngular/dist/textAngular-sanitize.js",
"bower_components/textAngular/dist/textAngularSetup.js",
"bower_components/KaTeX/dist/katex.min.js",
"bower_components/angular-bootstrap/ui-bootstrap-tpls.js",
"bower_components/angular-translate/angular-translate.js",
"bower_components/angular-translate-loader-static-files/angular-translate-loader-static-files.js",
"bower_components/angular-cookies/angular-cookies.js",
"bower_components/angular-translate-storage-cookie/angular-translate-storage-cookie.js",
"bower_components/angular-translate-storage-local/angular-translate-storage-local.js",
"bower_components/angular-translate-handler-log/angular-translate-handler-log.js",
"bower_components/angular-dynamic-locale/src/tmhDynamicLocale.js",
"bower_components/angular-tour/dist/angular-tour-tpls.min.js",
"bower_components/ng-sortable/dist/ng-sortable.js",
"bower_components/moment/moment.js",
"bower_components/angular-moment/angular-moment.js",
"bower_components/KaTeX/dist/contrib/auto-render.min.js",
'dist/js/main.js',
'node_modules/angular-mocks/angular-mocks.js',
'test/unit/**/*.spec.js'
],
browserify: {
debug: true,
transform: [
'babelify',
'brfs',
'bulkify'
]
},
// list of files to exclude
exclude: ['karma.conf.js'],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'test/unit/**/*.spec.js': ['browserify']
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// 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: ['Chrome'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
urlRoot: '/__karma__/'
})
};