Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Jest with React and Webpack

I was trying to configure Jest to work with React and Webpack. With the way I use Webpack aliases and ES6 modules exports, I had huge problems configuring it.

Since no other questions worked for me, I want to show you what I did to be able to use Jest with ReactJs and Webpack.

like image 652
Albert Olivé Avatar asked Nov 16 '16 09:11

Albert Olivé


People also ask

Does Jest use webpack?

Jest can be used in projects that use webpack to manage assets, styles, and compilation.

Does Jest require node?

To read TypeScript configuration files Jest requires ts-node . Make sure it is installed in your project.

What is Jest moduleNameMapper?

2. moduleNameMapper [Object] (Default: null) This configuration holds the key to file mocking. By using this configuration all the external asset files like images and style files will be mocked, as these files are not intended to be tested. So, while running test cases, only the the mock files are imported.

What is $1 Jest config?

In the right side's expression The $1 is replacement syntax to refer to capture group one. Had you two sets of parens on the left side, then you could refer to group one ( $1 ) and group two ( $2 ) in the replacement expression. Capture groups are numbered left-to-right, outermost to innermost.


1 Answers

My mainly problem was that I was using ES6 to export my modules and I needed a preprocessor to handle the imports.

Given that basic component:

import 'Incription.scss';

import React from 'react;
import InscriptionModal from './InscriptionModal';

class Inscription extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        return <InscriptionModal />;
    }
}

export default Inscription;

Test looks like:

import React from 'react';
import renderer from 'react-test-renderer';

import Inscription from './Inscription';

describe('Inscription', () => {
    it('renders correctly', () => {
        const tree = renderer.create(<Inscription />).toJSON();

        expect(tree).toMatchSnapshot();
    });
});

With no configuration in package.json, when I was running the test I always ended up with:

SyntaxError: Unexpected token .

Basically, I needed to handle asset files and, to do so, I needed to change my package.json jest config using moduleNameMapper.:

"jest": {
    "moduleNameMapper": {
      "^.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/cfg/mocks/fileMock.js",
      "^.+\\.(css|scss)$": "<rootDir>/cfg/mocks/styleMock.js"
    }
}

fileMock.js

module.exports = 'test-file-stub';

styleMock.js

module.exports = {};

Next problem was that Jest could not handle the modules of ES6 so, I needed a preprocessor. The exact error was:

Cannot find module './InscriptionModal' from 'Inscription.spec.js'

I found a plugin called jest-webpack-alias that it was doing that so, I changed again the jest configuration of my package.json:

"jest": {
    ...
    "transform": {
        "^.+\\.js$": "<rootDir>/cfg/preprocessor.js"
    },
    "jest-webpack-alias": {
        "configFile": "cfg/jest.js"
    },
}

And my preprocessor.js file looked like:

const babelJest = require('babel-jest');

require('babel-register');
const webpackAlias = require('jest-webpack-alias');

module.exports = {
  process: function (src, filename) {
    if (filename.indexOf('node_modules') === -1) {
      src = babelJest.process(src, filename);
      src = webpackAlias.process(src, filename);
    }
    return src;
  }
};

Notice the configFile of jest-webpack-file in my package.json. It has a route of my webpack test configuration. The file looked like this:

'use strict';

const path = require('path');

const srcPath = path.join(__dirname, '/../src/');
const baseConfig = require('./base');

module.exports = {
  devtool: 'eval',
  module: {
    loaders: [{
      test: /\.(png|jpg|gif|woff|woff2|css|sass|scss|less|styl)$/,
      loader: 'null-loader'
    }, {
      test: /\.(js|jsx)$/,
      loader: 'babel-loader',
      include: [].concat(
        baseConfig.additionalPaths,
        [
          path.join(__dirname, '/../src'),
          path.join(__dirname, '/../test')
        ]
      )
    }, {
      test: /\.json$/,
      loader: 'json-loader'
    }]
  },
  resolve: {
    root: path.resolve(__dirname, '/../src'),
    extensions: [
      '',
      '.js',
      '.jsx'
    ],
    alias: {
      actions: `${srcPath}/actions/`,
      components: `${srcPath}/components/`,
      sources: `${srcPath}/sources/`,
      stores: `${srcPath}/stores/`,
      services: `${srcPath}/services/`,
      styles: `${srcPath}/styles/`,
      i18n: `${srcPath}/i18n/`,
      config: `${srcPath}/config/test`,
      utils: `${srcPath}/utils/`,
      vendor: `${srcPath}/vendor/`,
      images: `${srcPath}/images/`
    }
  }
};

It's very important in this file to add the root of your project because if not, you will have this error:

Missing setting "resolve.modules" (webpack v2) or "resolve.root" (webpack v1) in...

With the configs added, you now can mock your imports using Jest mock:

jest.mock('./InscriptionModal, () => {
  return {};
});

It's important to add the param --no-cache when running tests because Jest caches transformed module files to speed up test execution.

Another annoying bug is when you're trying to add enzyme or React Test Utils in your tests. It prints this error:

Invariant Violation: ReactCompositeComponent: injectEnvironment() can only be called once.

What you have to do is add in your tests this line of code:

jest.mock('react/lib/ReactDefaultInjection');

I hope you find useful!

UPDATE

Upgrading React and React-dom to v15.4.0 fixes latest bug commented.

like image 52
Albert Olivé Avatar answered Oct 05 '22 00:10

Albert Olivé