UNPKG

9.93 kBJavaScriptView Raw
1/**
2 * @license React
3 * jest-react.development.js
4 *
5 * Copyright (c) Facebook, Inc. and its affiliates.
6 *
7 * This source code is licensed under the MIT license found in the
8 * LICENSE file in the root directory of this source tree.
9 */
10
11'use strict';
12
13if (process.env.NODE_ENV !== "production") {
14 (function() {
15'use strict';
16
17var React = require('react');
18var Scheduler = require('scheduler/unstable_mock');
19
20var assign = Object.assign;
21
22// ATTENTION
23// When adding new symbols to this file,
24// Please consider also adding to 'react-devtools-shared/src/backend/ReactSymbols'
25// The Symbol used to tag the ReactElement-like types.
26var REACT_ELEMENT_TYPE = Symbol.for('react.element');
27var REACT_FRAGMENT_TYPE = Symbol.for('react.fragment');
28
29var isArrayImpl = Array.isArray; // eslint-disable-next-line no-redeclare
30
31function isArray(a) {
32 return isArrayImpl(a);
33}
34
35var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
36
37function error(format) {
38 {
39 {
40 for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
41 args[_key2 - 1] = arguments[_key2];
42 }
43
44 printWarning('error', format, args);
45 }
46 }
47}
48
49function printWarning(level, format, args) {
50 // When changing this logic, you might want to also
51 // update consoleWithStackDev.www.js as well.
52 {
53 var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
54 var stack = ReactDebugCurrentFrame.getStackAddendum();
55
56 if (stack !== '') {
57 format += '%s';
58 args = args.concat([stack]);
59 } // eslint-disable-next-line react-internal/safe-string-coercion
60
61
62 var argsWithFormat = args.map(function (item) {
63 return String(item);
64 }); // Careful: RN currently depends on this prefix
65
66 argsWithFormat.unshift('Warning: ' + format); // We intentionally don't use spread (or .apply) directly because it
67 // breaks IE9: https://github.com/facebook/react/issues/13610
68 // eslint-disable-next-line react-internal/no-production-logging
69
70 Function.prototype.apply.call(console[level], console, argsWithFormat);
71 }
72}
73
74var didWarnAboutMessageChannel = false;
75var enqueueTaskImpl = null;
76function enqueueTask(task) {
77 if (enqueueTaskImpl === null) {
78 try {
79 // read require off the module object to get around the bundlers.
80 // we don't want them to detect a require and bundle a Node polyfill.
81 var requireString = ('require' + Math.random()).slice(0, 7);
82 var nodeRequire = module && module[requireString]; // assuming we're in node, let's try to get node's
83 // version of setImmediate, bypassing fake timers if any.
84
85 enqueueTaskImpl = nodeRequire.call(module, 'timers').setImmediate;
86 } catch (_err) {
87 // we're in a browser
88 // we can't use regular timers because they may still be faked
89 // so we try MessageChannel+postMessage instead
90 enqueueTaskImpl = function (callback) {
91 {
92 if (didWarnAboutMessageChannel === false) {
93 didWarnAboutMessageChannel = true;
94
95 if (typeof MessageChannel === 'undefined') {
96 error('This browser does not have a MessageChannel implementation, ' + 'so enqueuing tasks via await act(async () => ...) will fail. ' + 'Please file an issue at https://github.com/facebook/react/issues ' + 'if you encounter this warning.');
97 }
98 }
99 }
100
101 var channel = new MessageChannel();
102 channel.port1.onmessage = callback;
103 channel.port2.postMessage(undefined);
104 };
105 }
106 }
107
108 return enqueueTaskImpl(task);
109}
110
111var actingUpdatesScopeDepth = 0;
112function act(scope) {
113 if (Scheduler.unstable_flushAllWithoutAsserting === undefined) {
114 throw Error('This version of `act` requires a special mock build of Scheduler.');
115 }
116
117 if (setTimeout._isMockFunction !== true) {
118 throw Error("This version of `act` requires Jest's timer mocks " + '(i.e. jest.useFakeTimers).');
119 }
120
121 var previousIsActEnvironment = global.IS_REACT_ACT_ENVIRONMENT;
122 var previousActingUpdatesScopeDepth = actingUpdatesScopeDepth;
123 actingUpdatesScopeDepth++;
124
125 if ( actingUpdatesScopeDepth === 1) {
126 // Because this is not the "real" `act`, we set this to `false` so React
127 // knows not to fire `act` warnings.
128 global.IS_REACT_ACT_ENVIRONMENT = false;
129 }
130
131 var unwind = function () {
132 if ( actingUpdatesScopeDepth === 1) {
133 global.IS_REACT_ACT_ENVIRONMENT = previousIsActEnvironment;
134 }
135
136 actingUpdatesScopeDepth--;
137
138 {
139 if (actingUpdatesScopeDepth > previousActingUpdatesScopeDepth) {
140 // if it's _less than_ previousActingUpdatesScopeDepth, then we can
141 // assume the 'other' one has warned
142 error('You seem to have overlapping act() calls, this is not supported. ' + 'Be sure to await previous act() calls before making a new one. ');
143 }
144 }
145 }; // TODO: This would be way simpler if 1) we required a promise to be
146 // returned and 2) we could use async/await. Since it's only our used in
147 // our test suite, we should be able to.
148
149
150 try {
151 var result = scope();
152
153 if (typeof result === 'object' && result !== null && typeof result.then === 'function') {
154 var thenableResult = result;
155 return {
156 then: function (resolve, reject) {
157 thenableResult.then(function (returnValue) {
158 flushActWork(function () {
159 unwind();
160 resolve(returnValue);
161 }, function (error) {
162 unwind();
163 reject(error);
164 });
165 }, function (error) {
166 unwind();
167 reject(error);
168 });
169 }
170 };
171 } else {
172 var returnValue = result;
173
174 try {
175 // TODO: Let's not support non-async scopes at all in our tests. Need to
176 // migrate existing tests.
177 var didFlushWork;
178
179 do {
180 didFlushWork = Scheduler.unstable_flushAllWithoutAsserting();
181 } while (didFlushWork);
182
183 return {
184 then: function (resolve, reject) {
185 resolve(returnValue);
186 }
187 };
188 } finally {
189 unwind();
190 }
191 }
192 } catch (error) {
193 unwind();
194 throw error;
195 }
196}
197
198function flushActWork(resolve, reject) {
199 // Flush suspended fallbacks
200 // $FlowFixMe: Flow doesn't know about global Jest object
201 jest.runOnlyPendingTimers();
202 enqueueTask(function () {
203 try {
204 var didFlushWork = Scheduler.unstable_flushAllWithoutAsserting();
205
206 if (didFlushWork) {
207 flushActWork(resolve, reject);
208 } else {
209 resolve();
210 }
211 } catch (error) {
212 reject(error);
213 }
214 });
215}
216
217function captureAssertion(fn) {
218 // Trick to use a Jest matcher inside another Jest matcher. `fn` contains an
219 // assertion; if it throws, we capture the error and return it, so the stack
220 // trace presented to the user points to the original assertion in the
221 // test file.
222 try {
223 fn();
224 } catch (error) {
225 return {
226 pass: false,
227 message: function () {
228 return error.message;
229 }
230 };
231 }
232
233 return {
234 pass: true
235 };
236}
237
238function assertYieldsWereCleared(root) {
239 var Scheduler = root._Scheduler;
240 var actualYields = Scheduler.unstable_clearYields();
241
242 if (actualYields.length !== 0) {
243 throw new Error('Log of yielded values is not empty. ' + 'Call expect(ReactTestRenderer).unstable_toHaveYielded(...) first.');
244 }
245}
246
247function unstable_toMatchRenderedOutput(root, expectedJSX) {
248 assertYieldsWereCleared(root);
249 var actualJSON = root.toJSON();
250 var actualJSX;
251
252 if (actualJSON === null || typeof actualJSON === 'string') {
253 actualJSX = actualJSON;
254 } else if (isArray(actualJSON)) {
255 if (actualJSON.length === 0) {
256 actualJSX = null;
257 } else if (actualJSON.length === 1) {
258 actualJSX = jsonChildToJSXChild(actualJSON[0]);
259 } else {
260 var actualJSXChildren = jsonChildrenToJSXChildren(actualJSON);
261
262 if (actualJSXChildren === null || typeof actualJSXChildren === 'string') {
263 actualJSX = actualJSXChildren;
264 } else {
265 actualJSX = {
266 $$typeof: REACT_ELEMENT_TYPE,
267 type: REACT_FRAGMENT_TYPE,
268 key: null,
269 ref: null,
270 props: {
271 children: actualJSXChildren
272 },
273 _owner: null,
274 _store: {}
275 };
276 }
277 }
278 } else {
279 actualJSX = jsonChildToJSXChild(actualJSON);
280 }
281
282 return captureAssertion(function () {
283 expect(actualJSX).toEqual(expectedJSX);
284 });
285}
286
287function jsonChildToJSXChild(jsonChild) {
288 if (jsonChild === null || typeof jsonChild === 'string') {
289 return jsonChild;
290 } else {
291 var jsxChildren = jsonChildrenToJSXChildren(jsonChild.children);
292 return {
293 $$typeof: REACT_ELEMENT_TYPE,
294 type: jsonChild.type,
295 key: null,
296 ref: null,
297 props: jsxChildren === null ? jsonChild.props : assign({}, jsonChild.props, {
298 children: jsxChildren
299 }),
300 _owner: null,
301 _store: {}
302 };
303 }
304}
305
306function jsonChildrenToJSXChildren(jsonChildren) {
307 if (jsonChildren !== null) {
308 if (jsonChildren.length === 1) {
309 return jsonChildToJSXChild(jsonChildren[0]);
310 } else if (jsonChildren.length > 1) {
311 var jsxChildren = [];
312 var allJSXChildrenAreStrings = true;
313 var jsxChildrenString = '';
314
315 for (var i = 0; i < jsonChildren.length; i++) {
316 var jsxChild = jsonChildToJSXChild(jsonChildren[i]);
317 jsxChildren.push(jsxChild);
318
319 if (allJSXChildrenAreStrings) {
320 if (typeof jsxChild === 'string') {
321 jsxChildrenString += jsxChild;
322 } else if (jsxChild !== null) {
323 allJSXChildrenAreStrings = false;
324 }
325 }
326 }
327
328 return allJSXChildrenAreStrings ? jsxChildrenString : jsxChildren;
329 }
330 }
331
332 return null;
333}
334
335exports.act = act;
336exports.unstable_toMatchRenderedOutput = unstable_toMatchRenderedOutput;
337 })();
338}