I am setting up jest and it is not finding window.I18n.t() because it's defined by sprockets (ruby on rails, independent of webpack). So I'd like to mock that method. Here is the error:
FAIL spec/javascript/sanity_test.js
● Test suite failed to run
TypeError: Cannot read property 't' of undefined
24 |
25 | export const newOrderPreferenceObject = {
> 26 | label: window.I18n.t("some.path.in.localization"), default_value: "",
in package.json I can do this:
"jest": {
"testEnvironment": "jsdom",
"globals": {
"window": {
"I18n": { "t": {} }
}
},
I change testEnvironment from jsdom to node, as well.
However, t() must be a function, not a data structure. So how do I put a function in window in jest test?
I don't know the circumstances of your project but here's how I setup and mock global objects in my projects.
I create a setupTests.js in the root src directory and mock them there.
setupTests.js
window.I18n = {
t: jest.fn(),
}
https://create-react-app.dev/docs/running-tests/#srcsetuptestsjs-1
js and react newbie...playing around with testing frameworks...here's the code:
import React from 'react';
// import CheckboxWithLabel from '../CheckboxWithLabel';
import {shallow} from 'enzyme'; //not installed...
//var x = require ('../CheckboxWithLabel.js');
test('CheckboxWithLabel changes the text after click', () => {
const checkbox = shallow(
<CheckboxWithLabel labelOn="On" labelOff="Off" />
);
expect(checkbox.text()).toEqual('Off');
checkbox.find('input').simulate('change');
expect(checkbox.text()).toEqual('On');
});
The react-scripts test error is:
Cannot find module 'enzyme' from 'checkboxWithLabel-test.js'
While the jest error is:
Jest encountered an unexpected token
SyntaxError: /Users/shriamin/Development/js_prj_react_django_etc/jest_react_demo/my-app/src/__tests__/checkboxWithLabel-test.js: Unexpected token (12:4)
10 | test('CheckboxWithLabel changes the text after click', () => {
11 | const checkbox = shallow(
> 12 | <CheckboxWithLabel labelOn="On" labelOff="Off" />
| ^
13 | );
14 | expect(checkbox.text()).toEqual('Off');
15 | checkbox.find('input').simulate('change');
i have no idea why jest would throw this error...react-scripts test makes sense to me since enzyme is not installed....please tell me does jest suck or am i doing something wrong configuring jest (installed via npm and update package.json).
NOTE: i don't have babel installed...i don't know what that is yet.
thanks
You arrived at the answer yourself. To use jest your tests need to go through babel for the runner to understand react syntax. take a look at the babel-doc to understand it at greater detail. it's just a transformation tool that transforms fancy syntax into something javascript understands. install the following plugins and presets.
Presets
npm i --save #babel/preset-env
npm i --save #babel/preset-react
Plugins
npm install --save babel-plugin-transform-export-extensions
in your .babelrc add the following lines:
{
"env": {
"test": {
"presets": [
"#babel/preset-env",
"#babel/preset-react"
],
"plugins": [
"transform-export-extensions",
],
"only": [
"./**/*.js",
"node_modules/jest-runtime"
]
}
}
}
Now try running jest on the command-line from your project directory to make sure your tests are configured correctly.
react-scripts is a preconfigured set of commands that come out of the box with create-react-app if you want to use that instead of jest command, check here.
react-scripts expects your tests folder location to follow a certain convention.
this is probably why the tests weren't getting fetched when the react-scripts test command was run out of the box.
in package.json change
"scripts": {
"test": "jest",
},
to the following:
"scripts": {
"test": "react-scripts test",
},
i.e. don't change to jest in the first place
The error described here seem to be jsx that isn't interpreted, isn't your test file extension js instead of jsx ?
I'm using jest and enzyme to build this test, but I'm having this error when I build it.
This is the test
import React from 'react';
import { shallow, render } from 'enzyme';
import Title from './../';
describe('Title', () => {
it('should render correctly', () => {
const TitleDOM = render(<Title />);
expect(TitleDOM).toBeDefined();
});
});
This is the error
Versions:
"react": "^16.0.0",
"jest": "^21.2.1",
"enzyme": "^3.2.0"
I appreciate if someone can help me.
I was having similar issue with electron native components and the solution that worked for me was isolating those components to a separate file.
You could just separate the usage of the htmlparser2 to a function outside the component in a helper file.
Hit this issue myself today, turns out cheerio (a dependency of enzyme) introduced breaking changes in their 1.x version, and enzyme doesn't appear to correctly pin the version to avoid this.
You can see the related cheerio issues here:
https://github.com/cheeriojs/cheerio/issues/1260
https://github.com/cheeriojs/cheerio/issues/1261
Essentially, they're moving away from htmlparser2 by default, but there is a way to continue it's use.
I opened an enzyme issue about this here (which details where you could make the required changes in enzyme if required):
https://github.com/airbnb/enzyme/issues/2299
A workaround in the meantime would be to coerce cheerio's version resolution in your package.json, which can be done by adding a resolutions entry like the following:
{
// ..snip: other package.json things..
"dependencies": {
"enzyme": "^3.9.0",
// ..snip: all of your other dependencies..
},
"resolutions": {
"cheerio": ">= 0.22.0 < 1.0.0"
}
}
This appears to be a yarn specific feature, which you can read more about at:
https://yarnpkg.com/lang/en/docs/selective-version-resolutions/
If you use npm, there appears to be an equivalent package called npm-force-resolutions (though I haven't used it personally):
https://www.npmjs.com/package/npm-force-resolutions
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
I followed the Jest - React tutorial to test a React component.
Unfortunately, Jest throws:
SyntaxError: /Users/mishamoroshko/react-playground/src/search-panel/questions/__tests__/questions-test.js: /Users/mishamoroshko/react-playground/src/search-panel/questions/questions.js: Unexpected token ILLEGAL
at Contextify.sandbox.run (/Users/mishamoroshko/react-playground/node_modules/jest-cli/node_modules/jsdom/node_modules/contextify/lib/contextify.js:12:24)
at JSDomEnvironment.runSourceText (/Users/mishamoroshko/react-playground/node_modules/jest-cli/src/JSDomEnvironment.js:108:22)
at Object.runContentWithLocalBindings (/Users/mishamoroshko/react-playground/node_modules/jest-cli/src/lib/utils.js:341:23)
To reproduce:
git clone git#github.com:SEEK-Jobs/react-playground.git
cd react-playground
npm install
npm test
Any ideas?
UPDATE 1:
I wonder whether the problem is that Jest doesn't know about ES6, and I need to use 6to5-jest.
Is there a way to specify 2 preprocessors in package.json?
"jest": {
"rootDir": "src",
"scriptPreprocessor": "../preprocessor.js",
"unmockedModulePathPatterns": [
"../node_modules/react"
]
}
Indeed, adding 6to5-jest solved the problem.
Here is how I implemented multiple scriptPreprocessors in Jest:
// preprocessor.js
var ReactTools = require('react-tools');
var to5 = require('6to5-jest').process;
module.exports = {
process: function(src, filename) {
return ReactTools.transform(to5(src, filename));
}
};
If you have a better way to implement this, please leave a comment.