enzyme: Unable to test React with Material-UI Hidden element
I have a managed to reproduce my issue in a very simple React app that I created via npx create-react-app xxx
. I then installed material-ui/core
, enzyme
, and enzyme-adapter-react-16
resulting in this package.json
:
{
"name": "xxx",
"version": "0.1.0",
"private": true,
"dependencies": {
"@material-ui/core": "^4.1.3",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-scripts": "3.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.14.0"
}
}
I then modified the default App.js
to this:
import React from 'react';
import Hidden from '@material-ui/core/Hidden';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<div id='im-here'>Hello World!</div>
<Hidden xsDown><div id='im-also-here'>Goodbye World!</div></Hidden>
</header>
</div>
);
}
export default App;
and modified the default App.test.js
to this:
import React from 'react';
import Enzyme, { mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import App from './App';
Enzyme.configure({adapter: new Adapter()});
describe('test', () => {
it('should work', () => {
console.log(window.innerWidth, window.innerHeight);
const comp = mount(<App />);
console.log(comp.debug());
const nonHiddenComp = comp.find('#im-here');
expect(nonHiddenComp.exists()).toBeTruthy();
const hiddenComp = comp.find('#im-also-here');
expect(hiddenComp.exists()).toBeTruthy();
});
});
Current behavior
When I run the tests using npm test
I get this output:
FAIL src/App.test.js
test
✕ should work (41ms)
● test › should work
expect(received).toBeTruthy()
Received: false
15 | expect(nonHiddenComp.exists()).toBeTruthy();
16 | const hiddenComp = comp.find('#im-also-here');
> 17 | expect(hiddenComp.exists()).toBeTruthy();
| ^
18 | });
19 | });
20 |
at Object.toBeTruthy (src/App.test.js:17:33)
console.log src/App.test.js:11
1024 768
console.log src/App.test.js:13
<App>
<div className="App">
<header className="App-header">
<div id="im-here">
Hello World!
</div>
<Hidden xsDown={true} implementation="js" lgDown={false} lgUp={false} mdDown={false} mdUp={false} smDown={false} smUp={false} xlDown={false} xlUp={false} xsUp={false}>
<WithWidth(HiddenJs) xsDown={true} lgDown={false} lgUp={false} mdDown={false} mdUp={false} smDown={false} smUp={false} xlDown={false} xlUp={false} xsUp={false} />
</Hidden>
</header>
</div>
</App>
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 2.511s
Ran all test suites related to changed files.
Expected behavior
If I run npm start
then I correctly see both Hello World!
and Goodbye World!
elements.
However, from the output above you can see that the test fails to find the element protected by the <Hidden> material-ui element. The output shows that the test is running with a window of size 1024px by 768px. I would therefore have expected this element to have been visible and therefore found by the above test.
Your environment
Mac OS Version 10.14.5 (18F132)
API
- shallow
- [X ] mount
- render
Version
library | version |
---|---|
enzyme | 3.10.0 |
react | 16.8.6 |
react-dom | 16.8.6 |
react-test-renderer | 16.8.6 |
@material-ui/core | 4.1.3 |
adapter (below) |
Adapter
- [X ] enzyme-adapter-react-16
- enzyme-adapter-react-16.3
- enzyme-adapter-react-16.2
- enzyme-adapter-react-16.1
- enzyme-adapter-react-15
- enzyme-adapter-react-15.4
- enzyme-adapter-react-14
- enzyme-adapter-react-13
- enzyme-adapter-react-helper
- others ( )
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 5
- Comments: 27 (7 by maintainers)
I found a trick after some investigation.
You can hack materialUI by passing a custom theme to your
mount
. If you look thewithWidth.js
file you can see that theWithWidth
method extract some properties from the current theme.And if you read comments about
initialWidth
in PropTypes definition, you understand thatinitialWidth
allows you to define a basic width, even if you are in jsdom env 😉So, for me, I use it like this and my mounted component generate my
Hidden
children correctly.Thanks for the investigation - much appreciated.
In response to your last comment - I am not actually testing MaterialUI, what I am testing is that the developer has coded to spec, i.e. certain elements should be visible on a page when on a desktop browser but not when on a mobile device.
I will raise this as an issue on both the MaterialUI and jsdom github repos to see if either of them can shed any further light on how to fix this.
@ljharb You are right. We encourage the usage of a media query polyfill with jsdom in the documentation https://material-ui.com/components/use-media-query/#testing. And thanks for looking at it ❤️!
That solution in the MUI docs worked for me:
It seems that as of MUI v5, this solution (which worked great for us for a while! thanks!) no longer works unfortunately, as it seems they have entirely removed WithWidth, so it’s no longer possible to specify it with this props solution during theme creation. They did change their theming structure but there is no equivalent alternative in this new format to specify anything for withWidth in this way, since withWidth is gone. In our code base we had to switch a lot of components to using css media queries in their classes, since those do respond to
global.innerWidth = ...
calls in test rendering, at least for our class components. For our functional components we could follow this part of the migration guide to switch to the useMediaQuery hook, at least for our Hidden components.My solution to test content - wrapped in Hidden element:
For anybody using Typescript:
or
Or with
jest
:@japser36, do you have an example of the changes you made? I am struggling with this same issue, and changing
global.innerWidth
doesn’t seem to be affecting when I useuseMediaQuery
, or if I use thesx display
prop.Your solution work for me, thank you! I just put:
And we can see the element in the snapshot! We can put the id on the Hidden element and find by him: