Because enzyme
is not being maintained and does not support react 18+, I am trying to migrate 1750+ existing unit tests to run with react-testing-library
+ global-jsdom
, so life can go on and our app can keep running latest version of react. All our tests were written using; mocha
, chai
, enzyme
- and I want to migrate as simply as possible. In other words I am under no circumstacens going to rewrite 1750+ test in a completely new framework like jest
.
I'm trying to follow an example of using react-testing-library
to unit test react components. If I use simple elements like 'div'
or 'input'
when using React.createElement
it works just fine, but when I use material UI components, I get an error:
TypeError: Cannot read properties of null (reading 'registered') at C:\Users\user\Documents\project\node_modules@emotion\styled\base\dist\emotion-styled-base.cjs.dev.js:143:53
The above error occurred in the <Styled(div)> component:
at C:\Users\user\Documents\project\node_modules\@emotion\react\dist\emotion-element-b63ca7c6.cjs.dev.js:43:23 at Box (C:\Users\user\Documents\project\node_modules\@mui\system\createBox.js:29:41) at DummyComponent (C:\Users\user\Documents\project\act-app\src\component\page\dummyComponent.js:2:346)
Consider adding an error boundary to your tree to customize error handling behavior. Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries. The stacktrace is not accurate, but it fails where I try to do
h(Box, {},...)
(create the mui element).
This is the dummyComponent.js
that I am trying to render:
const { Box } = require('@mui/material');
const React = require('react');
const h = React.createElement;
const DummyComponent = (props) => {
const { children } = props;
const [showChild, setShowChild] = React.useState(false);
return (
h(Box, {},
h('label', { htmlFor: 'toggle' }, 'Show/Hide'),
h('input', { id: 'toggle', type: 'checkbox', onChange: (e) => setShowChild(e.target.checked), checked: showChild }),
showChild && children)
);
};
module.exports = DummyComponent;
This is the mocha unit test:
const React = require('react');
const { render, fireEvent } = require('@testing-library/react');
const h = React.createElement;
const DummyComponent = require('./dummyComponent');
describe('pageCenter', function () {
before(function () {
this.jsdom = require('global-jsdom')();
});
after(function () {
this.jsdom();
});
it('should render', function () {
const w = render(h(DummyComponent, {}, 'Hi!'));
w.queryAllByText('Hi!').should.have.length(0);
fireEvent.click(w.getByLabelText('Show/Hide'));
w.queryAllByText('Hi!').should.have.length(1);
});
});
It feels like I am missing some context to allow MUI components to be rendered, but I can't seem to figure out what, or if this is actually the case. There are not too many google results on this particular error. Any ideas?
Following the MUI librarie dependencies I saw they were using @emotion/react and @emotion/styled for some rendering processes, it looks like a problem with MUI v5 where they change the cache object and accidentally remove the cache object provided by @emotion/react, this leads to the TypeError: Cannot read property 'registered' of undefined
cause they are not adding the cache.registered
in the new cache.
Solution: I solved the issue just by wrapping my component in @emotion/react provider ({my component}). You can try following this example provided by @emotion.react: https://emotion.sh/docs/cache-provider
Also make sure to have proper dependencies from @emotion/react by runnin:
npm install --save @emotion/react
or yarn add @emotion/react
this is how your code should look:
This is the dummyComponent.js in a more familiar JSX syntax:
const React = require('react');
const { Box } = require('@mui/material');
const DummyComponent = (props) => {
const { children } = props;
const [showChild, setShowChild] = React.useState(false);
return (
<Box>
<label htmlFor="toggle">Show/Hide</label>
<input
id="toggle"
type="checkbox"
onChange={(e) => setShowChild(e.target.checked)}
checked={showChild}
/>
{showChild && children}
</Box>
);
};
export default DummyComponent;
This is the mocha unit test:
const React = require('react');
const { expect } = require('chai');
const { render, fireEvent } = require('@testing-library/react');
const createCache = require("@emotion/cache");
const DummyComponent = require('./dummyComponent');
describe('pageCenter', function () {
before(function () {
this.jsdom = require('global-jsdom')();
});
after(function () {
this.jsdom();
});
const myCache = createCache({
key: 'my-prefix-key'
});
it('should render', function () {
const { queryAllByText, getByLabelText } = render(
<CacheProvider value={myCache}>
<DummyComponent>Hi!</DummyComponent>
</CacheProvider>
);
expect(queryAllByText('Hi!')).should.have.length(0);
fireEvent.click(getByLabelText('Show/Hide'));
expect(queryAllByText('Hi!')).should.have.length(1);
});
});
Notice that I'm also using some JSX syntax in the test case and const { expect } = require('chai');
this allows me to use should
and chain other functions from chai.