Exports defaults else path not taken with Jest and ES6 - reactjs

I'm having issues to pass my tests with 100% coverage. Istanbul say that exports defaults Component else path not taken.
Because of that, I see in my generated html of istanbul that my tests are not completely at 100%. Mostly in the Statements and Branches tab.
I'm using:
React: 15.4.0
Jest: 17.0.2
Webpack: 1.12.11
Any idea?

The problem was in the jest configuration, we were using a preprocessor in order to resolve some imports:
In the package json we had this:
"transform": {
"^.+\\.js$": "<rootDir>/cfg/preprocessor.js"
},
This file contained this:
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;
}
};
We updated to Jest v20 and also use the module resolver from Jest, in our package.json we added:
"moduleDirectories": [
"node_modules",
"src"
],
and removed the transform config from the package.json and the preprocessor.js file.

Related

Swiper 8 and jest

Swiper 8 and Jest (support ESM) Must use import to load ES Module
enter image description here
enter image description here
How we can solve if I need to keep swiper 8 (without downgrade)
In case someone runs into this same issue but is using NextJs 12.2, next/jest and Jest 28 the answer is a variation from Francisco Barros' answer.
// jest.config.js
const nextJest = require('next/jest')
const createJestConfig = nextJest({
// Path to Next.js app to load next.config.js
dir: './'
})
/** #type {import('#jest/types').Config.InitialOptions} */
const customJestConfig = {
/**
* Custom config goes here, I am not adding it to keep this example simple.
* See next/jest examples for more information.
*/
}
module.exports = async () => ({
/**
* Using ...(await createJestConfig(customJestConfig)()) to override transformIgnorePatterns
* provided byt next/jest.
*
* #link https://github.com/vercel/next.js/issues/36077#issuecomment-1096635363
*/
...(await createJestConfig(customJestConfig)()),
/**
* Swiper uses ECMAScript Modules (ESM) and Jest provides some experimental support for it
* but "node_modules" are not transpiled by next/jest yet.
*
* #link https://github.com/vercel/next.js/issues/36077#issuecomment-1096698456
* #link https://jestjs.io/docs/ecmascript-modules
*/
transformIgnorePatterns: [
'node_modules/(?!(swiper|ssr-window|dom7)/)',
]
})
The transformIgnorePatterns on jest.config.js prevents the Swiper files from being transformed by Jest but it affects the CSS files that are provided by this package.
Mocking these CSS files is the solution with the smallest configuration.
In my case, it didn't matter much about having access to these CSS files while testing but there are other approaches if these files are relevant to you.
// jest.setup.js
import "#testing-library/jest-dom/extend-expect";
jest.mock("swiper/css", jest.fn());
export {};
I created a repository for a full reference of the necessary setup.
https://github.com/markcnunes/with-jest-and-esm
Have in mind this setup might have to change for future Next.js / next/js versions but just sharing this approach in case this is useful for other people using this same setup.
I have the same issue and still searching for a solution; I believe what the OP is asking is how can we transform swiper/react into a CJS module on the JEST pipeline.
Since using ESM Experimental in jest is not a good option...
Downgrading to v6 is not an option;
Any code that imports Swiper v8 fails in JEST because Swiper 8 only exports ESM;
A few days have passed since my original response. In the mean time I have found a solution that I have been using to effectively use Swiper v8 while still being able to test components that depend on it using jest.
The trick is to map the ESM imports to actual JavaScript and CSS files exported by Swiper, which can then be processed by babel and transpiled into CJS.
Create a file
// ./mocks/misc/swiper.js
module.exports = {
// Rewrite Swiper ESM imports as paths (allows them to be transformed w/o errors)
moduleNameMapper: {
"swiper/react": "<rootDir>/node_modules/swiper/react/swiper-react.js",
"swiper/css": "<rootDir>/node_modules/swiper/swiper.min.css",
"swiper/css/bundle": "<rootDir>/node_modules/swiper/swiper-bundle.min.css",
"swiper/css/autoplay": "<rootDir>/node_modules/swiper/modules/autoplay/autoplay.min.css",
"swiper/css/free-mode": "<rootDir>/node_modules/swiper/modules/autoplay/free-mode.min.css",
"swiper/css/navigation": "<rootDir>/node_modules/swiper/modules/autoplay/navigation.min.css",
"swiper/css/pagination": "<rootDir>/node_modules/swiper/modules/autoplay/pagination.min.css"
},
// Allow Swiper js and css mapped modules to be imported in test files
transform: {
"^.+\\.(js|jsx|ts|tsx)$": ["babel-jest", { presets: ["next/babel"] }],
"^.+\\.(css)$": "<rootDir>/jest.transform.js"
},
// Do not transform any node_modules to CJS except for Swiper and Friends
transformIgnorePatterns: ["/node_modules/(?!swiper|swiper/react|ssr-window|dom7)"]
}
Create another file in the root of your repository
// jest.transform.js
"use strict"
const path = require("path")
// Requried to fix Swiper CSS imports during jest executions, it transforms imports into filenames
module.exports = {
process: (_src, filename) => `module.exports = ${JSON.stringify(path.basename(filename))};`
}
Finally in your jest configuration use the things you created
// jest.config.js
const swiperMocks = require("./mocks/misc/swipper");
module.exports = {
...yourConfigurations
moduleNameMapper: {
...yourOtherModuleNameMappers,
...swiperMocks.moduleNameMapper,
},
transform: {
...yourOtherTransforms
...swiperMocks.transform,
},
transformIgnorePatterns: [
...yourOtherTransformsIgnorePatterns
...swiperMocks.transformIgnorePatterns,
],
}

Jest and file-loader import

I'm currently importing a module using file loader in one of my files in a react app (CRA):
"file-loader?name=scripts/[name].[hash].js!jsstore/dist/jsstore.worker.min.js"
When running Jest, it throws this error:
Cannot find module 'file-loader?name=scripts/[name].[hash].js!jsstore/dist/jsstore.worker.min.js'
I've attempted different configs in package.json for Jest, by setting either modulePathIgnorePatterns and moduleNameMapper, but neither config setting works:
"modulePathIgnorePatterns": [
"file-loader?name=scripts/[name].[hash].js!jsstore/dist/jsstore.worker.min.js"
]
"moduleNameMapper": {
"file-loader?name=scripts/[name].[hash].js!jsstore/dist/jsstore.worker.min.js": "<rootDir>/node_modules/jsstore/dist/jsstore.worker.min.js"
}
You can map this import to a file that will return a string which what file-loader returns;
moduleNameMapper: {
"^file\-loader":"<rootDir>/__mocks__/fileMock.js",
}
// __mocks__/fileMock.js
module.exports = 'file-path-mock';

Use Webpack's DefinePlugin vars in Jest tests

I'm pretty new in React, coming from Angular. I'm writing some tests for the code that's going to send request to an external endpoint. Obviously, I don't want to hardcode a real host in production code so I thought I could use webpack's DefinePlugin to keep this in a constant.
How to configure Webpack and Jest together to use Webpack's capabilities?
It's just like that in your package.json or jest.config.js :
"jest": {
"globals": {
"__DEV__": true
}
}
If you still have any problems, please check the jest offical docs here:
globals-object
As explained in comments:
/globals.js
module.exports = {
__DEV__: true
}
/webpack.config.js
const globals = require('./globals.js')
// ...
plugins: [
new webpack.DefinePlugin(globals)
]
/jest.config.js
const globals = require('./globals.js')
module.exports = {
// ...
globals,
}

Use scoped packages with Jest

I am developing an app with react-native and typescript and doing the tests with Jest, but I have a problem when I use scoped packages (#assets), jest can not find the path and gives error.
The directory structure looks like this:
project/
assets/
img/
foo.png
package.json
src/
Foo.ts
build/
Foo.js
// assets/package.json
{
"name": "#assets" // My #assets scope
}
// build/Foo.js
const image = require('#assets/img/foo.png'); // <- error in Jest
So when I run the jest:
npm run jest build/
It can not find '#assets/img/foo.png' and throws the error:
Cannot find module '#assets/img/logo.png' from 'Foo.js'
How can I use scope package in Jest?
Jest version: 20.0.4
thanks
For those that get here that are using private packages under a scoped org, here's how I tackled this:
"jest": {
"moduleNameMapper": {
"^#org-name/(.*)$": "<rootDir>/node_modules/#org-name/$1/dist/$1.es5.js"
}
}
This assumes that all of your scoped packages have a similar path to their exported module. If they don't, you can specify them individually:
"jest": {
"moduleNameMapper": {
"^#org-name/package-one$": "<rootDir>/node_modules/org-name/package-one/dist/package.js",
"^#org-name/package-two$": "<rootDir>/node_modules/org-name/package-two/build/index.js"
}
}
Necessary only define the moduleNameMapper in jest config:
// package.json
"jest": {
"moduleNameMapper": {
"^#assets.+\\.(png)$": "<rootDir>/assetsTransformer.js"
},
}
// assetsTransformers.js
const path = require('path');
module.exports = {
process(src, filename, config, options) {
return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';';
},
};
Thanks for this comment :-)

How to use jest with webpack?

I use webpack to develop a React component. Here is a simple version of it:
'use strict';
require('./MyComponent.less');
var React = require('react');
var MyComponent = React.createClass({
render() {
return (
<div className="my-component">
Hello World
</div>
);
}
});
module.exports = MyComponent;
Now, I would like to test this component using jest. Here is the relevant bit from my package.json:
"scripts": {
"test": "jest"
},
"jest": {
"rootDir": ".",
"testDirectoryName": "tests",
"scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
"unmockedModulePathPatterns": [
"react"
]
}
When running npm test, I get the following error:
SyntaxError: /Users/mishamoroshko/react-component/src/tests/MyComponent.js: /Users/mishamoroshko/react-component/src/MyComponent.js: /Users/mishamoroshko/react-component/src/MyComponent.less: Unexpected token ILLEGAL
Looks like webpack needs to process require('./MyComponent.less') before jest can run the test.
I wonder if I need to use something like jest-webpack. If yes, is there a way to specify multiple scriptPreprocessors? (note that I already use babel-jest)
The cleanest solution I found for ignoring a required module is to use the moduleNameMapper config (works on the latest version 0.9.2)
The documentation is hard to follow. I hope the following will help.
Add moduleNameMapper key to your packages.json config. The key for an item should be a regex of the required string. Example with '.less' files:
"moduleNameMapper": { "^.*[.](less|LESS)$": "EmptyModule" },
Add a EmptyModule.js to your root folder:
/**
* #providesModule EmptyModule
*/
module.exports = '';
The comment is important since the moduleNameMapper use EmptyModule as alias to this module (read more about providesModule).
Now each require reference that matches the regex will be replaced with an empty string.
If you use the moduleFileExtensions configuration with a 'js' file, then make sure you also add the EmptyModule to your 'unmockedModulePathPatterns'.
Here is the jest configuration I ended up with:
"jest": {
"scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
"moduleFileExtensions": ["js", "json","jsx" ],
"moduleNameMapper": {
"^.*[.](jpg|JPG|gif|GIF|png|PNG|less|LESS|css|CSS)$": "EmptyModule"
},
"preprocessorIgnorePatterns": [ "/node_modules/" ],
"unmockedModulePathPatterns": [
"<rootDir>/node_modules/react",
"<rootDir>/node_modules/react-dom",
"<rootDir>/node_modules/react-addons-test-utils",
"<rootDir>/EmptyModule.js"
]
}
I ended up with the following hack:
// package.json
"jest": {
"scriptPreprocessor": "<rootDir>/jest-script-preprocessor",
...
}
// jest-script-preprocessor.js
var babelJest = require("babel-jest");
module.exports = {
process: function(src, filename) {
return babelJest.process(src, filename)
.replace(/^require.*\.less.*;$/gm, '');
}
};
But, I'm still wondering what is the right solution to this problem.
I just found that it's even simpler with Jest's moduleNameMapper configuration.
// package.json
"jest": {
"moduleNameMapper": {
"^.+\\.scss$": "<rootDir>/scripts/mocks/style-mock.js"
}
}
// style-mock.js
module.exports = {};
More detail at Jest's tutorial page.
I recently released Jestpack which might help. It first builds your test files with Webpack so any custom module resolution/loaders/plugins etc. just work and you end up with JavaScript. It then provides a custom module loader for Jest which understands the Webpack module runtime.
From Jest docs:
// in terminal, add new dependency: identity-obj-proxy
npm install --save-dev identity-obj-proxy
// package.json (for CSS Modules)
{
"jest": {
"moduleNameMapper": {
"\\.(css|less)$": "identity-obj-proxy"
}
}
}
The snippet above will route all .less files to the new dependency identity-obj-proxy, which will return a string with the classname when invoked, e.g. 'styleName' for styles.styleName.
I think a less hacky solution would be to wrap your preprocessor in a conditional on the filename matching a javascript file:
if (filename.match(/\.jsx?$/)) {
return babelJest.process(src, filename);
} else {
return '';
}
This works even if you don't explicitly set the extension in the require line and doesn't require a regex substitution on the source.
I have experienced similar issue with such pattern
import React, { PropTypes, Component } from 'react';
import styles from './ContactPage.css';
import withStyles from '../../decorators/withStyles';
#withStyles(styles)
class ContactPage extends Component {
see example at https://github.com/kriasoft/react-starter-kit/blob/9204f2661ebee15dcb0b2feed4ae1d2137a8d213/src/components/ContactPage/ContactPage.js#L4-L7
For running Jest I has 2 problems:
import of .css
applying decorator #withStyles (TypeError: <...> (0 , _appDecoratorsWithStyles2.default)(...) is not a function)
First one was solved by mocking .css itself in script preprocessor.
Second one was solved by excluding decorators from automocking using unmockedModulePathPatterns
module.exports = {
process: function (src, filename) {
...
if (filename.match(/\.css$/)) src = '';
...
babel.transform(src, ...
}
}
example based on https://github.com/babel/babel-jest/blob/77a24a71ae2291af64f51a237b2a9146fa38b136/index.js
Note also: when you working with jest preprocessor you should clean cache:
$ rm node_modules/jest-cli/.haste_cache -r
Taking inspiration from Misha's response, I created an NPM package that solves this problem while also handling a few more scenarios I came across:
webpack-babel-jest
Hopefully this can save the next person a few hours.
If you're using babel, you can strip unwanted imports during the babel transform using something like https://github.com/Shyp/babel-plugin-import-noop and configuring your .babelrc test env to use the plugin, like so:
{
"env": {
"development": {
...
},
"test": {
"presets": [ ... ],
"plugins": [
["import-noop", {
"extensions": ["scss", "css"]
}]
]
}
}
}
We had a similar problem with CSS files. As you mentioned before jest-webpack solves this problem fine. You won't have to mock or use any module mappers either. For us we replaced our npm test command from jest to jest-webpack and it just worked.
Webpack is a great tool, but I don't need to test it's behavior with my Jest unit tests, and adding a webpack build prior to running unit tests is only going to slow down the process. The text-book answer is to mock non-code dependencies using the "moduleNameMapper" option
https://facebook.github.io/jest/docs/webpack.html#handling-static-assets

Resources