|
1 | | -import React from 'react' |
2 | | -import ReactDOM from 'react-dom' |
3 | | -import { |
4 | | - getQueriesForElement, |
5 | | - prettyDOM, |
6 | | - fireEvent as dtlFireEvent, |
7 | | - configure as configureDTL, |
8 | | -} from '@testing-library/dom' |
9 | | -import act, {asyncAct} from './act-compat' |
10 | | - |
11 | | -configureDTL({ |
12 | | - asyncWrapper: async cb => { |
13 | | - let result |
14 | | - await asyncAct(async () => { |
15 | | - result = await cb() |
16 | | - }) |
17 | | - return result |
18 | | - }, |
19 | | -}) |
20 | | - |
21 | | -const mountedContainers = new Set() |
22 | | - |
23 | | -function render( |
24 | | - ui, |
25 | | - { |
26 | | - container, |
27 | | - baseElement = container, |
28 | | - queries, |
29 | | - hydrate = false, |
30 | | - wrapper: WrapperComponent, |
31 | | - } = {}, |
32 | | -) { |
33 | | - if (!baseElement) { |
34 | | - // default to document.body instead of documentElement to avoid output of potentially-large |
35 | | - // head elements (such as JSS style blocks) in debug output |
36 | | - baseElement = document.body |
37 | | - } |
38 | | - if (!container) { |
39 | | - container = baseElement.appendChild(document.createElement('div')) |
40 | | - } |
41 | | - |
42 | | - // we'll add it to the mounted containers regardless of whether it's actually |
43 | | - // added to document.body so the cleanup method works regardless of whether |
44 | | - // they're passing us a custom container or not. |
45 | | - mountedContainers.add(container) |
46 | | - |
47 | | - const wrapUiIfNeeded = innerElement => |
48 | | - WrapperComponent |
49 | | - ? React.createElement(WrapperComponent, null, innerElement) |
50 | | - : innerElement |
51 | | - |
52 | | - act(() => { |
53 | | - if (hydrate) { |
54 | | - ReactDOM.hydrate(wrapUiIfNeeded(ui), container) |
55 | | - } else { |
56 | | - ReactDOM.render(wrapUiIfNeeded(ui), container) |
57 | | - } |
58 | | - }) |
59 | | - |
60 | | - return { |
61 | | - container, |
62 | | - baseElement, |
63 | | - // eslint-disable-next-line no-console |
64 | | - debug: (el = baseElement) => console.log(prettyDOM(el)), |
65 | | - unmount: () => ReactDOM.unmountComponentAtNode(container), |
66 | | - rerender: rerenderUi => { |
67 | | - render(wrapUiIfNeeded(rerenderUi), {container, baseElement}) |
68 | | - // Intentionally do not return anything to avoid unnecessarily complicating the API. |
69 | | - // folks can use all the same utilities we return in the first place that are bound to the container |
70 | | - }, |
71 | | - asFragment: () => { |
72 | | - /* istanbul ignore if (jsdom limitation) */ |
73 | | - if (typeof document.createRange === 'function') { |
74 | | - return document |
75 | | - .createRange() |
76 | | - .createContextualFragment(container.innerHTML) |
77 | | - } |
78 | | - |
79 | | - const template = document.createElement('template') |
80 | | - template.innerHTML = container.innerHTML |
81 | | - return template.content |
82 | | - }, |
83 | | - ...getQueriesForElement(baseElement, queries), |
84 | | - } |
85 | | -} |
86 | | - |
87 | | -function cleanup() { |
88 | | - mountedContainers.forEach(cleanupAtContainer) |
89 | | -} |
90 | | - |
91 | | -// maybe one day we'll expose this (perhaps even as a utility returned by render). |
92 | | -// but let's wait until someone asks for it. |
93 | | -function cleanupAtContainer(container) { |
94 | | - ReactDOM.unmountComponentAtNode(container) |
95 | | - if (container.parentNode === document.body) { |
96 | | - document.body.removeChild(container) |
97 | | - } |
98 | | - mountedContainers.delete(container) |
99 | | -} |
100 | | - |
101 | | -// react-testing-library's version of fireEvent will call |
102 | | -// dom-testing-library's version of fireEvent wrapped inside |
103 | | -// an "act" call so that after all event callbacks have been |
104 | | -// been called, the resulting useEffect callbacks will also |
105 | | -// be called. |
106 | | -function fireEvent(...args) { |
107 | | - let returnValue |
108 | | - act(() => { |
109 | | - returnValue = dtlFireEvent(...args) |
110 | | - }) |
111 | | - return returnValue |
112 | | -} |
113 | | - |
114 | | -Object.keys(dtlFireEvent).forEach(key => { |
115 | | - fireEvent[key] = (...args) => { |
116 | | - let returnValue |
117 | | - act(() => { |
118 | | - returnValue = dtlFireEvent[key](...args) |
119 | | - }) |
120 | | - return returnValue |
121 | | - } |
122 | | -}) |
123 | | - |
124 | | -// React event system tracks native mouseOver/mouseOut events for |
125 | | -// running onMouseEnter/onMouseLeave handlers |
126 | | -// @link https://github.com/facebook/react/blob/b87aabdfe1b7461e7331abb3601d9e6bb27544bc/packages/react-dom/src/events/EnterLeaveEventPlugin.js#L24-L31 |
127 | | -fireEvent.mouseEnter = fireEvent.mouseOver |
128 | | -fireEvent.mouseLeave = fireEvent.mouseOut |
129 | | - |
130 | | -fireEvent.select = (node, init) => { |
131 | | - // React tracks this event only on focused inputs |
132 | | - node.focus() |
133 | | - |
134 | | - // React creates this event when one of the following native events happens |
135 | | - // - contextMenu |
136 | | - // - mouseUp |
137 | | - // - dragEnd |
138 | | - // - keyUp |
139 | | - // - keyDown |
140 | | - // so we can use any here |
141 | | - // @link https://github.com/facebook/react/blob/b87aabdfe1b7461e7331abb3601d9e6bb27544bc/packages/react-dom/src/events/SelectEventPlugin.js#L203-L224 |
142 | | - fireEvent.keyUp(node, init) |
143 | | -} |
| 1 | +import {asyncAct} from './act-compat' |
| 2 | +import {cleanup} from './pure' |
144 | 3 |
|
145 | 4 | // if we're running in a test runner that supports afterEach |
146 | 5 | // then we'll automatically run cleanup afterEach test |
147 | 6 | // this ensures that tests run in isolation from each other |
148 | | -if (typeof afterEach === 'function' && !process.env.RTL_SKIP_CLEANUP) { |
| 7 | +// if you don't like this then either import the `pure` module |
| 8 | +// or set the RTL_SKIP_AUTO_CLEANUP env variable to 'true'. |
| 9 | +if (typeof afterEach === 'function' && !process.env.RTL_SKIP_AUTO_CLEANUP) { |
149 | 10 | afterEach(async () => { |
150 | 11 | await asyncAct(async () => {}) |
151 | 12 | cleanup() |
152 | 13 | }) |
153 | 14 | } |
154 | 15 |
|
155 | | -// just re-export everything from dom-testing-library |
156 | | -export * from '@testing-library/dom' |
157 | | -export {render, cleanup, fireEvent, act} |
158 | | - |
159 | | -// NOTE: we're not going to export asyncAct because that's our own compatibility |
160 | | -// thing for people using react-dom@16.8.0. Anyone else doesn't need it and |
161 | | -// people should just upgrade anyway. |
162 | | - |
163 | | -/* eslint func-name-matching:0 */ |
| 16 | +export * from './pure' |
0 commit comments