Jest mocking utilities for working with the DOM.
yarn add @shopify/jest-dom-mocks
This package provides two methods that should be included in the jest setup files:
ensureMocksReset
installMockStorage
Should be called in the afterEach
method of the jest each-test
setup file. For example:
import {ensureMocksReset} from '@shopify/jest-dom-mocks';
afterEach(() => {
ensureMocksReset();
});
this will ensure that appropriate error messages are shown if a DOM object is mocked without being restored for the next test.
Should be called in the jest setup
file. For example:
import {installMockStorage} from '@shopify/jest-dom-mocks';
installMockStorage();
this will install the localStorage
and sessionStorage
mocks onto the global window
object.
In this example, we are testing a NumberTransitioner
component using Jest
and Enzyme
. Note that parts of this file have been omitted in order to focus in on the relevant parts of the example.
import {clock, animationFrame} from '@shopify/jest-dom-mocks';
it('transitions to the next number after being updated', () => {
clock.mock();
animationFrame.mock();
const duration = 1000;
const rendered = mount(
<NumberTransitioner duration={duration}>{100}</NumberTransitioner>,
);
rendered.setProps({children: 200});
clock.tick(duration / 4);
animationFrame.runFrame();
expect(rendered.text()).toBe('125');
clock.tick(duration / 2);
animationFrame.runFrame();
expect(rendered.text()).toBe('175');
clock.restore();
animationFrame.restore();
});
The mocks provided can be divided into 3 primary categories:
- standard mocks
- fetch mock
- storage mocks
The following standard mocks are available:
animationFrame
requestIdleCallback
clock
(Deprecated, usejest.useFakeTimers()
instead)location
matchMedia
timer
(Deprecated, usejest.useFakeTimers()
instead)promise
intersectionObserver
dimension
connection
Each of the standard mocks can be installed, for a given test, using standardMock.mock()
, and must be restored before the end of the test using standardMock.restore()
.
For example:
import {location} from '@shopify/jest-dom-mocks';
beforeEach(() => {
location.mock();
});
afterEach(() => {
location.restore();
});
it('does a thing', () => {
// run test code here
});
Or, if you just need to mock something for a single test:
import {location} from '@shopify/jest-dom-mocks';
it('does a thing', () => {
location.mock();
// run test code here
location.restore();
});
Some of the standard mocks include additional features:
Executes all queued animation callbacks.
Removes window.requestIdleCallback
and window.cancelIdleCallback
, which can be useful for testing features that should work with and without idle callbacks available.
Runs all currently-scheduled idle callbacks. If provided, timeRemaining
/ didTimeout
will be used to construct the argument for these callbacks. Once called, all callbacks are removed from the queue.
Cancels all currently-scheduled idle callbacks.
Cancels the idle callback specified by the passed argument. This value should be the one returned from a call to window.requestIdleCallback
.
In addition to the usual .mock()
functionality (with no arguments), the Clock
object can be mock
ed by passing in a number
or Date
object to use as the current system time. Deprecated - use jest.useFakeTimers({now})
instead.
Ticks the mocked Clock
ahead by time
milliseconds. Deprecated - use jest.advanceTimersByTime()
instead.
Sets the system time to the given time
. Deprecated - use jest.setSystemTime()
instead.
In addition to the usual .mock()
functionality (with no arguments), the MatchMedia
object can be mock
ed by passing in a MediaMatching
function to use as the implementation.
The MediaMatching
function has the following interface:
interface MediaMatching {
(mediaQuery: string): Partial<MediaQueryList>;
}
it takes a mediaQuery
string as input and returns a partial MediaQueryList
to use as the result of window.matchMedia(mediaQuery)
. The partial result will be merged with the default values:
{
media: '',
addListener: noop,
removeListener: noop,
matches: false
}
Sets the implementation function for the mocked MatchMedia
object. see above (MatchMedia.mock(media?: MediaMatching): void
) for details on how MediaMatching
works.
You can also call setMedia
with no arguments to restore the default implementation.
Runs all system timers to completion. Deprecated - use jest.runAllTimers()
instead.
Runs all system timers to the given time
. Deprecated - use jest.advanceTimersByTime()
instead.
Runs all promise resolvers that have been queued.
Returns an array of records representing elements currently being observed with an IntersectionObserver
. Each record contains a target
(the element being observed), callback
(the function used when constructing the observer), options
(optional object used when constructing the observer), and a source
(the fake IntersectionObserver
instance that was used to observe).
IntersectionObserver.simulate(entry: Partial<IntersectionObserverEntry> | Partial<IntersectionObserverEntry>[]): void
Simulates a call on all matching observers. If you pass a target
on the passed entry/ entries, only observers with a matching target
element will be triggered. Otherwise, all observers will be triggered. If you do not provide a full IntersectionObserverEntry
in any case, the missing fields will be filled out with sensible defaults.
We use a version of fetch-mock
that is augmented to ensure that it is properly unmocked after each test run. See the API of fetch-mock
for more details.
The storage mocks are a bit different than the other mocks, because they serve primarily as a polyfill for the localStorage
and sessionStorage
APIs. The following standard API methods are implemented:
getItem
setItem
removeItem
clear
length
key
Each of these are wrapped in a jest spy, which is automatically restored at the end of the test run.
The dimension mocks allow mocking the following DOM properties:
scrollWidth
scrollHeight
offsetWidth
offsetHeight
innerWidth
Pass the dimension you want to mock and the value you want returned for all calls when calling mock
:
import {dimension} from '@shopify/jest-dom-mocks';
beforeEach(() => {
dimension.mock({
scrollWidth: 100,
offsetHeight: 200,
});
});
afterEach(() => dimension.restore());
You can also pass in a function as a mock that returns a number. The element is passed as the only argument to the function:
beforeEach(() => {
dimension.mock({
scrollWidth(element: HTMLElement) {
return element.id === 'test-id' ? 200 : 0;
},
});
});
afterEach(() => dimension.restore());
describe('DOM tests', () => {
it('returns the element width', () => {
function Component() {
return <div id="some-id" />;
}
const element = mount(<Component />);
const elementWidth =
element.domNode == null ? undefined : element.domNode.scrollWidth;
expect(elementWidth).toBe(200);
});
});