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();
});
});
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.
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
Mac OS Version 10.14.5 (18F132)
| 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) |
I'm having the same issue. Any solutions?
What is comp.debug()?
What is
comp.debug()?
That just prints out the HTML of the given comp for diagnostic purposes. I sed it to check if it was indeed picking up the correct component.
Also, in my case, the
but inside the testing environment (in my case Mocha with "jsdom-global/register" installed with npm)
it is not rendered despite checking the window.innerWidth (which when logged is 1024 as in @asnaseer-resilient case)
Just found the problem in my case!
In my
and i thought it was true only when below 960px!!!
@asnaseer-resilient that doesn't actually print out the HTML, that prints out the enzyme react tree, which contains non-html components - can you please provide the actual full output of comp.debug()?
@asnaseer-resilient that doesn't actually print out the HTML, that prints out the enzyme react tree, which contains non-html components - can you please provide the actual full output of
comp.debug()?
I have already provided the full output in my report above - repeated here:
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>
I guess I'm a bit confused, because you're using mount, and Hidden outputs WithWidth(HiddenJs) but no other children. What are these components?
Also, your test is looking for an ID of "'im-also-here", but you can clearly see that it is not present in that debug output. Why would you expect hiddenComp.exists() to be truthy, when it doesn't actually exist?
I guess I'm a bit confused, because you're using
mount, andHiddenoutputsWithWidth(HiddenJs)but no other children. What are these components?Also, your test is looking for an ID of "'im-also-here", but you can clearly see that it is _not_ present in that debug output. Why would you expect
hiddenComp.exists()to be truthy, when it doesn't actually exist?
Yes - that is the point. The hiddenComp _should_ be present as the test is being run with a Window width of 1024 (as displayed in the console output). So both comp.debug() and the test itself fail to "see" this component.
Recall that I stated "If I run npm start then I correctly see both Hello World! and Goodbye World! elements."
It's a fairly simple setup so you should be able to reproduce this in your environment which might help in diagnosing why this is failing.
I believe jsdom has issues with correctly reporting viewport sizes. Can you share the exact component code that responds to the window width?
I believe jsdom has issues with correctly reporting viewport sizes. Can you share the exact component code that responds to the window width?
I have included that code in the report as follows:
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;
what about Hidden? are you relying on App.css to do anything?
Hidden is a standard component from the @material-ui library. App.css is exactly what the default create-react-app generates and nothing inside it is being used in the example.
This is the contents of App.css:
.App {
text-align: center;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 40vmin;
pointer-events: none;
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
Hi, I have the same problem.
When I want to test hidden nested components, I don't find any nested elements.
The only way to see this elements, it's to change the implementation property to css
But I want to keep the JS implementation.
Please give me some help with this Hidden component 🙏
OK, so the implication is that something about Material UI's Hidden component's JS implementation conflicts with jsdom, or, uses hooks in a way that doesn't work with mount (which would be a surprising and new problem, and one that definitely needs fixing).
That leads me here: https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/Hidden/HiddenJs.js
It's using useTheme, but this seems unrelated; withWidth() seems to be relevant, which leads me here: https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/withWidth/withWidth.js
This seems to be using media queries. https://github.com/jsdom/jsdom/blob/d6f8a97b5fb7709d6ad0215c1ae95fd4cab58489/lib/jsdom/level2/style.js#L29 and https://github.com/testing-library/jest-dom/issues/113 both strongly suggest that jsdom does not support media queries - which means that it's impossible to test this implementation outside of a real browser (a suggestion like this: https://github.com/jsdom/jsdom/issues/2342#issuecomment-415086243 which has you manually set the width may work, but probably won't work with media queries)
Thus, I'm going to close this since it's either a bug in jsdom, or a flaw in Material UI's choice of implementation.
I'd suggest using shallow rendering anyways - testing Material UI itself is not something you really need to care about, since that's what its own tests cover.
OK, so the implication is that something about Material UI's Hidden component's JS implementation conflicts with jsdom, or, uses hooks in a way that doesn't work with
mount(which would be a surprising and new problem, and one that definitely needs fixing).That leads me here: https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/Hidden/HiddenJs.js
It's using
useTheme, but this seems unrelated;withWidth()seems to be relevant, which leads me here: https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/withWidth/withWidth.jsThis seems to be using media queries. https://github.com/jsdom/jsdom/blob/d6f8a97b5fb7709d6ad0215c1ae95fd4cab58489/lib/jsdom/level2/style.js#L29 and testing-library/jest-dom#113 both strongly suggest that jsdom does not support media queries - which means that it's impossible to test this implementation outside of a real browser (a suggestion like this: jsdom/jsdom#2342 (comment) which has you manually set the width may work, but probably won't work with media queries)
Thus, I'm going to close this since it's either a bug in jsdom, or a flaw in Material UI's choice of implementation.
I'd suggest using shallow rendering anyways - testing Material UI itself is not something you really need to care about, since that's what its own tests cover.
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.
I found a trick after some investigation.
You can hack materialUI by passing a custom theme to your mount.
If you look the withWidth.js file you can see that the WithWidth method extract some properties from the current theme.
And if you read comments about initialWidth in PropTypes definition, you understand that initialWidth 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.
const theme = createMuiTheme({ props: { MuiWithWidth: { initialWidth: 'xs' } } })
const wrapper = createMount()(<MuiThemeProvider theme={theme}>
<MyComponentWithHidden />
</MuiThemeProvider>);
@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 <3!
Thanks for confirming!!
My solution to test content - wrapped in Hidden element:
it('should display correct score of the selected question while landing on question player', () => {
const topPanelScoreHiddenElement = questionPlayer.find('div[data-testid="assessment-top-panel-score"]').find(Hidden).at(0)
const topPanelScore = mount(<>{topPanelScoreHiddenElement.prop('children')}</>)
expect(topPanelScore.find('h4').text())
.toEqual('2 / 12')
})
Most helpful comment
I found a trick after some investigation.
You can hack materialUI by passing a custom theme to your
mount.If you look the
withWidth.jsfile you can see that theWithWidthmethod extract some properties from the current theme.And if you read comments about
initialWidthin PropTypes definition, you understand thatinitialWidthallows 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
Hiddenchildren correctly.