Unit testing ES7 React components with Jest - javascript

How do you make Jest play nicely with ES7 initializers? I've searched far a wide on here and other sources, but didn't find anything conclusive.
.babelrc.js
{
"env": {
"development": {
"presets": [["es2015", { "modules": false }], "react", "react-hmre"],
"plugins": [
"transform-class-properties",
"react-hot-loader/babel"
]
},
"test": {
"presets": ["env", "react"],
"plugins": ["transform-class-properties"]
},
"production": {
"presets": [["es2015", { "modules": false }], "react"],
"plugins": ["transform-class-properties"]
}
}
}
package.json
{
"name": "demo",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"dependencies": {
"react": "^15.5.4",
"react-dom": "^15.5.4",
},
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-core": "^6.24.1",
"babel-jest": "^20.0.3",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-polyfill": "^6.23.0",
"babel-preset-env": "^1.5.2",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-react-hmre": "^1.1.1",
"enzyme": "^2.8.2",
"react-hot-loader": "next",
"babel-plugin-import": "^1.2.1",
"enzyme": "^2.9.1",
"enzyme-to-json": "^1.5.1"
},
"scripts": {
"test": "export NODE_ENV=test && ./node_modules/.bin/jest --no-cache"
},
"engines": {
"node": ">= 7.8.0"
},
"jest": {
"verbose": true,
"collectCoverage": true,
"coverageDirectory": "__coverage__",
"mapCoverage": true,
"setupFiles": [
"./tests/setup.js"
],
"testPathIgnorePatterns": [
"/node_modules/"
],
"transform": {
"\\.js$": "../node_modules/babel-jest"
},
"testRegex": ".*\\.test\\.js$",
"snapshotSerializers": [
"enzyme-to-json/serializer"
]
}
}
Demo.jsx
import React from 'react';
import PropTypes from 'prop-types';
export class Demo extends React.Component {
static props = { name: PropTypes.string.isRequired };
constructor(props) {
super(props);
}
render() {
return (
<div className='demo'>{this.props.name}</div>
);
}
}
Demo.test.js
import React from 'react';
import { Demo } from '..';
import { render } from 'enzyme';
describe('Demo', () => {
it('renders correctly', () => {
const wrapper = render(<Demo name="foo" />);
expect(wrapper).toMatchSnapshot();
});
});
After running yarn test or export NODE_ENV=test && ../node_modules/.bin/jest --no-cache, Jest will complain that it sees an unexpected character
8 | props = {
| ^
9 | name: PropTypes.string.isRequired
From my understanding, the environment variable set when we run the tests should automatically transform the initializers to something which can be used by Jest, but this doesn't appear to be happening.
I am also using webpack 2.x, but the configuration overhead to get that to work seems daunting. Is there another way?
Update 1
I modified my Jest settings as follows:
"transform": {
"\\.js$": "./node_modules/babel-plugin-transform-class-properties"
}
This failed immediately with:
TypeError: Jest: a transform must export a process function.
Removing transform altogether yields a slightly different issue:
TypeError: Cannot read property 'props' of null
I also added the constructor to the Demo component.

I believe if you are trying to add propTypes, with ES7 initializers and using the transform-class-properties you need to do
static propTypes = {
name: PropTypes.string.isRequired
};
So it would be
import React from 'react';
import PropTypes from 'prop-types';
export class Demo extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired
};
render() {
return (
<div className='demo'>{this.props.name}</div>
);
}
}
Pretty sure you do not have to explicitly define props as it is part of the React.Component when you extends from it. That or you may need to declare constructor and then call super(props);

Jest uses babel to transpile JS code. You need to add support for ES7 features in .babelrc file.
Here are the steps:
Do npm install --save-dev babel-preset-stage-0 babel-jest
In your package.json, add babel-jest for transforming all the js files.
In your .babelrc file, add "stage-0" preset.
Here is how my package.json looks like:
...
"jest": {
"transform": {
"^.+\\.js$": "babel-jest"
}
}
...
And here is how my .babelrc file looks like:
{
"presets": [
"es2015",
"react",
"stage-0"
]
}

Related

React component error: expected a string or a class/function but got undefined

I am getting the following error in a create-react-app. Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
The app is currently a super-basic - it just imports and renders one single component:
import './App.css';
import { PasswordInput } from 'gg-react-test-lib';
function App() {
return (
<div className="App">
<PasswordInput />
</div>
);
}
export default App;
I am therefore assuming that the problem is with my component library that I am importing the PasswordInput component from. I am installing it from NPM.
It has an index.js file like:
import Radio from './Radio/Radio';
export { PasswordInput, Radio }```
Those component files themselves do a default export at the bottom of the file e.g.:
`export default PasswordInput;`
I have a pretty minimal webpack.config.js file in my component library repo:
```const TerserPlugin = require('terser-webpack-plugin');
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader"
}
]
},
output: {
path: path.resolve(__dirname, "dist/"),
filename: 'bundle.js'
},
optimization: {
minimizer: [new TerserPlugin()]
},
};
My package.json:
{
"name": "gg-react-test-lib",
"version": "1.0.7",
"description": "",
"main": "dist/bundle.js",
"module": "src/index.js",
"author": "",
"license": "ISC",
"dependencies": {
"clsx": "^1.0.4",
"node-sass": "^4.13.0",
"react": "^16.12.0",
"react-dom": "^16.12.0"
},
"scripts": {
"build": "webpack"
},
"devDependencies": {
"#babel/cli": "^7.7.5",
"#babel/core": "^7.7.5",
"#babel/preset-react": "^7.7.4",
"babel-loader": "^8.0.6",
"webpack": "^4.41.3",
"webpack-cli": "^3.3.10"
}
}
.babelrc file:
{
"presets": [
"#babel/preset-react"
]
}

Simple react component not accepting arrow function

I'm trying to create my first component in react but I keep getting error. It results in not showing button element on the website at all. Here are my files:
ERROR in ./src/js/components/presentational/Button1.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
SyntaxError: Unexpected token (3:13)
1 | import React, { Component } from "react";
2 | class Button1 extends React.Component {
> 3 | handleClick = () => {
| ^
4 | console.log("dupa");
5 | };
6 | render() {
./src/js/components/presentational/Button1.js
import React, { Component } from "react";
class Button1 extends React.Component {
handleClick = () => {
console.log("dupa");
};
render() {
return (
<button onclick={this.props.handleClick}>
Button
</button>
);
}
}
export default Button1;
./src/js/components/container/FormContainer.js
import React, { Component } from "react";
import ReactDOM from "react-dom";
import Button1 from "../presentational/Button1";
class FormContainer extends Component {
render() {
return (
<Button1 />
);
}
}
export default FormContainer;
const wrapper = document.getElementById("create-article-form");
wrapper ? ReactDOM.render(<FormContainer />, wrapper) : false;
./src/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" >
<title>How to set up React, Webpack, and Babel</title>
</head>
<body>
<div class="container">
<div class="row mt-5">
<div class="col-md-4 offset-md-1">
<p>Create a new article</p>
<div id="create-article-form">
<!-- form -->
</div>
</div>
</div>
</div>
</body>
</html>
EDIT
I thought I was using babel. Do I need some additional step for transpilation? Currently I'm using only
npm start
This is my package.json:
{
"name": "front-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server --open --mode development",
"build": "webpack --mode production",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-preset-env": "^1.7.0",
"babel-preset-latest": "^6.24.1",
"babel-preset-react": "^6.24.1",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.2.0",
"prop-types": "^15.6.2",
"react": "^16.4.2",
"react-dom": "^16.4.2",
"webpack": "^4.16.5",
"webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.5"
}
}
and this is my .babelrc
{
"presets": [
"react",
"env"
]
}
EDIT2:
Problem solved. As stated here it was because arrow functions are not included into standard right now. I had to run sudo npm install --save-dev babel-plugin-transform-class-properties and had to edit my .babelrc:
{
"presets": [
"react",
"env"
],
"plugins": [
["transform-class-properties", { "spec": true }]
]
}
You get the error because class properties (class Example { myProperty = 'foobar' }) is not a part of the language yet.
You need to add either a Babel plugin or preset for it to work. You could use the stage 2 preset.
.babelrc
{
"presets": [
"react",
"env"
"stage-2"
]
}
You can also do:
handleClick() {
// code here
}

Uncaught Error: Minified React error #130

So here is what my code looks like
---------index.js-----
var React =require('react');
var ReactDOM =require('react-dom');
var App=require('../components/App');
ReactDOM.render(<App />, document.getElementById('app'));
---------App.js-----
var React = require('react');
class App extends React.Component {
render() {
return(
<div>
<h1>HI wassup</h1>
</div>
);
}
}
export default App;
---------package.json-----
{
"name": "views",
"version": "1.0.0",
"description": "learning",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server --mode development --hot && webpack"
},
"author": "vinayak",
"license": "ISC",
"dependencies": {
"react": "^16.4.2",
"react-dom": "^16.4.2"
},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-preset-react": "^6.24.1",
"html-webpack-plugin": "^3.2.0",
"unminified-webpack-plugin": "^2.0.0",
"webpack": "^4.16.5",
"webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.5"
}
}
---------webpackconfig-----
const HTMLWebpackPlugin=require('html-webpack-plugin');
const webpack=require('webpack');
const UnminifiedWebpackPlugin = require('unminified-webpack-plugin');
const HTMLWebpackPluginConfig=new HTMLWebpackPlugin({
template: '../../views/index.hbs',
filename:'index.hbs'
});
module.exports={
entry:__dirname + '/app/index.js',
module:{
rules:[
{
test:/\.js$/,
exclude:/node_modules/,
loader:'babel-loader'
}
]
},
plugins: [
new UnminifiedWebpackPlugin(),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('development')
}
})
],
devtool: 'source-map',
output:{
filename:'app.js',
path:__dirname + '../../public/javascripts/'
},
devServer:{
inline:false
}
};
------------------folder structure-------------
|react
|node modules
|components
|App.js
|app
|index.js
Everything works fine but when I am execting the app in browser I get the react error and gives a link which displays the following.
"Element type is invalid: expected a string (for built-in components)
or a class/function (for composite components) but got: object."
You're mixing up require, and import / export.
Since you're running webpack, all of your React code (and anything that gets transpiled by babel via webpack) should stick to using import/export. require should only be used in places like js that's directly run by node.
In your index.js, change all the requires to imports and see if that helps.
In the file index.js, you should change your code like this:
var App = require('../components/App').default;
or use import
import App from '../components/App';
I recommend using a unified usage. You can import/export or module.exports/require.

import undefined when bundling ui library with rollup using yarn workspaces

For a new project, I started using rollup to bundle a UI library and consume that library in a react application. I'm also using yarn workspaces for the internal dependency management between the UI library and the web app.
When I try to use the UI library in my web app, the import returns undefined and throws the "cannot get from undefined" error.
TypeError: Cannot read property 'NavBar' of undefined
[0] at App (C:/Users/user/dev/project/packages/project-web/src/pages/App.jsx:9:6)
The webapp code:
import React from 'react';
import {NavBar} from 'project-ui';
const App = () => (
<div>
<NavBar/>
<div>App component!x</div>
</div>
);
root package.json:
{
"name": "project",
"version": "1.0.0",
"private": true,
"workspaces": [
"packages/*"
]
}
UI package.json:
{
"name": "project-ui",
"version": "1.0.0",
"main": "dist/project-ui.cjs.js",
"jsnext:main": "dist/project-ui.es.js",
"module": "dist/project-ui.es.js",
"files": ["dist"],
"scripts": {
"build": "rollup -c"
},
"peerDependencies": {
"react": "16.3.2",
"react-dom": "16.3.2"
},
"devDependencies": {
"babel-core": "6.26.3",
"babel-plugin-external-helpers": "6.22.0",
"babel-preset-env": "1.6.1",
"babel-preset-react": "6.24.1",
"babel-preset-stage-2": "6.24.1",
"rollup": "0.60.0",
"rollup-plugin-babel": "3.0.4",
"rollup-plugin-commonjs": "9.1.3",
"rollup-plugin-node-resolve": "3.0.0",
"rollup-plugin-replace": "2.0.0",
"rollup-plugin-uglify": "4.0.0"
}
}
web app package.json:
{
"name": "project-web",
"version": "1.0.0",
"scripts": {
"build": "webpack --colors --display-error-details --config=webpack/webpack.dev.js",
"dev": "concurrently --kill-others \"npm run dev:start\"",
"dev:start": "node ./server/index.js"
},
"dependencies": {
"babel-polyfill": "^6.26.0",
"express": "^4.16.3",
"react": "^16.3.2",
"react-dom": "^16.3.2",
"project-ui": "1.0.0"
},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.3",
"babel-loader": "^7.1.4",
"babel-plugin-add-module-exports": "^0.2.1",
"babel-plugin-dynamic-import-node": "^1.2.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"concurrently": "^3.5.1",
"eslint": "^4.19.1",
"eslint-loader": "^2.0.0",
"eslint-plugin-react": "^7.7.0",
"piping": "^1.0.0-rc.4",
"webpack": "^4.6.0",
"webpack-cli": "^2.0.15",
"webpack-dev-middleware": "^3.1.3",
"webpack-dev-server": "^3.1.3",
"webpack-hot-middleware": "^2.22.1",
"webpack-node-externals": "^1.7.2"
}
}
rollup config:
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import babel from 'rollup-plugin-babel';
import replace from 'rollup-plugin-replace';
import { uglify } from 'rollup-plugin-uglify';
import pkg from './package.json'
const FORMATS = {
UMD: 'umd',
ES: 'es',
CJS: 'cjs'
};
const allowedFormats = [FORMATS.UMD, FORMATS.ES, FORMATS.CJS];
const bundle = (fileFormat, {format, minify}) => {
if (!allowedFormats.includes(format)) {
throw new Error(`Invalid format given: ${format}`);
}
const shouldMinify = minify && format === FORMATS.UMD;
const externals = format === FORMATS.UMD
? Object.keys(pkg.peerDependencies || {})
: [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {})
];
return {
input: 'src/index.js',
output: {
file: fileFormat.replace('{format}', shouldMinify ? `${format}.min` : format),
format,
name: 'project-ui',
exports: 'named',
globals: {
react: 'React',
'prop-types': 'PropTypes'
}
},
external: externals,
plugins: [
resolve({ jsnext: true, main: true }),
commonjs({ include: 'node_modules/**' }),
babel({
exclude: 'node_modules/**',
}),
format === FORMATS.UMD
? replace({'process.env.NODE_ENV': JSON.stringify(shouldMinify ? 'production' : 'development')})
: null,
shouldMinify ? uglify() : null
].filter(Boolean)
};
};
export default [
bundle('dist/project-ui.{format}.js', {format: FORMATS.UMD, minify: true}),
bundle('dist/project-ui.{format}.js', {format: FORMATS.CJS}),
bundle('dist/project-ui.{format}.js', {format: FORMATS.ES})
];
the actual generated code from rollup:
import React from 'react';
var NavBar = function NavBar() {
return React.createElement(
'header',
null,
'nav bar'
);
};
module.exports = exports['default'];
export { NavBar };
The original navbar:
import React from 'react';
const NavBar = () => (
<header>
nav bar
</header>
);
export default NavBar;
index.js:
export { default as NavBar} from './NavBar/NavBar';
.babelrc:
{
"presets": [
["env", {
"loose": true,
"modules": false,
"targets": {
"browsers": ["last 2 versions"]
}
}],
"react",
"stage-2"
],
"plugins": [
"transform-runtime",
"add-module-exports",
"external-helpers"
]
}
The generated rollup code looks to be ok, so I'm thinking this is a yarn issue, but I'm not sure. Any help would be appreciated!
Regards
Cornel
The problem must be in the way you are transpilling the code with babel/rollup. I have a live example in how your code should look in an online babel:
The generated code for me is:
import React from 'react';
const NavBar = () => React.createElement(
'header',
null,
'nav bar'
);
export default NavBar; // first define default and then we assign export['default']
module.exports = exports['default'];
Observe that in this code we first assign export default to the desired value and then we assign export['defaults'] (when I debug your example I get that export['default'] is undefined, hence you get the error Cannot read property 'NavBar' of undefined [0], since you are passing undefined to exports.
This is done by the plugin 'add-module-exports', that is kind of necessary if you really need module.exports (that shouldnt be needed unless NodeJS or some RequireJS is present).
In order to make it work, just remove "add-module-exports" from your plugins in the project-ui .babelrc.

after add component link error: Uncaught SyntaxError: Unexpected token export

i create test react project. Add modules and my packege.json look like:
{
"name": "untitled",
"version": "0.1.0",
"private": true,
"devDependencies": {
"babel-preset-node5": "^12.0.1",
"react-scripts": "0.9.5"
},
"dependencies": {
"babel-preset-stage-0": "^6.22.0",
"history": "^4.6.1",
"react": "^15.4.2",
"react-dom": "^15.4.2",
"react-native": "^0.42.3",
"react-router": "^4.0.0",
"react-router-config": "^1.0.0-beta.1",
"react-router-dom": "^4.0.0",
"react-router-native": "^4.0.0",
"webpack": "^2.2.1",
"webpack-dev-server": "^2.4.2"
},
"scripts": {
"start": "react-scripts start",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"build": "babel input.js -o compiled.js"
}
}
and webpack.config.js
var config = {
entry: './index.js',
output: {
path:'/',
filename: 'index.js',
},
devServer: {
inline: true,
port: 8080
},
module: {
loaders: [
{
test: /\.js?$/,
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['es2015', "node5", "stage-1", 'react', "stage-0"]
}
}
]
}
};
module.exports = config;
in my component i import component Link from react-router-native and after this adding react show error 'Uncaught SyntaxError: Unexpected token export' but if i delete this string project work well. it's not the only component in the module, i can add any components like Promt, Route or Router. Why it's not work with Link?
This is code where error reproduced
import React from 'react';
import { Route, Router } from 'react-router';
import { Promt } from 'react-router';
import { Link } from 'react-router-native';
import logo from '../../logo.svg';
import './App.css';
class App extends React.Component {
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Welcome to React</h2>
</div>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
);
}
}
export default App;
You need to update the regular expression for the test value in your web pack config to be test: /(\.js|\.jsx)$/
Right now you are telling webpack to only run .js files through babel loader, but you aren't telling it to also run .jsx files through babel loader.

Categories