score:208
The Jest documentation now has an "official" workaround:
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // Deprecated
removeListener: jest.fn(), // Deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
score:0
Because I used a library that used window.matchMedia
what worked for me was requiring the Component (I use React) under test and the window.matchMedia
mock inside jest.isolateModules()
function getMyComponentUnderTest(): typeof ComponentUnderTest {
let Component: typeof ComponentUnderTest;
// Must use isolateModules because we need to require a new module everytime so
jest.isolateModules(() => {
// Required so the library (inside Component) won't fail as it uses the window.matchMedia
// If we import/require it regularly once a new error will happen:
// `TypeError: Cannot read property 'matches' of undefined`
require('<your-path-to-the-mock>/__mocks__/window/match-media');
Component = require('./<path-to-component>');
});
// @ts-ignore assert the Component (TS screams about using variable before initialization)
// If for some reason in the future the behavior will change and this assertion will fail
// We can do a workaround by returning a Promise and the `resolve` callback will be called with the Component in the `isolateModules` function
// Or we can also put the whole test function inside the `isolateModules` (less preferred)
expect(Component).toBeDefined();
// @ts-ignore the Component must be defined as we assert it
return Component;
}
window.matchMedia
Mock (inside /__mocks__/window/match-media
):
// Mock to solve: `TypeError: window.matchMedia is not a function`
// From https://stackoverflow.com/a/53449595/5923666
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => {
return ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // Deprecated
removeListener: jest.fn(), // Deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
});
}),
});
// Making it a module so TypeScript won't scream about:
// TS1208: 'match-media.ts' cannot be compiled under '--isolatedModules' because it is considered a global script file. Add an import, export, or an empty 'export {}' statement to make it a module.
export {};
score:0
If the component you are testing includes window.matchMedia()
or imports another component (ie. a CSS media query hook uses useMedia() ) and you don't aim to test anything related to it, you can bypass calling the method by adding a window check to your component.
In the example code below, useMedia hook will always return false if the code runned by Jest.
There is a post about an argument against mocking module imports., https://dev.to/jackmellis/don-t-mock-modules-4jof
import { useLayoutEffect, useState } from 'react';
export function useMedia(query): boolean {
const [state, setState] = useState(false);
useLayoutEffect(() => {
// ******* WINDOW CHECK START *******
if (!window || !window.matchMedia) {
return;
}
// ******* WINDOW CHECK END *******
let mounted = true;
const mql = window.matchMedia(query);
const onChange = () => {
if (!mounted) return;
setState(!!mql.matches);
};
mql.addEventListener('change', onChange);
setState(mql.matches);
return () => {
mounted = false;
mql.removeEventListener('change', onChange);
};
}, [query]);
return state;
}
But if you want to access the object returned from the method, you can mock it in the component itself, instead of testing files. see a sample usage: (source link)
import {useState, useEffect, useLayoutEffect} from 'react';
import {queryObjectToString, noop} from './utilities';
import {Effect, MediaQueryObject} from './types';
// ************** MOCK START **************
export const mockMediaQueryList: MediaQueryList = {
media: '',
matches: false,
onchange: noop,
addListener: noop,
removeListener: noop,
addEventListener: noop,
removeEventListener: noop,
dispatchEvent: (_: Event) => true,
};
// ************** MOCK END **************
const createUseMedia = (effect: Effect) => (
rawQuery: string | MediaQueryObject,
defaultState = false,
) => {
const [state, setState] = useState(defaultState);
const query = queryObjectToString(rawQuery);
effect(() => {
let mounted = true;
************** WINDOW CHECK START **************
const mediaQueryList: MediaQueryList =
typeof window === 'undefined'
? mockMediaQueryList
: window.matchMedia(query);
************** WINDOW CHECK END **************
const onChange = () => {
if (!mounted) {
return;
}
setState(Boolean(mediaQueryList.matches));
};
mediaQueryList.addListener(onChange);
setState(mediaQueryList.matches);
return () => {
mounted = false;
mediaQueryList.removeListener(onChange);
};
}, [query]);
return state;
};
export const useMedia = createUseMedia(useEffect);
export const useMediaLayout = createUseMedia(useLayoutEffect);
export default useMedia;
score:1
These guys have a pretty slick solution via the Jest setupFiles:
https://github.com/HospitalRun/components/pull/117/commits/210d1b74e4c8c14e1ffd527042e3378bba064ed8
score:1
I developed a library specially designed for that: https://www.npmjs.com/package/mock-match-media
It proposes a complete implementation of matchMedia
for node.
And it even has a jest-setup
file you can import in your jest settings to apply this mock to all of your tests (see https://www.npmjs.com/package/mock-match-media#jest):
require('mock-match-media/jest-setup);
score:3
I tried all the above previous answers without any success.
Adding matchMedia.js to the mocks folder, did it for me.
I filled it up with techguy2000's content:
// __mocks__/matchMedia.js
'use strict';
Object.defineProperty(window, 'matchMedia', {
value: () => ({
matches: false,
addListener: () => {},
removeListener: () => {}
})
});
Object.defineProperty(window, 'getComputedStyle', {
value: () => ({
getPropertyValue: () => {}
})
});
module.exports = window;
And then imported this in setup.js
:
import matchMedia from '../__mocks__/matchMedia';
Boom! :)
score:3
You can also test if the type of window.matchMedia
is a function before using it
Example :
if (typeof window.matchMedia === 'function') {
// Do something with window.matchMedia
}
And the tests won't fails anymore
score:4
TL;DR answer further down below
In my case, the answer was not enough, as window.matchMedia
would always return false
(or true
if you change it). I had some React hooks and components that needed to listen to multiple different queries with possibly different matches
.
What I tried
If you only need to test one query at a time and your tests don't rely on multiple matches, jest-matchmedia-mock
was useful. However, from what I've understood after trying to use it for 3 hours was that when you call useMediaQuery
, the previous queries you've made no longer work. In fact, the query you pass into useMediaQuery
will just match true
whenever your code calls window.matchMedia
with that same query, regardless of the actual "window width".
Answer
After realizing I couldn't actually test my queries with jest-matchmedia-mock
, I changed the original answer a bit to be able to mock the behavior of dynamic query matches
. This solution requires the css-mediaquery
npm package.
import mediaQuery from "css-mediaquery";
// Mock window.matchMedia's impl.
Object.defineProperty(window, "matchMedia", {
writable: true,
value: jest.fn().mockImplementation((query) => {
const instance = {
matches: mediaQuery.match(query, {
width: window.innerWidth,
height: window.innerHeight,
}),
media: query,
onchange: null,
addListener: jest.fn(), // Deprecated
removeListener: jest.fn(), // Deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
};
// Listen to resize events from window.resizeTo and update the instance's match
window.addEventListener("resize", () => {
const change = mediaQuery.match(query, {
width: window.innerWidth,
height: window.innerHeight,
});
if (change != instance.matches) {
instance.matches = change;
instance.dispatchEvent("change");
}
});
return instance;
}),
});
// Mock window.resizeTo's impl.
Object.defineProperty(window, "resizeTo", {
value: (width: number, height: number) => {
Object.defineProperty(window, "innerWidth", {
configurable: true,
writable: true,
value: width,
});
Object.defineProperty(window, "outerWidth", {
configurable: true,
writable: true,
value: width,
});
Object.defineProperty(window, "innerHeight", {
configurable: true,
writable: true,
value: height,
});
Object.defineProperty(window, "outerHeight", {
configurable: true,
writable: true,
value: height,
});
window.dispatchEvent(new Event("resize"));
},
});
It uses css-mediaquery
with the window.innerWidth
to determine if the query ACTUALLY matches instead of a hard-coded boolean. It also listens to resize events fired by the window.resizeTo
mocked implementation to update the matches
value.
You may now use window.resizeTo
in your tests to change the window's width so your calls to window.matchMedia
reflect this width. Here's an example, which was made just for this question, so ignore the performance issues it has!
const bp = { xs: 200, sm: 620, md: 980, lg: 1280, xl: 1920 };
// Component.tsx
const Component = () => {
const isXs = window.matchMedia(`(min-width: ${bp.xs}px)`).matches;
const isSm = window.matchMedia(`(min-width: ${bp.sm}px)`).matches;
const isMd = window.matchMedia(`(min-width: ${bp.md}px)`).matches;
const isLg = window.matchMedia(`(min-width: ${bp.lg}px)`).matches;
const isXl = window.matchMedia(`(min-width: ${bp.xl}px)`).matches;
console.log("matches", { isXs, isSm, isMd, isLg, isXl });
const width =
(isXl && "1000px") ||
(isLg && "800px") ||
(isMd && "600px") ||
(isSm && "500px") ||
(isXs && "300px") ||
"100px";
return <div style={{ width }} />;
};
// Component.test.tsx
it("should use the md width value", () => {
window.resizeTo(bp.md, 1000);
const wrapper = mount(<Component />);
const div = wrapper.find("div").first();
// console.log: matches { isXs: true, isSm: true, isMd: true, isLg: false, isXl: false }
expect(div.prop("style")).toHaveProperty("width", "600px");
});
Note: I have not tested the behavior of this when resizing the window AFTER mounting the component
score:4
The official workaround worked for me until I decided to update react-scripts
from 3.4.1 to 4.0.3 (as I use create-react-app). Then I started getting an error Cannot read property 'matches' of undefined
.
So here's workaround I found. Install mq-polyfill as dev dependency.
Then code this in src/setupTests.js
:
import matchMediaPolyfill from 'mq-polyfill'
matchMediaPolyfill(window)
// implementation of window.resizeTo for dispatching event
window.resizeTo = function resizeTo(width, height) {
Object.assign(this, {
innerWidth: width,
innerHeight: height,
outerWidth: width,
outerHeight: height
}).dispatchEvent(new this.Event('resize'))
}
This worked for me.
score:6
Add following lines to your setupTest.js
file,
global.matchMedia = global.matchMedia || function() {
return {
matches : false,
addListener : function() {},
removeListener: function() {}
}
}
This would add match media query for your all test cases.
score:8
You can mock the API:
describe("Test", () => {
beforeAll(() => {
Object.defineProperty(window, "matchMedia", {
value: jest.fn(() => {
return {
matches: true,
addListener: jest.fn(),
removeListener: jest.fn()
};
})
});
});
});
score:10
You can use the jest-matchmedia-mock
package for testing any media queries (like device screen change, color-scheme change, etc.)
score:11
I have just encountered this issue and had to mock these in jestGlobalMocks.ts:
Object.defineProperty(window, 'matchMedia', {
value: () => {
return {
matches: false,
addListener: () => {},
removeListener: () => {}
};
}
});
Object.defineProperty(window, 'getComputedStyle', {
value: () => {
return {
getPropertyValue: () => {}
};
}
});
score:14
JESTS OFFICIAL WORKAROUND
is to create a mock file, called matchMedia.js
and add the following code:
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation((query) => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // Deprecated
removeListener: jest.fn(), // Deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
Then, inside your test file, import your mock import './matchMedia';
and as long as you import it in every use case, it should solve your problem.
ALTERNATIVE OPTION
I kept running into this issue and found myself just making too many imports, thought I would offer an alternative solution.
which is to create a setup/before.js
file, with the following contents:
import 'regenerator-runtime';
/** Add any global mocks needed for the test suite here */
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation((query) => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // Deprecated
removeListener: jest.fn(), // Deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
And then inside your jest.config file, add the following:
setupFiles: ['<rootDir>/
ROUTE TO YOUR BEFORE.JS FILE'],
score:16
Jest uses jsdom to create a browser environment. JSDom doesn't however support window.matchMedia
so you will have to create it yourself.
Jest's manual mocks work with module boundaries, i.e. require / import statements so they wouldn't be appropriate to mock window.matchMedia
as is because it's a global.
You therefore have two options:
Define your own local matchMedia module which exports window.matchMedia. -- This would allow you to then define a manual mock to use in your test.
Define a setup file which adds a mock for matchMedia to the global window.
With either of these options you could use a matchMedia polyfill as a mock which would at least allow your tests to run or if you needed to simulate different states you might want to write your own with private methods allowing you to configure it's behaviour similar to the Jest fs
manual mock
score:35
I put a matchMedia stub in my Jest test file (above the tests), which allows the tests to pass:
window.matchMedia = window.matchMedia || function() {
return {
matches: false,
addListener: function() {},
removeListener: function() {}
};
};
score:54
I've been using this technique to solve a bunch of mocking problems.
describe("Test", () => {
beforeAll(() => {
Object.defineProperty(window, "matchMedia", {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // Deprecated
removeListener: jest.fn(), // Deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
}))
});
});
});
Or, if you want to mock it all the time, you could put inside your mocks
file called from your package.json
:
"setupFilesAfterEnv": "<rootDir>/src/tests/mocks.js",
.
Reference: setupTestFrameworkScriptFile
Source: stackoverflow.com
Related Query
- Jest test fails : TypeError: window.matchMedia is not a function
- JEST test suite fails with TypeError: environment.setup is not a function
- In Jest test it says withRouter () is not a function
- Jest - assert async function throws test fails
- ReferenceError: window is not defined. I got this error when I run npm test to unit testing by jest
- Function call inside useEffect not firing in jest test
- JEST Test suite failed to run: TypeError: (0 , _reactRedux.connect) is not a function
- history.createBrowserHistory is not a function in Jest test using React
- Why does the jest mock function not getting executed in my test with react testing library?
- Mock function not being called in Jest + Enzyme test
- TypeError jQuery timepicker is not a function on test
- integration test for axios fetching, and it fails when I apply the `done` as callback in the `it` function with jest
- TypeError: dispatch is not a function when I try to test a method using JEST
- React Jest Test Failing - type.toUpperCase is not a function
- includes() and toLowerCase throws not a function error from jest unit test
- Jest test error: ...subscribe is not a function
- Jest test fails when using window object environment variable?
- Jest test fails due to mock function despite its execution
- react-router-bootstrap LinkContainer component fails with a TypeError (0 , _reactRouterDom.withRouter) is not a function
- TypeError: routingState.equals is not a function | Jest test case
- "foo" is not a function using Jest to test a component method
- Function works but fails in Jest test
- asynchronous function test onChange event fails in enzyme and jest
- `regeneratorRuntime` is not defined when running Jest test
- Jest URL.createObjectURL is not a function
- TypeError dispatcher.useState is not a function when using React Hooks
- Test a React Component function with Jest
- Jest Expected mock function to have been called, but it was not called
- How to fix TypeError is not a function (testing promises with Jest)
- Jest Test Babel Error: Plugin/Preset files are not allowed to export objects
More Query from same tag
- react-redux TypeError: notes.map is not a function
- useReducer: dispatch action, show state in other component and update state when action is dispatched
- Redux Form: MapStateToProps is breaking form
- How to resolve "Uncaught TypeError: Failed to construct 'Comment': Please use the 'new' operat....." with respect to React JS?
- Confused about React's Flux architecture - waitFor
- React Typescript property does not exist on type during array map
- React + JS file, Uncaught Error: Minified exception, using Atom + Chrome console
- Can't resolve 'crypto' in react-oauth2-pkce
- Create a Wrapper ReactJS
- How to pass a function instead of string as a prop to a component
- Why is array not updating after sorting in React Hooks?
- Why Are My CSS Files Applied To Every React Componnent?
- React router v6 routes with Fallback
- Using ThemeProvider props in Global Styled-Components
- Material UI component breaking React application
- handleSubmit functional is not working in functional component of React
- Unexpected token when trying to iterate over Object
- REACT: Rendering a "please fill out this field" warning On Hover
- How to apply css inside the values of Label
- React Jest how to test if a span contains some certain text from mount snapshot?
- react typescript: use "Object.freeze" (enum) as type
- How to pass props from functional component to class component
- I would like to have my code push to my all songs page after submitting the form
- How is my React app able to use my Node REST API when its not accessible from the outside?
- How to change heart ( wishlist icon ) color when clicked in ReactJS?
- Implementing authentication and authorization with React hooks, .NET Core Web API, and SQL Server
- React state is always one step behind while making predictions on uploaded image using Tensorflowjs
- ReactJS Pagination React Hooks
- React - Objects are not valid as a React child?
- How prevent fetch request before auth token is received in React Js?