UNPKG

8.44 kBMarkdownView Raw
1# `@shopify/jest-dom-mocks`
2
3[![Build Status](https://github.com/Shopify/quilt/workflows/Node-CI/badge.svg?branch=main)](https://github.com/Shopify/quilt/actions?query=workflow%3ANode-CI)
4[![Build Status](https://github.com/Shopify/quilt/workflows/Ruby-CI/badge.svg?branch=main)](https://github.com/Shopify/quilt/actions?query=workflow%3ARuby-CI)
5[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE.md) [![npm version](https://badge.fury.io/js/%40shopify%2Fjest-dom-mocks.svg)](https://badge.fury.io/js/%40shopify%2Fjest-dom-mocks)
6
7Jest mocking utilities for working with the DOM.
8
9## Installation
10
11```bash
12$ yarn add @shopify/jest-dom-mocks
13```
14
15## Setup
16
17This package provides two methods that should be included in the jest setup files:
18
19- `ensureMocksReset`
20- `installMockStorage`
21
22### `ensureMocksReset`
23
24Should be called in the `afterEach` method of the jest `each-test` setup file. For example:
25
26```ts
27import {ensureMocksReset} from '@shopify/jest-dom-mocks';
28
29afterEach(() => {
30 ensureMocksReset();
31});
32```
33
34this will ensure that appropriate error messages are shown if a DOM object is mocked without beign restored for the next test.
35
36### `installMockStorage`
37
38Should be called in the jest `setup` file. For example:
39
40```ts
41import {installMockStorage} from '@shopify/jest-dom-mocks';
42
43installMockStorage();
44```
45
46this will install the `localStorage` and `sessionStorage` mocks onto the global `window` object.
47
48## Example Usage
49
50In 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.
51
52```ts
53import {clock, animationFrame} from '@shopify/jest-dom-mocks';
54
55it('transitions to the next number after being updated', () => {
56 clock.mock();
57 animationFrame.mock();
58
59 const duration = 1000;
60 const rendered = mount(
61 <NumberTransitioner duration={duration}>{100}</NumberTransitioner>,
62 );
63 rendered.setProps({children: 200});
64
65 clock.tick(duration / 4);
66 animationFrame.runFrame();
67 expect(rendered.text()).toBe('125');
68
69 clock.tick(duration / 2);
70 animationFrame.runFrame();
71 expect(rendered.text()).toBe('175');
72
73 clock.restore();
74 animationFrame.restore();
75});
76```
77
78## API Reference
79
80The mocks provided can be divided into 3 primary categories:
81
82- standard mocks
83- fetch mock
84- storage mocks
85
86### Standard Mocks
87
88The following standard mocks are available:
89
90- `animationFrame`
91- `requestIdleCallback`
92- `clock`
93- `location`
94- `matchMedia`
95- `timer`
96- `promise`
97- `intersectionObserver`
98- `dimension`
99- `connection`
100
101Each 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()`.
102
103For example:
104
105```ts
106import {location} from '@shopify/jest-dom-mocks';
107
108beforeEach(() => {
109 location.mock();
110});
111
112afterEach(() => {
113 location.restore();
114});
115
116it('does a thing', () => {
117 // run test code here
118});
119```
120
121Or, if you just need to mock something for a single test:
122
123```ts
124import {location} from '@shopify/jest-dom-mocks';
125
126it('does a thing', () => {
127 location.mock();
128
129 // run test code here
130
131 location.restore();
132});
133```
134
135Some of the standard mocks include additional features:
136
137#### `AnimationFrame.runFrame(): void`
138
139Executes all queued animation callbacks.
140
141#### `RequestIdleCallback.mockAsUnsupported(): void`
142
143Removes `window.requestIdleCallback` and `window.cancelIdleCallback`, which can be useful for testing features that should work with and without idle callbacks available.
144
145#### `RequestIdleCallback.runIdleCallbacks(timeRemaining?: number, didTimeout?: boolean): void`
146
147Runs 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.
148
149#### `RequestIdleCallback.cancelIdleCallbacks(): void`
150
151Cancels all currently-scheduled idle callbacks.
152
153#### `RequestIdleCallback.cancelIdleCallback(callback: any): void`
154
155Cancels the idle callback specified by the passed argument. This value should be the one returned from a call to `window.requestIdleCallback`.
156
157#### `Clock.mock(now: number | Date): void`
158
159In 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.
160
161#### `Clock.tick(time: number): void`
162
163Ticks the mocked `Clock` ahead by `time` milliseconds.
164
165#### `Clock.setTime(time: number): void`
166
167Sets the system time to the given `time`.
168
169#### `MatchMedia.mock(media?: MediaMatching): void`
170
171In 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.
172
173The `MediaMatching` function has the following interface:
174
175```ts
176interface MediaMatching {
177 (mediaQuery: string): Partial<MediaQueryList>;
178}
179```
180
181it 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:
182
183```ts
184{
185 media: '',
186 addListener: noop,
187 removeListener: noop,
188 matches: false
189}
190```
191
192#### `MatchMedia.setMedia(media?: MediaMatching): void`
193
194Sets the implementation function for the mocked `MatchMedia` object. see above (`MatchMedia.mock(media?: MediaMatching): void`) for details on how `MediaMatching` works.
195
196You can also call `setMedia` with no arguments to restore the default implementation.
197
198#### `Timer.runAllTimers(): void`
199
200Runs all system timers to completion.
201
202#### `Timer.runTimersToTime(time: number): void`
203
204Runs all system timers to the given `time`.
205
206#### `Promise.runPending(): void`
207
208Runs all promise resolvers that have been queued.
209
210#### `IntersectionObserver.observers`
211
212Returns 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).
213
214#### `IntersectionObserver.simulate(entry: Partial<IntersectionObserverEntry> | Partial<IntersectionObserverEntry>[]): void`
215
216Simulates 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.
217
218### Fetch Mock
219
220We 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`](http://www.wheresrhys.co.uk/fetch-mock) for more details.
221
222### Storage mock
223
224The 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:
225
226- `getItem`
227- `setItem`
228- `removeItem`
229- `clear`
230
231Each of these are wrapped in a jest spy, which is automatically restored at the end of the test run.
232
233### Dimension mocks
234
235The dimension mocks allow mocking the following DOM properties:
236
237- `scrollWidth`
238- `scrollHeight`
239- `offsetWidth`
240- `offsetHeight`
241- `innerWidth`
242
243Pass the dimension you want to mock and the value you want returned for all calls when calling `mock`:
244
245```ts
246import {dimension} from '@shopify/jest-dom-mocks';
247
248beforeEach(() => {
249 dimension.mock({
250 scrollWidth: 100,
251 offsetHeight: 200,
252 });
253});
254
255afterEach(() => dimension.restore());
256```
257
258You can also pass in a function as a mock that returns a number. The element is passed as the only argument to the function:
259
260```tsx
261beforeEach(() => {
262 dimension.mock({
263 scrollWidth(element: HTMLElement) {
264 return element.id === 'test-id' ? 200 : 0;
265 },
266 });
267});
268
269afterEach(() => dimension.restore());
270
271describe('DOM tests', () => {
272 it('returns the element width', () => {
273 function Component() {
274 return <div id="some-id" />;
275 }
276 const element = mount(<Component />);
277 const elementWidth =
278 element.domNode == null ? undefined : element.domNode.scrollWidth;
279
280 expect(elementWidth).toBe(200);
281 });
282});
283```