Reputation: 103
EDIT: I was able to determine that MUI's instructions work correctly when using RTL. This issue is only taking place in Enzyme tests!
I'm following MUI's documentation on how to test useMediaQuery, but I am confused as to whether or not the way I am using useMediaQuery (outlined here in MUI's docs) in my component is compatible with the testing instructions in MUI's docs.
Here's the code in my component:
import { useTheme } from '@mui/material/styles';
import useMediaQuery from '@material-ui/core/useMediaQuery';
const List = () => {
const theme = useTheme();
const isDownLargeBreakpoint =
useMediaQuery(theme.breakpoints.down('lg'));
...
{isDownLargeBreakpoint && (
<ul className="list">
// list items
</ul>
)}
}
The useMediaQuery hook works as expected when I run my app locally, it correctly toggles between true
and false
when I resize the screen below/above MUI's theme lg
breakpoint.
When I try to run my test with the recommended method of setup, despite the window.innerWidth falling below what would satisfy useMediaQuery to return a value of true
I always get false
in my test. Perhaps it's because I'm not rerendering my component from within my test? Or do I have to do something more in my it
clause to trigger what is needing to happen?
Here's the block of code using css-mediaquery
recommended by MUI as well as this post which was already answered:
import mediaQuery from 'css-mediaquery';
function createMatchMedia(width) {
return (query) => ({
matches: mediaQuery.match(query, {
width,
}),
addListener: () => {},
removeListener: () => {},
});
}
describe('MyTests', () => {
beforeAll(() => {
window.matchMedia = createMatchMedia(window.innerWidth);
});
});
Here's how I've organized my test file:
import React from 'react';
import { shallow } from 'enzyme';
import mediaQuery from 'css-mediaquery';
import SessionStore from 'app/stores/SessionStore';
import CustomFields from '../CustomFields';
import CustomFieldButton from '../CustomFieldButton';
import PrepareFieldsList from '../PrepareFieldsList';
describe('PrepareFieldsList Component', () => {
let wrapper;
function createMatchMedia(width) {
return (query) => ({
matches: mediaQuery.match(query, {
width,
}),
addListener: () => {},
removeListener: () => {},
});
}
beforeAll(() => {
window.matchMedia = createMatchMedia(window.innerWidth);
});
const defaultProps = {
customFields: [
{
data: null,
id: 'fieldId',
name: '',
required: false,
value: 'test',
},
],
};
beforeEach(() => {
jest.spyOn(SessionStore, 'getSession').mockReturnValue({
hasFeature: () => true,
});
wrapper = shallow(<PrepareFieldsList {...defaultProps} />);
});
...
it('should render CustomFieldButton and CustomFields when hasFeature is true', () => {
expect(wrapper.find(CustomFieldButton)).toHaveLength(1);
expect(wrapper.find(CustomFields)).toHaveLength(1);
});
});
Upvotes: 1
Views: 1559
Reputation: 1
I encountered a simillar issue while working with jsdom and vitest.
Here is what worked for me:
First I installed css-mediaquery
npm install [email protected] --save-dev
Then I installed @types/css-mediaquery (needed for typescript)
npm install @types/[email protected] --save-dev
Then implemented the createMatchMedia function and used as in the following example.
//other imports
import { match } from "css-mediaquery";
const createMatchMedia = (width: number) => {
return (query: string) => ({
matches: match(query, {
width,
}),
addEventListener: () => {},
removeEventListener: () => {},
media: "",
dispatchEvent: () => false,
onchange: () => {},
addListener: () => {},
removeListener: () => {},
});
};
describe.each([
{
width: 1
},
{
width: 600
} //etc, or create a util function to generate them
])(
"Width: $width",
({ width }) => {
beforeAll(() => {
window.matchMedia = createMatchMedia(width);
});
it("should do something", () => {
render(<MyComponent></MyComponent>, { wrapper: MyProviders}) //Make sure to add all necessary providers like the ThemeProvider with your theme as in <ThemeProvider theme={theme} ... etc
//test here
expect(screen.getByRole("button")).toBeInTheDocument();
});
}
);
Upvotes: 0
Reputation: 1
I came across this after spending some time diagnosing my test, which was breaking because my custom breakpoints weren't being recognized. In the end, the answer for me was as simple as wrapping the component being tested in the MUI ThemeProvider (passing my theme). Otherwise, I used the example from the docs for implementing matchMedia (link).
<ThemeProvider theme={theme}>
<Component />
<ThemeProvider>
Upvotes: 0
Reputation: 103
I figured it out. When doing this in Enzyme, you need to mock the hook and its return value with jest.
At the top of the test file:
import useMediaQuery from '@material-ui/core/useMediaQuery';
The mock at the top of the file, below the imports:
jest.mock('@material-ui/core/useMediaQuery');
Then to update the mock value:
useMediaQuery.mockReturnValueOnce(true);
Make sure to do this before you render your component in each test case. So like:
it('should render ChildComponent when useMediaQuery is true', () => {
useMediaQuery.mockReturnValueOnce(true);
const wrapper = shallow(<ParentComponent />);
expect(wrapper).toContainExactlyOneMatchingElement(ChildComponent);
});
Upvotes: 1
Reputation: 3577
I think it's because innerWidth is not defined in window in jest and you need to set it's value manually, so you can pass value in createMatchMedia like : createMatchMedia(1000) or set value to window.innerWidth like this :
Object.defineProperty(window, 'innerWidth', {writable: true, configurable: true, value: 500})
and in your example would be sth like this :
beforeAll(() => {
Object.defineProperty(window, 'innerWidth', {writable: true, configurable: true, value: 500})
window.matchMedia = createMatchMedia(window.innerWidth);
});
Upvotes: 0