Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to test with Jest when I'm using webpack #334

Closed
iroy2000 opened this issue Apr 22, 2015 · 32 comments · May be fixed by tjenkinson/jest#6, affiliatedkat/jest#3, oudomskn/jest#2, oudomskn/jest#1 or nathang21/jest#1

Comments

@iroy2000
Copy link

I'm using webpack and react, everything works fine until I do the unit test with Jest.

In one of my js files I have

require('style/components/example_app.scss');

And it said
Cannot find module 'style/pages/example_index.scss'

The following is my webpack config

resolve: {
        extensions: ['', '.js', '.jsx'],
        root: [path.join(__dirname, 'src'), path.join(__dirname, 'assets')]
    },

The following is my configuration in package.json

    "jest": {
        "scriptPreprocessor": "<rootDir>/preprocessor.js",
        "unmockedModulePathPatterns": ["<rootDir>/node_modules/react"],
        "testFileExtensions": [
            "js",
            "jsx"
        ],
        "moduleFileExtensions": [
            "js",
            "json",
            "jsx"
        ]
    },

The following is my folder structure

tests / ( host all my test )

src / js / ( host all my js )

@sporto
Copy link

sporto commented Apr 26, 2015

I have a related issue, in my case it is finding the file because I am using a relative path: require('./something.less');

But getting Unexpected token . as it is trying to parse the file as JS

@pherris
Copy link

pherris commented May 1, 2015

Not much of a solution, but you can require your css in main.js or app.js where you don't have tests (or refactor these to not have business logic to test).

@iroy2000
Copy link
Author

iroy2000 commented May 1, 2015

Yes, this is what I'm doing right now, I put the css at the very top level, to make my test pass.

I wonder if anyone out there actually solve that issue in different way.

@jehoshua02
Copy link

Don't you need all the loaders for Sass in your webpack config? I didn't
see anything in there. I imagine during preprocessing webpack would convert
required Sass files to JavaScript modules and jest would handle them fine.

On Tuesday, April 21, 2015, Roy notifications@github.com wrote:

I'm using webpack and react, everything works fine until I do the unit
test with Jest.

In one of my js files I have

require('style/components/example_app.scss');

And it said
Cannot find module 'style/pages/example_index.scss'

The following is my webpack config

resolve: {
extensions: ['', '.js', '.jsx'],
root: [path.join(__dirname, 'src'), path.join(__dirname, 'assets')]
},

The following is my configuration in package.json

"jest": {
    "scriptPreprocessor": "<rootDir>/preprocessor.js",
    "unmockedModulePathPatterns": ["<rootDir>/node_modules/react"],
    "testFileExtensions": [
        "js",
        "jsx"
    ],
    "moduleFileExtensions": [
        "js",
        "json",
        "jsx"
    ]
},

The following is my folder structure

tests / ( host all my test )

src / js / ( host all my js )


Reply to this email directly or view it on GitHub
#334.

Joshua K Stoutenburg
Web Applications Developer
jehoshua02@gmail.com
503-916-9945

@iroy2000
Copy link
Author

iroy2000 commented May 4, 2015

I have all the loaders in my webpack config, but when I'm running jest CLI, it isn't going through webpack. But let me see if I can instrument jest within webpack. But if you have successfully did that before, I would love to see it, thanks.

@zhengl
Copy link

zhengl commented May 4, 2015

Here is how I do

var ReactTools = require('react-tools');

module.exports = {
process: function(src, path) {
if (path.match(/.svg$/) || path.match(/.css$/)) {
return;
} else if(path.match(/.jsx$/)) {
return ReactTools.transform(src);
} else {
return src;
}
}
};

Hope it can help.

@iroy2000
Copy link
Author

iroy2000 commented May 4, 2015

Thanks zhengl,

I also searched for further solution and someone has a similar issue, what he does is after the transform, he remove the line that is related to css, so the JEST won't see the css.

http://stackoverflow.com/questions/28870296/how-to-use-jest-with-webpack

Seems like it solve my problem by removing the thing I don't need to test, but is there a better way than this ?

@pherris
Copy link

pherris commented May 5, 2015

Actually, the S.O. approach seems like a fine solution to me - the actual CSS isn't really needed for testing. I want to test that the class names are or are-not in the rendered HTML, but it doesn't matter if the actual CSS declarations are present for tests.

I updated my preprocessor to look like this:

'use strict';
var ReactTools = require('react-tools');

module.exports = {

  process: function(src, file) {
    if (file.match(/node_modules/)) {
      return src;
    } else if (file.match(/css/)) {
      return '';
    }

    var transformed = ReactTools.transform(src, { harmony: true });

    return transformed;
  }

};

@screendriver
Copy link
Contributor

+1 same here. I also ended up like the StackOverflow approach. Here is my final preprocessor.js. Note: I am using CoffeeScript and CJSX.

var coffee = require('coffee-react');
var transform = require('coffee-react-transform');

module.exports = {
  process: function(src, path) {
    if (coffee.helpers.isCoffee(path)) {
      return coffee.compile(transform(src), {
        'bare': true
      });
    } else if (path.match(/.scss/)) {
      // Ignoring require'd SASS files
      return '';
    }
    return src;
  }
};

@mfrye-intuit
Copy link

I'm using the 'usable' aspect, which made this even tricker. I'm still trying to figure out a better option, but here's how I accomplished it if anyone is interested:

The component

import styles        from '../../less/index.less';

export default class MyComponent extends React.Component {

  componentDidMount() {
    styles.use();
  }

  componentWillUnmount() {
    styles.unuse();
  }

The preprocessor from React Starter Kit

'use strict';

var babel = require('babel-core');

module.exports = {
  process: function(src, filename) {
    // Ignore files other than .js, .es, .jsx or .es6
    if (!babel.canCompile(filename)) {
      return '';
    }
    // Ignore all files within node_modules
    if (filename.indexOf('node_modules') === -1) {
      return babel.transform(src, {filename: filename}).code;
    }
    return src;
  }
};

The jest code

  var PluginCss = require('../../client/less/plugin/plugin.less');

  // Mock wepack style loader
  PluginCss.use =  jest.genMockFunction();
  PluginCss.unuse =  jest.genMockFunction();
  PluginCss.locals = {
    'myClass': 'test'
  }

@appleboy
Copy link

Babel + Less + Webpack + Jest

I write the new preprocessor.

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

module.exports = {
  process: function(src, filename) {
    return babelJest.process(src, filename)
      .replace(/require\(\'[^\']+\.less\'\);/gm, '');
  }
};

It is working for me.

@atecarlos
Copy link

I had some mixed results with the solution on stack overflow due to the babel-jest output containing spaces after "require(". I enhanced the regular expression to handle those scenarios and also capture css, scss, and less.

I published a package to npm called webpack-babel-jest in case it can help anybody else out.

@mwolson
Copy link

mwolson commented Sep 15, 2015

For the resolving directories part of the problem, here's a workaround which can resolve webpack paths in the preprocess phase: https://www.npmjs.com/package/jest-webpack-alias

@benweizhu
Copy link

This works for me.

var babelJest = require('babel-jest');
module.exports = {
    process: function(src, filename) {
        if (filename.match(/\.[css|less|scss]/)) {
            return '';
        }
        return babelJest.process(src, filename);
    },
};

@magbicaleman
Copy link

I would greatly appreciate if someone would enlighten me, on the purpose of integrating jest and webpack. I've setup the configs ...etc as this article describes to, but I'm unclear as to what I'm suppose to expect.

Is the integration solely for resolving requires, or when you run npm start is that suppose to watch test?

@aoto
Copy link

aoto commented Mar 18, 2016

I got the same problem.

@chrislloyd
Copy link

I've done a little investigation into this. I think ColCh/jest-webpack has the cleanest approach. The biggest issue I can see however is that webpack now runs asynchronously, (i.e. Webpack's compiler takes a callback) whereas Jest expects any preprocessor to be synchronous because require itself is synchronous. At this point it mighn't be possible without small hacks around babel-jest like the ones provided above. @cpojer I'm happy to provide PRs — do you have any thoughts?

@cpojer
Copy link
Member

cpojer commented Mar 25, 2016

Hey @chrislloyd. I think with babel6 and the Jest+Babel auto-integration, there is little need to use webpack for processing. However, webpack might be doing some things that Jest does not and I'd like to collect a list of things that are missing from Jest that make it not work so well with your particular projects.

Here is a list of things that would help:

  • What does webpack do when it processes files that Jest doesn't do?
  • What kind of special things does require do in webpack?
  • Can you provide a sample project on GitHub that uses webpack where I can dig into the webpack config to understand what specifically doesn't work?

My guess is that with babel-jest we are already 90 % on the way for good webpack support. If we can identify the things that don't work yet, I'm more than happy to guide you through fixing those one or two minor issues to make the support great out of the box.

@suniala
Copy link

suniala commented Mar 29, 2016

@chrislloyd, maybe we are getting a bit off topic but you can use deasync as a workaround for the synchronous preprocessor / asynchronous webpack problem. Something like this works for me:

var done = false;
webpack(options).run(function callback(err, stats) {
  done = true;
});
deasync.loopWhile(function () {
  return !done;
});

Disclaimer: Remember kids, you should never ever force synchronous execution on asynchronous implementations unless you understand the consequences.

@isaac-peka
Copy link

I'm experiencing the same problem.

@cpojer Correct me if I'm wrong, but I can't see how it will be possible to get jest to play nicely with webpack without using webpack for processing. Webpack allows you to define any arbitrary loaders and resolve extensions which means the behaviour of require calls or imports will vary greatly dependant on the specific config.

@cpojer
Copy link
Member

cpojer commented Apr 4, 2016

@sampeka: can you share a typical webpack configuration with all the special things require does in webpack? Then we can consider improving support for it. I think besides the consolidation around babel (and .babelrc) and the moduleNameMapper feature in Jest we probably won't need to much other stuff.

@chrislloyd
Copy link

@cpojer any webpack config with loaders that lets you import non-JS assets kind of messes with Jest.

loaders: [
    {test: /\.css$/, loaders: 'style!css?module=true'},
    {test: /\.svg$/, loaders: 'file'}
]

which can result in app level code like:

import React from 'react';
import styles from './styles.css';
import BackgroundImageUrl from './background.png'

// ...

@psimyn
Copy link

psimyn commented Apr 13, 2016

@chrislloyd did @benweizhu's idea from above work for you - could return empty object for css modules, empty string for images?

@chrislloyd
Copy link

@psimyn that's what we're currently using. I thought it was initially less ideal as it couples our jest harness to our webpack config.

However, thinking more about it, if the guard was inverted, it might actually be a decent solution as jest fundamentally can only mock JS.

if (!filename.match(/\.jsx?$/)) {
  return '';
}

@cpojer
Copy link
Member

cpojer commented May 20, 2016

After all this time we have finally implemented more support for webpack. I'm sorry for the long wait. modulePaths and moduleDirectories are now part of Jest 12.1.0. We'll be adding some more documentation for the website but it should match the implementation in webpack. See http://facebook.github.io/jest/docs/api.html#moduledirectories-array-string A long time ago we also implemented a moduleNameMapper feature which should be similar to the alias feature in webpack. It can also be used to map css/scss files to a stub module, see http://facebook.github.io/jest/docs/api.html#modulenamemapper-object-string-string For actually compiling scss/css for a test, a separate preprocessor can be created that uses node-sass and babel-jest – this is something I'd be happy to accept a PR for and happy to maintain a babel-webpack-jest package, for example.

I think we support all the features for module resolution well and testing a project with Jest when using webpack should work well, so we can close this issue at last.

cc @jquense who offered to write some documentation for the website and who implemented this feature into jest-resolve.

@cpojer cpojer closed this as completed May 20, 2016
@jquense
Copy link
Contributor

jquense commented May 20, 2016

👏 I will get on it!

@okonet
Copy link
Contributor

okonet commented Nov 21, 2016

Another example I've just run into working on a big project is shiming modules: https://webpack.github.io/docs/shimming-modules.html

So my webpack config looks like:

loaders: [
        // Shims
        { test: /jssha256\-[\w\.]+js/, loader: 'exports?array_to_string&SHA256_hash&HMAC_SHA256_init&HMAC_SHA256_write&HMAC_SHA256_finalize' },
        { test: /modernizr\-custom\.js$/, loader: 'imports?this=>window!exports?window.Modernizr' },
        { test: /easepack\-[\w\.]+js$/, loader: 'imports?window=>{}!exports?Power1&Power2&Power3' },

etc.

and then somewhere deep in the source code I have:

import jssha256 from 'jssha256'; 

So webpack is handling wrapping those modules with module.exports. Any suggestion on how I could approach this?

I've spent quite some time on it already trying compiling using webpack: https://gist.github.com/ninjapanzer/70b55f90edcb69c74e52 but it doesn't seem to work for me yet. Any idea on a better approach?

@dcalhoun
Copy link

dcalhoun commented Mar 8, 2017

@okonet I'm facing the same issue when attempting to have Jest work with modules that webpack is shimming (i.e. using the imports-loader and exports-loader). Did you find a resolution to this issue?

@okonet
Copy link
Contributor

okonet commented Mar 9, 2017

@darkowic
Copy link

darkowic commented May 10, 2017

@dcalhoun as a workaround for imports-loader to just run my tests configuration without the modules (I don't need to test them at the moment), I simply mock them:

    "moduleNameMapper": {
      "imports-loader.*$": "<rootDir>/internals/mocks/importsLoader.js"
    },

Btw how should I import/mock correctly imports-loader? Do I really need to create webpack (I use webpack 2) config for jest?
Example

import 'imports-loader!jquery-mousewheel';

Also asked about it here webpack-contrib/imports-loader#54

@fkotsian
Copy link

fkotsian commented Apr 22, 2021

if anybody hits this issue in 2021 with webpack/peerDeps/and Jest, you can use moduleNameMapper in your consuming package to map your peerDeps to the <rootDir>: https://jestjs.io/docs/webpack#configuring-jest-to-find-our-files

// package.json
{
  "jest": {
    "modulePaths": ["/shared/vendor/modules"],
    "moduleFileExtensions": ["js", "jsx"],
    "moduleDirectories": ["node_modules"],  // doesn't quite get there

    "moduleNameMapper": {
      // directly map your peerDeps to the root node_modules

      "^react$": "<rootDir>/node_modules/react$",        // gets there
      "^styled_components$": "<rootDir>/node_modules/styled-components"
    }
  }
}

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 23, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.