UNPKG

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