I'm trying to figure out how to make my unit tests in my reactJS ES6 application. My application is already using es6 module system, transpiled with jspm/babel to systemJs.
I found babel-jest as preprocessor but even with it, I can't run my tests because jest can't find SystemJs. ("System is not defined" error is shown in the console)
In the browser, as explained in jspm documentation, SystemJs is loaded globally. I guess I should load SystemJs inside my preprocessor, but How can I make systemJs available for loading additional modules in my tests?
Thanks in advance
Unfortunately, Jest does not support SystemJS ES6 modules at the moment.
See the following comments:
So it sounds like jest assumes that your modules resolve based on the Node resolution algorithm.
Unfortunately this isn't compatible with SystemJS's resolution algorithm.
If there was a way in jest to set a "custom resolver" algorithm through an API then we could plug jspm into jest, but I'm not sure if this is currently possible.
-- Comment by Guy Bedford, creator of SystemJS, Jun 2015
It is unlikely there'll be official support for [SystemJS] unless it is a community contribution.
-- Comment by #cpojer, Jest Collaborator, Jan 2016
Also see the following issue: Invalid URL is thrown when requiring systemjs in jest test cases
in essence to get Jest to play nice with an app running on JSPM/SystemJS you need to "teach" it about all the mapping it holds in the config.js file (or however you call System.config())
the long answer is that you need to create an entry for each dependency you have installed with JSPM like this:
//jest configuration
moduleNameMapper: {
...
"^redux$": "redux#3.6.0",
...
}
for each alias you have, you need at least one entry:
moduleNameMapper: {
...
"^common\\/(.*)": "<rootDir>/src/common/$1", //for a dir alias
"^actions$": "<rootDir>/src/actions/index", //for a file alias
...
}
you also need to have these mappings in your nodeNameMapper:
moduleNameMapper: {
...
"^npm:(.*)": "<rootDir>/jspm_packages/npm/$1",
"^github:(.*)": "<rootDir>/jspm_packages/github/$1",
...
}
and finally, you need to have this moduleDirectories config:
moduleDirectories: ["node_modules", "jspm_packages/npm", "jspm_packages/github"]
this isnt really practical because you dont want to keep two copies of all your dependencies registry and have to sync them when they change...
so the short and better answer, you use my gulp-jest-jspm plugin :)
even if you dont use gulp, you can use its getJestConfig() method to generate the config for you before you run Jest.
Related
I used Create React App to make a new project, and now i try to implement each feature i will need.
I am facing an issue i dont understand while trying to configure jest with reactjs.
One my node_modules, react-dotenv break jest test.
If i try to copy/past the issue line import env from 'react-dotenv'; directly in the root tested file Login.tsx there is no error.
I tried to configure moduleNameMapper and search around without success..
I dont understand the problem, what am i missing?
Ok, i was missing mocks and transformIgnorePatterns
Didn't really make transformIgnorePatterns working using:
transformIgnorePatterns: ["node_modules/(?!react-dotenv)"],
but the mocks
moduleNameMapper: {
'^.+\\.(css|scss)$': '<rootDir>/__mocks__/CSSStub.config.js',
'react-dotenv': '<rootDir>/__mocks__/react-dotenv.tsx'
},
did the job.
I am testing a react app with the react-testing-library. To use renderhooks, I had to add the '#testing-library/react-hooks' library. This library depends on another library "pure.js". Running tests that makes use of renderhooks in vscode works fine but shows and error in a web IDE.
Test suite failed to run
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.
By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".
Here's what you can do:
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/en/configuration.html
Details:
/projects/challenge/node_modules/#testing-library/react-hooks/lib/pure.js:41
} catch {
^
SyntaxError: Unexpected token {
at ScriptTransformer._transformAndBuildScript (node_modules/#jest/transform/build/ScriptTransformer.js:537:17)
at ScriptTransformer.transform (node_modules/#jest/transform/build/ScriptTransformer.js:579:25)
at Object.<anonymous> (node_modules/#testing-library/react-hooks/lib/index.js:11:13)
How do one get around this error?
The try/catch is a modern JavaScript feature and supported since Node version 10.
Try to update Node.js if your version is < 10
Please also see this answer here: https://stackoverflow.com/a/62971606/1108161
Ok, so look at the documentation here.
Sometimes it happens (especially in React Native or TypeScript projects) that 3rd party modules are published as untranspiled. Since all files inside node_modules are not transformed by default, Jest will not understand the code in these modules, resulting in syntax errors. To overcome this, you may use transformIgnorePatterns to allow transpiling such modules. You'll find a good example of this use case in React Native Guide.
Your error message states that Jest has found code it can't understand, and is suggesting that you transform it. It also says that the code it can't understand is in node_modules/#testing-library.
However, the default value for transformIgnorePatterns is ["/node_modules/", "\\.pnp\\.[^\\\/]+$"], which means that the node_modules folder will not be transformed. To allow jest to transform the folder, you need to change the transformIgnorePatternsvalue to something else that includes the file Jest can't understand. That could e.g be something like
"transformIgnorePatterns": [
"node_modules/(?!(#testing-library)/)"
]
I am trying to do this as simple as possible, I studied Yarn Workspaces for a while, but that's a solution that's currently doesn't work with Electron, there were simply too many issues.
I have am Electron project here: ./electron/
I have a directory with components here: ./common/
The components are developed in React/JSX, there is nothing really fancy about them. That said, I am using hooks (useXXX).
I tried many ways to include those components (ideally, I wanted to use Yarn Workspaces, but it only multiplied the number of issues), but they all failed. Which is why I would like to avoid using yarn link or workspaces or making the common a library, etc. I just want my Electron project to behave as if the files were under ./electron. That's it.
The closest I came to a solution is by using electron-webpack, and overriding it with this config:
module.exports = function(config) {
config = merge.smart(config, {
module: {
rules: [
{
test: /\.jsx?$/,
//include: /node_modules/,
include: Path.resolve(__dirname, '../common'),
loaders: ['react-hot-loader/webpack', 'babel-loader?presets[]=#babel/preset-react']
},
]
},
resolve: {
alias: {
'#common': Path.resolve(__dirname, '../common')
}
}
})
return config
}
I can import modules, and they work... except if I use hooks. And I am getting the "Invalid Hook Call Warning": https://reactjs.org/warnings/invalid-hook-call-warning.html.
I feel like that /common folder is not being compiled properly by babel, but the reality is that I have no idea where to look or what to try. I guess there is a solution for this, through that webpack config.
Thanks in advance for your help :)
I found the solution. That happens because the instance of React is different between /common and /electron.
The idea is to add an alias, like this:
'react': Path.resolve('./node_modules/react')
Of course, the same can be done for other modules which need to be exactly on the same instance. Don't hesitate to comment this if this answer it not perfectly right.
I wrestled more than a day with a similar problem. My project has a dependency on a module A that is itself bundled by Webpack (one that I authored myself). I externalised React from A (declaring it to be a commonjs2 module). This will exclude the React files from the library bundle.
My main program, running in the Electron Renderer process, uses React as well. I had Webpack include React into the bundle (no special configuration).
However, this produced the 'hooks' problem because of two instances of React in the runtime environment.
This is caused by these facts:
module A 'requires' React and this is resolved by the module system of Electron. So Electron takes React from node_modules;
the main program relies on the Webpack runtime to 'load' React from the bundle itself.
both Electron and the Webpack runtime have their own module cache...
My solution was to externalise React from the main program as well. This way, both the main program and module A get their React from Electron - a single instance in memory.
I tried any number of aliases, but that does not solve the problem as an alias only gives direction to the question of where to find the module code. It does nothing with respect to the problem of multiple module caches!
If you run into this problem with a module that you cannot control, find out if and how React is externalised. If it is not externalised, I think you cannot solve this problem in the context of Electron. If it is externalised as a global, put React into your .html file and make your main program depend on that as well.
I am just starting to dabble in react and one of the first components I want is something to use photoswipe.js. (react photoswipe) It looks like there is a pretty decent one on npm, but I am running into a problem. When I run my storybook to start testing and building my component, I get an error from babel. It says:
in ./~/react-photoswipe/lib/index.js
Module build failed: ReferenceError: [BABEL] C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\react-photoswipe\lib\index.js: Using removed Babel 5 option: C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\react-photoswipe\.babelrc.stage - Check out the corresponding stage-x presets http://babeljs.io/docs/plugins/#presets
at Logger.error (C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\babel-core\lib\transformation\file\logger.js:41:11)
at OptionManager.mergeOptions (C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\babel-core\lib\transformation\file\options\option-manager.js:220:20)
at OptionManager.init (C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\babel-core\lib\transformation\file\options\option-manager.js:368:12)
at File.initOptions (C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\babel-core\lib\transformation\file\index.js:212:65)
at new File (C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\babel-core\lib\transformation\file\index.js:135:24)
at Pipeline.transform (C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\babel-core\lib\transformation\pipeline.js:46:16)
at transpile (C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\babel-loader\lib\index.js:46:20)
at C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\babel-loader\lib\fs-cache.js:79:18
at ReadFileContext.callback (C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\babel-loader\lib\fs-cache.js:15:14)
at FSReqWrap.readFileAfterOpen [as oncomplete] (fs.js:365:13)
So I did a bit of poking around and noticed that the babel rc file appears to be set to stage:0 which from my understanding seems like a really bad idea if you are producing a component that is supposed to be durable as the javascript spec updates and evolves.
I am still pretty new to using babel though so I am kind of having a hard time tracking down what I would need to update for this component to get it working in my environment with the newer babel. Has anyone encountered this problem with this component before? Does anyone have any advice or tips on how to troubleshoot the bable transpile and track down what I need to update?
The .babelrc from react-photoswipe does not work with babel 6. But it doesn't need to, because main entry point of the module is lib/index.js, which contains the already transpiled code. You're trying to transpile it again, and it automatically applies the .babelrc closest to it.
You should exclude node_modules in your webpack config, for example:
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}
It will not only fix your issue, but also reduce the build time.
Thanks Michael for getting me pointed in the right direction. I am testing and building a component using a react storybook tool that has a whitelist configuration that tells it what node modules not to run through the full build. I had to add react-photoswipe to that whitelist and it is now working... well starting to work, but this problem is taken care of.
I am following the React-router docs, but I have encountered an obstacle that is not really related to the router itself: Babel transpiles the {import} as require, which would be used by Express or Node.js on the server, but from what I understand from the docs, it is actually intended for client-side rendering.
Of course, the JSX file with the router transpiled using Babel and included into a HTML browser page does not work, since require is only used by express/node server-side.
May I ask how is it actually supposed to work in the browser?
Thank you
Babel's transpile of import produces code relying on CommonJS require, you're correct.
You're also correct that node offers a natire require implementation, whereas browsers do not.
There are tools - such as webpack, browserify, and requirejs (among others,) which each do at least two things:
to package up source into a single bundle
to expose that source in a way that satisfies require to match node, allowing you to use the same code at either side.
To that end, what you need to do is to pair babel with one of the packaging tools.
Webpack is more powerful; browserify is easier to use.
Here's a tiny gulpfile where I've automated the process. The relevant source clip is this:
gulp.task('browserify', ['babel'], function() {
var browserifyConfig = {},
bpack = browserify(browserifyConfig, { 'debug' : !production });
return bpack
.require('./dist/pbar.es5.js', { 'expose' : 'pbar' })
.bundle()
.on('error', errorHandler)
.pipe(source('pbar.es5.js'))
.pipe(gulp.dest('./dist'));
});
In order for commonjs like require statement to work in a browser environment. You will need to look into a bundling solution like:
https://webpack.github.io/
http://browserify.org/
A bundler will statically parse your commonjs files and their dependencies to create a bundle which can be used in the browser.
Internet is full of great examples on how they work.
Browserify is easier to get started than Webpack, however I would suggest you learn Webpack over Browserify. Webpack provides you much much more than just loading JS files with its extensive loaders, for example you can do something like:
const imgSrc = require('images/test.svg')
magical right?