(FIX) React-Starter-Kit: testing isomorphic-style-loader with jest and enzyme

TypeError: Cannot read property ‘apply’ of undefined

Mojtaba Izadmehr
4 min readDec 25, 2018

TLDR: go to the summary section for the fix:)

React-Starter-Kit provides isomorphic-style-loader as a method of injecting styles to the components, which is essentially the same as the method provided by the CSS-Modules.

In this approach, the styles are written in a separate file (e.g. *.scss file) which is imported in the top of the file. Then withStyles method is imported from the isomorphic-style-loader, and used as a higher order component to provide the styles as a variable in the component.

import React from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import styles from './styles.scss';
const Component = () => (
<div className={styles.component}>some styled input</div>
);

export default withStyles(styles)(Home);

Although the styles are written in css format, it isomorphic-style-loader, allows us to use the whole file as an object, and to be able to import the class names as attributes of this object. For example if the style file written in the following way:

.component {
color: red;
}

now we are able to write:

<div className={styles.component}>some styled input</div>

This method also creates unique classnames in order to prevent naming conflicts in the project. The created names are in the following format:

[css file name]-[classname]-[random id]

For example in the case of our example, the name of css file is styles.scss, the classname is component,as a result we will have a generated class name in the following format:

styles-component-3CHFb

We can use the name of the style file in order to create more meaningful names, such as:

Home-component-3CHFb

Testing Component with JEST

Up to this point everything is working fine, and if we want to test the component with jest we can simply import the component in our test file and write test scenarios on it (But not so fast…)

If we import the exported component in our jest file, we will face the following error:

SyntaxError: Invalid or unexpected token @import styles from './styles.scss';

because it fails to import the scss file.

we need to mock style files. One way to do so is by using identity-obj-proxy.

identity-obj-proxy is a mocking object field tool. This library provides identity strings, for example, if we try to get the foo field of the mock provider it will return ‘foo’ string for us. for example:

import idObj from 'identity-obj-proxy';console.log(idObj.foo); // 'foo'console.log(idObj.bar); // 'bar'console.log(idObj[1]); // '1'

This method can help us to fake all the classnames required in our component by fake value. In order to add this to our jest, we need to find the jest.config.js file in the react-starter-kit root directory and see this part:

moduleNameMapper: {
'\\.(css|less|styl|scss|sass|sss)$': 'identity-obj-proxy',
},

The react-starter-kit already provides us with this line, using moduleNameMapper.

moduleNameMapper is a map from regular expressions to module names that allow to stub out resources, like images or styles with a single module.

Now if we import the component without withStyles we can simply use it in the test file:

import React from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import styles from './styles.scss';
export const Component = () => (
<div className={styles.component}>some styled input</div>
);

export default withStyles(styles)(Home);

and then import only the main component in the test file:

import {Component} from '../Component';

What if we import the main exported component?

If we import the default component, without brackets:

import Component from '../Component';

we will get an error:

TypeError: Cannot read property 'apply' of undefined

because it is not going to be able to handle the higher order component. And this is going the be great pain if we want to mount a component (instead of shallow rendering) which has some children, in which the styles are imported using withStyles.

Then, we need to mock the whole isomorphic-style-loader library.

Solution

In order to mock a whole library, we again can use moduleNameMapper method in the config file. moduleNameMapper is an object, in which the attributes are the imported resources which are going to be mocked, and the values are the mocked values. In order to mock the isomorphic-style-loader we can simply add a filed with this module and the value of a mock function:

'isomorphic-style-loader/lib/withStyles':
'<rootDir>/tools/mocks/withStyles.js',

the result of this line of code is that, when ever jest file faces:

import withStyles from 'isomorphic-style-loader/lib/withStyles'

it changes this import with:

import withStyles from '<rootDir>/tools/mocks/withStyles.js'

Then we only need to create a file with the name of withStyles.js in the following route:

<rootDir>/tools/mocks

which has the following content:

module.exports = () => component => component;

consequently or moduleNameMapper will look like this:

moduleNameMapper: {
'\\.(css|less|sass|scss)$': 'identity-obj-proxy',
'isomorphic-style-loader/lib/withStyles':
'<rootDir>/tools/mocks/withStyles.js',
},

In order to also, mock images and static files, not to get any error in them, we can add another propery in our moduleNameMapper config:

'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/tools/mocks//fileMock.js',

this property, mocks all static imports such as images and fonts and media files with the fileMock.js. we can simply create a fileMock.js file in that route with this content:

module.exports = 'test-file-stub';

test-file-stub provides mock replace for the static assets.

Summary

In summary, in order to deal with style inject errors, we can go to jest.config.js file in root directory of react-starter-kit, and find moduleNameMapper config and change it the the following value:

moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/tools/mocks//fileMock.js',
'\\.(css|less|sass|scss)$': 'identity-obj-proxy',
'isomorphic-style-loader/lib/withStyles':
'<rootDir>/tools/mocks/withStyles.js',
},

Then we simply need to create a folder called mocks, in the tools directory (where webpack config file is located).

Finally, inside the mocks folder, we need to create to files:

fileMock.js

module.exports = 'test-file-stub';

withStyles.js

module.exports = () => component => component;

And we can import components with imported styles to our test files:)

--

--