Supported technologies: Webpack

Wallaby.js provides two different methods to configure projects that use webpack: Wallaby.js built-in postprocessor, and via the open source wallaby-webpack postprocessor.

This page describes the new recommend built-in webpack postprocessor, however if you’re interested in how to use the open source wallaby-webpack postprocessor please find its documentation here.

The key difference between the two methods is that the built-in postprocessor integrates with webpack’s loaders to perform instrumentation which allows you to use the same loaders as you use in your test, development and production webpack configurations such as babel-loader for ES6/ES7/ES8, ts-loader or awesome-typescript-loader for TypeScript and coffee-loader for CoffeeScript. If you are using the open source wallaby-webpack postprocessor then unlike the built-in method, you must additionally configure wallaby preprocessors and compilers.

Usage

// Wallaby.js configuration

module.exports = function (wallaby) {
  return {
    // set `load: false` to all source files and tests processed by webpack
    // (except external files),
    // as they should not be loaded in browser,
    // their wrapped versions will be loaded instead
    files: [
      { pattern: 'src/**/*.js', load: false }
      // {pattern: 'lib/jquery.js', instrument: false},
    ],

    tests: [
      { pattern: 'test/**/*.spec.js', load: false }
    ],

    postprocessor: wallaby.postprocessors.webpack({
      // webpack options, such as
      // module: {
      //   rules: [...]
      // },
      // externals: { jquery: "jQuery" }
    }),

    setup: function () {
      // required to trigger test loading
      window.__moduleBundler.loadTests();
    }
  };
};

You may find a working sample of wallaby.js configuration for webpack in this repository.

Webpack options

To make your tests run as fast as possible, specify only the options you need for your tests to run – avoid doing anything that would make each test run slower. Your production webpack configuration and your test webpack configuration serve different purpose and don’t have to be absolutely identical.

You don’t need to specify any output options because wallaby doesn’t use a concatenated bundle. While concatenating files is beneficial for a production environment, in a testing environment it is different. Serving a large bundle every time one of many files (that the bundle consists of) changes is wasteful. So instead, each compiled module code is passed to wallaby, wallaby caches it in memory (and when required, writes it to disk) and serves each requested module file separately to properly leverage browser caching.

You may also consider re-using your existing webpack config by requiring it in wallaby config and adjusting it the way you need.

const options = require('./webpack.test.config');

// Adjust the config as required
// options.plugins.push(...);

module.exports = function (wallaby) {
  return {  
  ...
    postprocessor: wallaby.postprocessors.webpack(options),
  ...
};  

Most likely for some of your files you will have corresponding loaders. The loaders that are used for instrumented files must have source maps support and it must be enabled.

It is important to configure loaders with speed-wise optimal settings, for example if you use ts-loader for TypeScript files then you should consider enabling transpileOnly setting:


module.exports = function (wallaby) {
  return {
    files: [
      {pattern: 'src/**/*.ts', load: false}
    ],

    tests: [
      {pattern: 'test/**/*.spec.ts', load: false}
    ],

    postprocessor: wallaby.postprocessors.webpack({
      module: {
        rules: [
          {
            test: /\.ts$/,
            use: [
              { loader: 'ts-loader', options: { transpileOnly: true } },
            ]
          }
        ]
      },
      resolve: {
        extensions: [".ts"]
      }
    }),
    ...
  };
};

Most of the popular loaders provide multiple ways and settings to improve compilation time; we recommend you to familiarize yourself with its documentation.

Below you may find a few loader independent optimisation techniques that may cut a few seconds off your webpack build. Most of the techniques are not wallaby.js specific and may be used for other test runners and even your production build webpack config.

NormalModuleReplacement plugin

When possible, consider using NormalModuleReplacementPlugin with node-noop for dependencies that you don’t test. In this case, you can remove all files that you use the plugin for from your files list so they don’t get copied over to the wallaby cache, don’t get tracked for changes, etc.

For example, if you require style files or images from your source files, but not asserting anything about them, then you may use the NormalModuleReplacementPlugin plugin:

const webpack = require('webpack');

module.exports = function (wallaby) {
  return {
    files: [
      // there's no need to list .gif, .png, .scss, .css, files here
      ...
    ],
    ...
    postprocessor: wallaby.postprocessors.webpack({
      ...
      plugins: [
        new webpack.NormalModuleReplacementPlugin(/\.(gif|png|scss|css)$/, 'node-noop')
      ]
      ...
    })
  }
};

Don’t forget to install the node-noop module by running:

npm install node-noop --save-dev

Null loader

When not possible to use NormalModuleReplacementPlugin, consider using null loader for dependencies that you don’t test, for example for styles or templates that are not used in your tests.

...
  module: {
      rules: [
          ...
          { test: /\.(scss|css)$/, use: 'null-loader' }
      ]
      ...
  }
...

Externals

When possible, use the webpack externals option to let webpack know not to bundle libraries and instead use their pre-built versions. For example, if you use:

const React = require('react');

in your code and tests, it makes sense to configure wallaby to load the pre-built version of react from node_modules/react/dist and configure webpack to use the external version (as opposed to compile it from sources):

module.exports = function (wallaby) {
  return {
    files: [
      ...
      { pattern: 'node_modules/react/dist/react-with-addons.js', instrument: false },
      ...
    ],
    ...
    postprocessor: wallaby.postprocessors.webpack({
      externals: {
        // Use external version of React
        "react": "React"
      }
    ...
    })
  }
};

Files and tests

All source files and tests (except external files/libs) must have a load: false set, because wallaby will load wrapped versions of these files on window.__moduleBundler.loadTests() call in the setup function. Node modules should not be listed in the files list (unless you would like to load them globally as opposed to require/import them).

The order of source files does not matter, so patterns can be used instead of listing all the files.

The code inside each file is wrapped in such a way that, when the file is loaded in browser, it doesn’t execute the code immediately. Instead, it just adds some function that executes the file code to the test loader’s cache. Tests and dependent files are loaded from wallaby setup function, by calling __moduleBundler.loadTests(), and then executed.

If you would like to load other files into the test sandbox without having to require/import them, you may just specify those files in your files list with load: true (default) flag. These files will just be loaded into the sandbox via script tags.

Setup files

Sometimes you may want to execute some files that require/import other modules before executing your tests, so they need to be compiled with webpack and cannot be loaded directly via script tags. To support the scenario, wallaby.js has a configuration option called setup files, where you may specify paths to such files.

So, for example, if you have a fixture.js file that requires/imports other modules and you would like it to be compiled by webpack, loaded into the sandbox and executed before tests, then your wallaby config may look like:

module.exports = function (wallaby) {
  return {
    files: [
      { pattern: 'calculator.js', load: false },
      { pattern: 'fixture.js', load: false }
    ],

    tests: [
      { pattern: 'calculator.spec.js', load: false }
    ],

    postprocessor: wallaby.postprocessors.webpack({}, {setupFiles: ['fixture.js']}),

    setup: function () {
      window.__moduleBundler.loadTests();
    }
  };
};

Note that because setup files execute before tests, wallaby.js does not have any way to track tests dependencies, so if you change a setup file it’s recommended to restart wallaby.js.

More configuration examples

You may find a few repositories with wallaby.js configuration for webpack and different loaders.