Files
react/compiled/facebook-www/ReactTestUtils-dev.classic.js
T
acdlite a96ef807a2 Add stable React.act export (#28160)
Starting in version 19, users can import the `act` testing API from the
`react` package instead of using a renderer specific API, like
`react-dom/test-utils`.

DiffTrain build for [53b12e46a1](https://github.com/facebook/react/commit/53b12e46a17549ec7644e13c126440ed2f3629fd)
2024-02-01 18:34:16 +00:00

1802 lines
53 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @noflow
* @nolint
* @preventMunge
* @preserve-invariant-messages
*/
"use strict";
if (__DEV__) {
(function () {
"use strict";
var React = require("react");
var ReactDOM = require("react-dom");
// This refers to a WWW module.
var warningWWW = require("warning");
function warn(format) {
{
{
for (
var _len = arguments.length,
args = new Array(_len > 1 ? _len - 1 : 0),
_key = 1;
_key < _len;
_key++
) {
args[_key - 1] = arguments[_key];
}
printWarning("warn", format, args);
}
}
}
function error(format) {
{
{
for (
var _len2 = arguments.length,
args = new Array(_len2 > 1 ? _len2 - 1 : 0),
_key2 = 1;
_key2 < _len2;
_key2++
) {
args[_key2 - 1] = arguments[_key2];
}
printWarning("error", format, args);
}
}
}
function printWarning(level, format, args) {
{
var React = require("react");
var ReactSharedInternals =
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; // Defensive in case this is fired before React is initialized.
if (ReactSharedInternals != null) {
var ReactDebugCurrentFrame =
ReactSharedInternals.ReactDebugCurrentFrame;
var stack = ReactDebugCurrentFrame.getStackAddendum();
if (stack !== "") {
format += "%s";
args.push(stack);
}
} // TODO: don't ignore level and pass it down somewhere too.
args.unshift(format);
args.unshift(false);
warningWWW.apply(null, args);
}
}
/**
* `ReactInstanceMap` maintains a mapping from a public facing stateful
* instance (key) and the internal representation (value). This allows public
* methods to accept the user facing instance as an argument and map them back
* to internal methods.
*
* Note that this module is currently shared and assumed to be stateless.
* If this becomes an actual Map, that will break.
*/
function get(key) {
return key._reactInternals;
}
// Re-export dynamic flags from the www version.
require("ReactFeatureFlags");
var FunctionComponent = 0;
var ClassComponent = 1;
var HostRoot = 3; // Root of a host tree. Could be nested inside another node.
var HostComponent = 5;
var HostText = 6;
var HostHoistable = 26;
var HostSingleton = 27;
var NoFlags =
/* */
0;
var Placement =
/* */
2;
var Hydrating =
/* */
4096; // You can change the rest (and add more).
function getNearestMountedFiber(fiber) {
var node = fiber;
var nearestMounted = fiber;
if (!fiber.alternate) {
// If there is no alternate, this might be a new tree that isn't inserted
// yet. If it is, then it will have a pending insertion effect on it.
var nextNode = node;
do {
node = nextNode;
if ((node.flags & (Placement | Hydrating)) !== NoFlags) {
// This is an insertion or in-progress hydration. The nearest possible
// mounted fiber is the parent but we need to continue to figure out
// if that one is still mounted.
nearestMounted = node.return;
} // $FlowFixMe[incompatible-type] we bail out when we get a null
nextNode = node.return;
} while (nextNode);
} else {
while (node.return) {
node = node.return;
}
}
if (node.tag === HostRoot) {
// TODO: Check if this was a nested HostRoot when used with
// renderContainerIntoSubtree.
return nearestMounted;
} // If we didn't hit the root, that means that we're in an disconnected tree
// that has been unmounted.
return null;
}
function assertIsMounted(fiber) {
if (getNearestMountedFiber(fiber) !== fiber) {
throw new Error("Unable to find node on an unmounted component.");
}
}
function findCurrentFiberUsingSlowPath(fiber) {
var alternate = fiber.alternate;
if (!alternate) {
// If there is no alternate, then we only need to check if it is mounted.
var nearestMounted = getNearestMountedFiber(fiber);
if (nearestMounted === null) {
throw new Error("Unable to find node on an unmounted component.");
}
if (nearestMounted !== fiber) {
return null;
}
return fiber;
} // If we have two possible branches, we'll walk backwards up to the root
// to see what path the root points to. On the way we may hit one of the
// special cases and we'll deal with them.
var a = fiber;
var b = alternate;
while (true) {
var parentA = a.return;
if (parentA === null) {
// We're at the root.
break;
}
var parentB = parentA.alternate;
if (parentB === null) {
// There is no alternate. This is an unusual case. Currently, it only
// happens when a Suspense component is hidden. An extra fragment fiber
// is inserted in between the Suspense fiber and its children. Skip
// over this extra fragment fiber and proceed to the next parent.
var nextParent = parentA.return;
if (nextParent !== null) {
a = b = nextParent;
continue;
} // If there's no parent, we're at the root.
break;
} // If both copies of the parent fiber point to the same child, we can
// assume that the child is current. This happens when we bailout on low
// priority: the bailed out fiber's child reuses the current child.
if (parentA.child === parentB.child) {
var child = parentA.child;
while (child) {
if (child === a) {
// We've determined that A is the current branch.
assertIsMounted(parentA);
return fiber;
}
if (child === b) {
// We've determined that B is the current branch.
assertIsMounted(parentA);
return alternate;
}
child = child.sibling;
} // We should never have an alternate for any mounting node. So the only
// way this could possibly happen is if this was unmounted, if at all.
throw new Error("Unable to find node on an unmounted component.");
}
if (a.return !== b.return) {
// The return pointer of A and the return pointer of B point to different
// fibers. We assume that return pointers never criss-cross, so A must
// belong to the child set of A.return, and B must belong to the child
// set of B.return.
a = parentA;
b = parentB;
} else {
// The return pointers point to the same fiber. We'll have to use the
// default, slow path: scan the child sets of each parent alternate to see
// which child belongs to which set.
//
// Search parent A's child set
var didFindChild = false;
var _child = parentA.child;
while (_child) {
if (_child === a) {
didFindChild = true;
a = parentA;
b = parentB;
break;
}
if (_child === b) {
didFindChild = true;
b = parentA;
a = parentB;
break;
}
_child = _child.sibling;
}
if (!didFindChild) {
// Search parent B's child set
_child = parentB.child;
while (_child) {
if (_child === a) {
didFindChild = true;
a = parentB;
b = parentA;
break;
}
if (_child === b) {
didFindChild = true;
b = parentB;
a = parentA;
break;
}
_child = _child.sibling;
}
if (!didFindChild) {
throw new Error(
"Child was not found in either parent set. This indicates a bug " +
"in React related to the return pointer. Please file an issue."
);
}
}
}
if (a.alternate !== b) {
throw new Error(
"Return fibers should always be each others' alternates. " +
"This error is likely caused by a bug in React. Please file an issue."
);
}
} // If the root is not a host container, we're in a disconnected tree. I.e.
// unmounted.
if (a.tag !== HostRoot) {
throw new Error("Unable to find node on an unmounted component.");
}
if (a.stateNode.current === a) {
// We've determined that A is the current branch.
return fiber;
} // Otherwise B has to be current branch.
return alternate;
}
var assign = Object.assign;
/**
* `charCode` represents the actual "character code" and is safe to use with
* `String.fromCharCode`. As such, only keys that correspond to printable
* characters produce a valid `charCode`, the only exception to this is Enter.
* The Tab-key is considered non-printable and does not have a `charCode`,
* presumably because it does not produce a tab-character in browsers.
*
* @param {object} nativeEvent Native browser event.
* @return {number} Normalized `charCode` property.
*/
function getEventCharCode(nativeEvent) {
var charCode;
var keyCode = nativeEvent.keyCode;
if ("charCode" in nativeEvent) {
charCode = nativeEvent.charCode; // FF does not set `charCode` for the Enter-key, check against `keyCode`.
if (charCode === 0 && keyCode === 13) {
charCode = 13;
}
} else {
// IE8 does not implement `charCode`, but `keyCode` has the correct value.
charCode = keyCode;
} // IE and Edge (on Windows) and Chrome / Safari (on Windows and Linux)
// report Enter as charCode 10 when ctrl is pressed.
if (charCode === 10) {
charCode = 13;
} // Some non-printable keys are reported in `charCode`/`keyCode`, discard them.
// Must not discard the (non-)printable Enter-key.
if (charCode >= 32 || charCode === 13) {
return charCode;
}
return 0;
}
function functionThatReturnsTrue() {
return true;
}
function functionThatReturnsFalse() {
return false;
} // This is intentionally a factory so that we have different returned constructors.
// If we had a single constructor, it would be megamorphic and engines would deopt.
function createSyntheticEvent(Interface) {
/**
* Synthetic events are dispatched by event plugins, typically in response to a
* top-level event delegation handler.
*
* These systems should generally use pooling to reduce the frequency of garbage
* collection. The system should check `isPersistent` to determine whether the
* event should be released into the pool after being dispatched. Users that
* need a persisted event should invoke `persist`.
*
* Synthetic events (and subclasses) implement the DOM Level 3 Events API by
* normalizing browser quirks. Subclasses do not necessarily have to implement a
* DOM interface; custom application-specific events can also subclass this.
*/
// $FlowFixMe[missing-this-annot]
function SyntheticBaseEvent(
reactName,
reactEventType,
targetInst,
nativeEvent,
nativeEventTarget
) {
this._reactName = reactName;
this._targetInst = targetInst;
this.type = reactEventType;
this.nativeEvent = nativeEvent;
this.target = nativeEventTarget;
this.currentTarget = null;
for (var propName in Interface) {
if (!Interface.hasOwnProperty(propName)) {
continue;
}
var normalize = Interface[propName];
if (normalize) {
this[propName] = normalize(nativeEvent);
} else {
this[propName] = nativeEvent[propName];
}
}
var defaultPrevented =
nativeEvent.defaultPrevented != null
? nativeEvent.defaultPrevented
: nativeEvent.returnValue === false;
if (defaultPrevented) {
this.isDefaultPrevented = functionThatReturnsTrue;
} else {
this.isDefaultPrevented = functionThatReturnsFalse;
}
this.isPropagationStopped = functionThatReturnsFalse;
return this;
} // $FlowFixMe[prop-missing] found when upgrading Flow
assign(SyntheticBaseEvent.prototype, {
// $FlowFixMe[missing-this-annot]
preventDefault: function () {
this.defaultPrevented = true;
var event = this.nativeEvent;
if (!event) {
return;
}
if (event.preventDefault) {
event.preventDefault(); // $FlowFixMe[illegal-typeof] - flow is not aware of `unknown` in IE
} else if (typeof event.returnValue !== "unknown") {
event.returnValue = false;
}
this.isDefaultPrevented = functionThatReturnsTrue;
},
// $FlowFixMe[missing-this-annot]
stopPropagation: function () {
var event = this.nativeEvent;
if (!event) {
return;
}
if (event.stopPropagation) {
event.stopPropagation(); // $FlowFixMe[illegal-typeof] - flow is not aware of `unknown` in IE
} else if (typeof event.cancelBubble !== "unknown") {
// The ChangeEventPlugin registers a "propertychange" event for
// IE. This event does not support bubbling or cancelling, and
// any references to cancelBubble throw "Member not found". A
// typeof check of "unknown" circumvents this issue (and is also
// IE specific).
event.cancelBubble = true;
}
this.isPropagationStopped = functionThatReturnsTrue;
},
/**
* We release all dispatched `SyntheticEvent`s after each event loop, adding
* them back into the pool. This allows a way to hold onto a reference that
* won't be added back into the pool.
*/
persist: function () {
// Modern event system doesn't use pooling.
},
/**
* Checks if this event should be released back into the pool.
*
* @return {boolean} True if this should not be released, false otherwise.
*/
isPersistent: functionThatReturnsTrue
});
return SyntheticBaseEvent;
}
/**
* @interface Event
* @see http://www.w3.org/TR/DOM-Level-3-Events/
*/
var EventInterface = {
eventPhase: 0,
bubbles: 0,
cancelable: 0,
timeStamp: function (event) {
return event.timeStamp || Date.now();
},
defaultPrevented: 0,
isTrusted: 0
};
var SyntheticEvent = createSyntheticEvent(EventInterface);
var UIEventInterface = assign({}, EventInterface, {
view: 0,
detail: 0
});
createSyntheticEvent(UIEventInterface);
var lastMovementX;
var lastMovementY;
var lastMouseEvent;
function updateMouseMovementPolyfillState(event) {
if (event !== lastMouseEvent) {
if (lastMouseEvent && event.type === "mousemove") {
// $FlowFixMe[unsafe-arithmetic] assuming this is a number
lastMovementX = event.screenX - lastMouseEvent.screenX; // $FlowFixMe[unsafe-arithmetic] assuming this is a number
lastMovementY = event.screenY - lastMouseEvent.screenY;
} else {
lastMovementX = 0;
lastMovementY = 0;
}
lastMouseEvent = event;
}
}
/**
* @interface MouseEvent
* @see http://www.w3.org/TR/DOM-Level-3-Events/
*/
var MouseEventInterface = assign({}, UIEventInterface, {
screenX: 0,
screenY: 0,
clientX: 0,
clientY: 0,
pageX: 0,
pageY: 0,
ctrlKey: 0,
shiftKey: 0,
altKey: 0,
metaKey: 0,
getModifierState: getEventModifierState,
button: 0,
buttons: 0,
relatedTarget: function (event) {
if (event.relatedTarget === undefined)
return event.fromElement === event.srcElement
? event.toElement
: event.fromElement;
return event.relatedTarget;
},
movementX: function (event) {
if ("movementX" in event) {
return event.movementX;
}
updateMouseMovementPolyfillState(event);
return lastMovementX;
},
movementY: function (event) {
if ("movementY" in event) {
return event.movementY;
} // Don't need to call updateMouseMovementPolyfillState() here
// because it's guaranteed to have already run when movementX
// was copied.
return lastMovementY;
}
});
createSyntheticEvent(MouseEventInterface);
/**
* @interface DragEvent
* @see http://www.w3.org/TR/DOM-Level-3-Events/
*/
var DragEventInterface = assign({}, MouseEventInterface, {
dataTransfer: 0
});
createSyntheticEvent(DragEventInterface);
/**
* @interface FocusEvent
* @see http://www.w3.org/TR/DOM-Level-3-Events/
*/
var FocusEventInterface = assign({}, UIEventInterface, {
relatedTarget: 0
});
createSyntheticEvent(FocusEventInterface);
/**
* @interface Event
* @see http://www.w3.org/TR/css3-animations/#AnimationEvent-interface
* @see https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent
*/
var AnimationEventInterface = assign({}, EventInterface, {
animationName: 0,
elapsedTime: 0,
pseudoElement: 0
});
createSyntheticEvent(AnimationEventInterface);
/**
* @interface Event
* @see http://www.w3.org/TR/clipboard-apis/
*/
var ClipboardEventInterface = assign({}, EventInterface, {
clipboardData: function (event) {
return "clipboardData" in event
? event.clipboardData
: window.clipboardData;
}
});
createSyntheticEvent(ClipboardEventInterface);
/**
* @interface Event
* @see http://www.w3.org/TR/DOM-Level-3-Events/#events-compositionevents
*/
var CompositionEventInterface = assign({}, EventInterface, {
data: 0
});
createSyntheticEvent(CompositionEventInterface);
/**
* Normalization of deprecated HTML5 `key` values
* @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
*/
var normalizeKey = {
Esc: "Escape",
Spacebar: " ",
Left: "ArrowLeft",
Up: "ArrowUp",
Right: "ArrowRight",
Down: "ArrowDown",
Del: "Delete",
Win: "OS",
Menu: "ContextMenu",
Apps: "ContextMenu",
Scroll: "ScrollLock",
MozPrintableKey: "Unidentified"
};
/**
* Translation from legacy `keyCode` to HTML5 `key`
* Only special keys supported, all others depend on keyboard layout or browser
* @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
*/
var translateToKey = {
"8": "Backspace",
"9": "Tab",
"12": "Clear",
"13": "Enter",
"16": "Shift",
"17": "Control",
"18": "Alt",
"19": "Pause",
"20": "CapsLock",
"27": "Escape",
"32": " ",
"33": "PageUp",
"34": "PageDown",
"35": "End",
"36": "Home",
"37": "ArrowLeft",
"38": "ArrowUp",
"39": "ArrowRight",
"40": "ArrowDown",
"45": "Insert",
"46": "Delete",
"112": "F1",
"113": "F2",
"114": "F3",
"115": "F4",
"116": "F5",
"117": "F6",
"118": "F7",
"119": "F8",
"120": "F9",
"121": "F10",
"122": "F11",
"123": "F12",
"144": "NumLock",
"145": "ScrollLock",
"224": "Meta"
};
/**
* @param {object} nativeEvent Native browser event.
* @return {string} Normalized `key` property.
*/
function getEventKey(nativeEvent) {
if (nativeEvent.key) {
// Normalize inconsistent values reported by browsers due to
// implementations of a working draft specification.
// FireFox implements `key` but returns `MozPrintableKey` for all
// printable characters (normalized to `Unidentified`), ignore it.
var key = normalizeKey[nativeEvent.key] || nativeEvent.key; // $FlowFixMe[invalid-computed-prop] unable to index with a `mixed` value
if (key !== "Unidentified") {
return key;
}
} // Browser does not implement `key`, polyfill as much of it as we can.
if (nativeEvent.type === "keypress") {
var charCode = getEventCharCode(
// $FlowFixMe[incompatible-call] unable to narrow to `KeyboardEvent`
nativeEvent
); // The enter-key is technically both printable and non-printable and can
// thus be captured by `keypress`, no other non-printable key should.
return charCode === 13 ? "Enter" : String.fromCharCode(charCode);
}
if (nativeEvent.type === "keydown" || nativeEvent.type === "keyup") {
// While user keyboard layout determines the actual meaning of each
// `keyCode` value, almost all function keys have a universal value.
// $FlowFixMe[invalid-computed-prop] unable to index with a `mixed` value
return translateToKey[nativeEvent.keyCode] || "Unidentified";
}
return "";
}
/**
* Translation from modifier key to the associated property in the event.
* @see http://www.w3.org/TR/DOM-Level-3-Events/#keys-Modifiers
*/
var modifierKeyToProp = {
Alt: "altKey",
Control: "ctrlKey",
Meta: "metaKey",
Shift: "shiftKey"
}; // Older browsers (Safari <= 10, iOS Safari <= 10.2) do not support
// getModifierState. If getModifierState is not supported, we map it to a set of
// modifier keys exposed by the event. In this case, Lock-keys are not supported.
// $FlowFixMe[missing-local-annot]
// $FlowFixMe[missing-this-annot]
function modifierStateGetter(keyArg) {
var syntheticEvent = this;
var nativeEvent = syntheticEvent.nativeEvent;
if (nativeEvent.getModifierState) {
return nativeEvent.getModifierState(keyArg);
}
var keyProp = modifierKeyToProp[keyArg];
return keyProp ? !!nativeEvent[keyProp] : false;
}
function getEventModifierState(nativeEvent) {
return modifierStateGetter;
}
/**
* @interface KeyboardEvent
* @see http://www.w3.org/TR/DOM-Level-3-Events/
*/
var KeyboardEventInterface = assign({}, UIEventInterface, {
key: getEventKey,
code: 0,
location: 0,
ctrlKey: 0,
shiftKey: 0,
altKey: 0,
metaKey: 0,
repeat: 0,
locale: 0,
getModifierState: getEventModifierState,
// Legacy Interface
charCode: function (event) {
// `charCode` is the result of a KeyPress event and represents the value of
// the actual printable character.
// KeyPress is deprecated, but its replacement is not yet final and not
// implemented in any major browser. Only KeyPress has charCode.
if (event.type === "keypress") {
return getEventCharCode(
// $FlowFixMe[incompatible-call] unable to narrow to `KeyboardEvent`
event
);
}
return 0;
},
keyCode: function (event) {
// `keyCode` is the result of a KeyDown/Up event and represents the value of
// physical keyboard key.
// The actual meaning of the value depends on the users' keyboard layout
// which cannot be detected. Assuming that it is a US keyboard layout
// provides a surprisingly accurate mapping for US and European users.
// Due to this, it is left to the user to implement at this time.
if (event.type === "keydown" || event.type === "keyup") {
return event.keyCode;
}
return 0;
},
which: function (event) {
// `which` is an alias for either `keyCode` or `charCode` depending on the
// type of the event.
if (event.type === "keypress") {
return getEventCharCode(
// $FlowFixMe[incompatible-call] unable to narrow to `KeyboardEvent`
event
);
}
if (event.type === "keydown" || event.type === "keyup") {
return event.keyCode;
}
return 0;
}
});
createSyntheticEvent(KeyboardEventInterface);
/**
* @interface PointerEvent
* @see http://www.w3.org/TR/pointerevents/
*/
var PointerEventInterface = assign({}, MouseEventInterface, {
pointerId: 0,
width: 0,
height: 0,
pressure: 0,
tangentialPressure: 0,
tiltX: 0,
tiltY: 0,
twist: 0,
pointerType: 0,
isPrimary: 0
});
createSyntheticEvent(PointerEventInterface);
/**
* @interface TouchEvent
* @see http://www.w3.org/TR/touch-events/
*/
var TouchEventInterface = assign({}, UIEventInterface, {
touches: 0,
targetTouches: 0,
changedTouches: 0,
altKey: 0,
metaKey: 0,
ctrlKey: 0,
shiftKey: 0,
getModifierState: getEventModifierState
});
createSyntheticEvent(TouchEventInterface);
/**
* @interface Event
* @see http://www.w3.org/TR/2009/WD-css3-transitions-20090320/#transition-events-
* @see https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent
*/
var TransitionEventInterface = assign({}, EventInterface, {
propertyName: 0,
elapsedTime: 0,
pseudoElement: 0
});
createSyntheticEvent(TransitionEventInterface);
/**
* @interface WheelEvent
* @see http://www.w3.org/TR/DOM-Level-3-Events/
*/
var WheelEventInterface = assign({}, MouseEventInterface, {
deltaX: function (event) {
return "deltaX" in event
? event.deltaX // Fallback to `wheelDeltaX` for Webkit and normalize (right is positive).
: "wheelDeltaX" in event // $FlowFixMe[unsafe-arithmetic] assuming this is a number
? -event.wheelDeltaX
: 0;
},
deltaY: function (event) {
return "deltaY" in event
? event.deltaY // Fallback to `wheelDeltaY` for Webkit and normalize (down is positive).
: "wheelDeltaY" in event // $FlowFixMe[unsafe-arithmetic] assuming this is a number
? -event.wheelDeltaY // Fallback to `wheelDelta` for IE<9 and normalize (down is positive).
: "wheelDelta" in event // $FlowFixMe[unsafe-arithmetic] assuming this is a number
? -event.wheelDelta
: 0;
},
deltaZ: 0,
// Browsers without "deltaMode" is reporting in raw wheel delta where one
// notch on the scroll is always +/- 120, roughly equivalent to pixels.
// A good approximation of DOM_DELTA_LINE (1) is 5% of viewport size or
// ~40 pixels, for DOM_DELTA_SCREEN (2) it is 87.5% of viewport size.
deltaMode: 0
});
createSyntheticEvent(WheelEventInterface);
/**
* HTML nodeType values that represent the type of the node
*/
var ELEMENT_NODE = 1;
// Provided by www
var ReactFbErrorUtils = require("ReactFbErrorUtils");
if (typeof ReactFbErrorUtils.invokeGuardedCallback !== "function") {
throw new Error(
"Expected ReactFbErrorUtils.invokeGuardedCallback to be a function."
);
}
function invokeGuardedCallbackImpl(name, func, context, a, b, c, d, e, f) {
// This will call `this.onError(err)` if an error was caught.
ReactFbErrorUtils.invokeGuardedCallback.apply(this, arguments);
}
var hasError = false;
var caughtError = null; // Used by event system to capture/rethrow the first error.
var hasRethrowError = false;
var rethrowError = null;
var reporter = {
onError: function (error) {
hasError = true;
caughtError = error;
}
};
/**
* Call a function while guarding against errors that happens within it.
* Returns an error if it throws, otherwise null.
*
* In production, this is implemented using a try-catch. The reason we don't
* use a try-catch directly is so that we can swap out a different
* implementation in DEV mode.
*
* @param {String} name of the guard to use for logging or debugging
* @param {Function} func The function to invoke
* @param {*} context The context to use when calling the function
* @param {...*} args Arguments for function
*/
function invokeGuardedCallback(name, func, context, a, b, c, d, e, f) {
hasError = false;
caughtError = null;
invokeGuardedCallbackImpl.apply(reporter, arguments);
}
/**
* Same as invokeGuardedCallback, but instead of returning an error, it stores
* it in a global so it can be rethrown by `rethrowCaughtError` later.
* TODO: See if caughtError and rethrowError can be unified.
*
* @param {String} name of the guard to use for logging or debugging
* @param {Function} func The function to invoke
* @param {*} context The context to use when calling the function
* @param {...*} args Arguments for function
*/
function invokeGuardedCallbackAndCatchFirstError(
name,
func,
context,
a,
b,
c,
d,
e,
f
) {
invokeGuardedCallback.apply(this, arguments);
if (hasError) {
var error = clearCaughtError();
if (!hasRethrowError) {
hasRethrowError = true;
rethrowError = error;
}
}
}
/**
* During execution of guarded functions we will capture the first error which
* we will rethrow to be handled by the top level error handler.
*/
function rethrowCaughtError() {
if (hasRethrowError) {
var error = rethrowError;
hasRethrowError = false;
rethrowError = null;
throw error;
}
}
function clearCaughtError() {
if (hasError) {
var error = caughtError;
hasError = false;
caughtError = null;
return error;
} else {
throw new Error(
"clearCaughtError was called but no error was captured. This error " +
"is likely caused by a bug in React. Please file an issue."
);
}
}
var isArrayImpl = Array.isArray; // eslint-disable-next-line no-redeclare
function isArray(a) {
return isArrayImpl(a);
}
var SecretInternals =
ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
var EventInternals = SecretInternals.Events;
var getInstanceFromNode = EventInternals[0];
var getNodeFromInstance = EventInternals[1];
var getFiberCurrentPropsFromNode = EventInternals[2];
var enqueueStateRestore = EventInternals[3];
var restoreStateIfNeeded = EventInternals[4]; // TODO: Add a warning if this API is accessed with advice to switch to
// importing directly from the React package instead.
var act = React.act;
function Event(suffix) {}
var hasWarnedAboutDeprecatedMockComponent = false;
/**
* @class ReactTestUtils
*/
function findAllInRenderedFiberTreeInternal(fiber, test) {
if (!fiber) {
return [];
}
var currentParent = findCurrentFiberUsingSlowPath(fiber);
if (!currentParent) {
return [];
}
var node = currentParent;
var ret = [];
while (true) {
if (
node.tag === HostComponent ||
node.tag === HostText ||
node.tag === ClassComponent ||
node.tag === FunctionComponent ||
node.tag === HostHoistable ||
node.tag === HostSingleton
) {
var publicInst = node.stateNode;
if (test(publicInst)) {
ret.push(publicInst);
}
}
if (node.child) {
node.child.return = node;
node = node.child;
continue;
}
if (node === currentParent) {
return ret;
}
while (!node.sibling) {
if (!node.return || node.return === currentParent) {
return ret;
}
node = node.return;
}
node.sibling.return = node.return;
node = node.sibling;
}
}
function validateClassInstance(inst, methodName) {
if (!inst) {
// This is probably too relaxed but it's existing behavior.
return;
}
if (get(inst)) {
// This is a public instance indeed.
return;
}
var received;
var stringified = String(inst);
if (isArray(inst)) {
received = "an array";
} else if (inst && inst.nodeType === ELEMENT_NODE && inst.tagName) {
received = "a DOM node";
} else if (stringified === "[object Object]") {
received = "object with keys {" + Object.keys(inst).join(", ") + "}";
} else {
received = stringified;
}
throw new Error(
methodName +
"(...): the first argument must be a React class instance. " +
("Instead received: " + received + ".")
);
}
/**
* Utilities for making it easy to test React components.
*
* See https://reactjs.org/docs/test-utils.html
*
* Todo: Support the entire DOM.scry query syntax. For now, these simple
* utilities will suffice for testing purposes.
* @lends ReactTestUtils
*/
function renderIntoDocument(element) {
var div = document.createElement("div"); // None of our tests actually require attaching the container to the
// DOM, and doing so creates a mess that we rely on test isolation to
// clean up, so we're going to stop honoring the name of this method
// (and probably rename it eventually) if no problems arise.
// document.documentElement.appendChild(div);
return ReactDOM.render(element, div);
}
function isElement(element) {
return React.isValidElement(element);
}
function isElementOfType(inst, convenienceConstructor) {
return React.isValidElement(inst) && inst.type === convenienceConstructor;
}
function isDOMComponent(inst) {
return !!(inst && inst.nodeType === ELEMENT_NODE && inst.tagName);
}
function isDOMComponentElement(inst) {
return !!(inst && React.isValidElement(inst) && !!inst.tagName);
}
function isCompositeComponent(inst) {
if (isDOMComponent(inst)) {
// Accessing inst.setState warns; just return false as that'll be what
// this returns when we have DOM nodes as refs directly
return false;
}
return (
inst != null &&
typeof inst.render === "function" &&
typeof inst.setState === "function"
);
}
function isCompositeComponentWithType(inst, type) {
if (!isCompositeComponent(inst)) {
return false;
}
var internalInstance = get(inst);
var constructor = internalInstance.type;
return constructor === type;
}
function findAllInRenderedTree(inst, test) {
validateClassInstance(inst, "findAllInRenderedTree");
if (!inst) {
return [];
}
var internalInstance = get(inst);
return findAllInRenderedFiberTreeInternal(internalInstance, test);
}
/**
* Finds all instances of components in the rendered tree that are DOM
* components with the class name matching `className`.
* @return {array} an array of all the matches.
*/
function scryRenderedDOMComponentsWithClass(root, classNames) {
validateClassInstance(root, "scryRenderedDOMComponentsWithClass");
return findAllInRenderedTree(root, function (inst) {
if (isDOMComponent(inst)) {
var className = inst.className;
if (typeof className !== "string") {
// SVG, probably.
className = inst.getAttribute("class") || "";
}
var classList = className.split(/\s+/);
if (!isArray(classNames)) {
if (classNames === undefined) {
throw new Error(
"TestUtils.scryRenderedDOMComponentsWithClass expects a " +
"className as a second argument."
);
}
classNames = classNames.split(/\s+/);
}
return classNames.every(function (name) {
return classList.indexOf(name) !== -1;
});
}
return false;
});
}
/**
* Like scryRenderedDOMComponentsWithClass but expects there to be one result,
* and returns that one result, or throws exception if there is any other
* number of matches besides one.
* @return {!ReactDOMComponent} The one match.
*/
function findRenderedDOMComponentWithClass(root, className) {
validateClassInstance(root, "findRenderedDOMComponentWithClass");
var all = scryRenderedDOMComponentsWithClass(root, className);
if (all.length !== 1) {
throw new Error(
"Did not find exactly one match (found: " +
all.length +
") " +
"for class:" +
className
);
}
return all[0];
}
/**
* Finds all instances of components in the rendered tree that are DOM
* components with the tag name matching `tagName`.
* @return {array} an array of all the matches.
*/
function scryRenderedDOMComponentsWithTag(root, tagName) {
validateClassInstance(root, "scryRenderedDOMComponentsWithTag");
return findAllInRenderedTree(root, function (inst) {
return (
isDOMComponent(inst) &&
inst.tagName.toUpperCase() === tagName.toUpperCase()
);
});
}
/**
* Like scryRenderedDOMComponentsWithTag but expects there to be one result,
* and returns that one result, or throws exception if there is any other
* number of matches besides one.
* @return {!ReactDOMComponent} The one match.
*/
function findRenderedDOMComponentWithTag(root, tagName) {
validateClassInstance(root, "findRenderedDOMComponentWithTag");
var all = scryRenderedDOMComponentsWithTag(root, tagName);
if (all.length !== 1) {
throw new Error(
"Did not find exactly one match (found: " +
all.length +
") " +
"for tag:" +
tagName
);
}
return all[0];
}
/**
* Finds all instances of components with type equal to `componentType`.
* @return {array} an array of all the matches.
*/
function scryRenderedComponentsWithType(root, componentType) {
validateClassInstance(root, "scryRenderedComponentsWithType");
return findAllInRenderedTree(root, function (inst) {
return isCompositeComponentWithType(inst, componentType);
});
}
/**
* Same as `scryRenderedComponentsWithType` but expects there to be one result
* and returns that one result, or throws exception if there is any other
* number of matches besides one.
* @return {!ReactComponent} The one match.
*/
function findRenderedComponentWithType(root, componentType) {
validateClassInstance(root, "findRenderedComponentWithType");
var all = scryRenderedComponentsWithType(root, componentType);
if (all.length !== 1) {
throw new Error(
"Did not find exactly one match (found: " +
all.length +
") " +
"for componentType:" +
componentType
);
}
return all[0];
}
/**
* Pass a mocked component module to this method to augment it with
* useful methods that allow it to be used as a dummy React component.
* Instead of rendering as usual, the component will become a simple
* <div> containing any provided children.
*
* @param {object} module the mock function object exported from a
* module that defines the component to be mocked
* @param {?string} mockTagName optional dummy root tag name to return
* from render method (overrides
* module.mockTagName if provided)
* @return {object} the ReactTestUtils object (for chaining)
*/
function mockComponent(module, mockTagName) {
{
if (!hasWarnedAboutDeprecatedMockComponent) {
hasWarnedAboutDeprecatedMockComponent = true;
warn(
"ReactTestUtils.mockComponent() is deprecated. " +
"Use shallow rendering or jest.mock() instead.\n\n" +
"See https://reactjs.org/link/test-utils-mock-component for more information."
);
}
}
mockTagName = mockTagName || module.mockTagName || "div";
module.prototype.render.mockImplementation(function () {
return React.createElement(mockTagName, null, this.props.children);
});
return this;
}
function nativeTouchData(x, y) {
return {
touches: [
{
pageX: x,
pageY: y
}
]
};
} // Start of inline: the below functions were inlined from
// EventPropagator.js, as they deviated from ReactDOM's newer
// implementations.
/**
* Dispatch the event to the listener.
* @param {SyntheticEvent} event SyntheticEvent to handle
* @param {function} listener Application-level callback
* @param {*} inst Internal component instance
*/
function executeDispatch(event, listener, inst) {
var type = event.type || "unknown-event";
event.currentTarget = getNodeFromInstance(inst);
invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);
event.currentTarget = null;
}
/**
* Standard/simple iteration through an event's collected dispatches.
*/
function executeDispatchesInOrder(event) {
var dispatchListeners = event._dispatchListeners;
var dispatchInstances = event._dispatchInstances;
if (isArray(dispatchListeners)) {
for (var i = 0; i < dispatchListeners.length; i++) {
if (event.isPropagationStopped()) {
break;
} // Listeners and Instances are two parallel arrays that are always in sync.
executeDispatch(event, dispatchListeners[i], dispatchInstances[i]);
}
} else if (dispatchListeners) {
executeDispatch(event, dispatchListeners, dispatchInstances);
}
event._dispatchListeners = null;
event._dispatchInstances = null;
}
/**
* Dispatches an event and releases it back into the pool, unless persistent.
*
* @param {?object} event Synthetic event to be dispatched.
* @private
*/
function executeDispatchesAndRelease(
event
/* ReactSyntheticEvent */
) {
if (event) {
executeDispatchesInOrder(event);
if (!event.isPersistent()) {
event.constructor.release(event);
}
}
}
function isInteractive(tag) {
return (
tag === "button" ||
tag === "input" ||
tag === "select" ||
tag === "textarea"
);
}
function getParent(inst) {
do {
inst = inst.return; // TODO: If this is a HostRoot we might want to bail out.
// That is depending on if we want nested subtrees (layers) to bubble
// events to their parent. We could also go through parentNode on the
// host node but that wouldn't work for React Native and doesn't let us
// do the portal feature.
} while (
inst &&
inst.tag !== HostComponent &&
inst.tag !== HostSingleton
);
if (inst) {
return inst;
}
return null;
}
/**
* Simulates the traversal of a two-phase, capture/bubble event dispatch.
*/
function traverseTwoPhase(inst, fn, arg) {
var path = [];
while (inst) {
path.push(inst);
inst = getParent(inst);
}
var i;
for (i = path.length; i-- > 0; ) {
fn(path[i], "captured", arg);
}
for (i = 0; i < path.length; i++) {
fn(path[i], "bubbled", arg);
}
}
function shouldPreventMouseEvent(name, type, props) {
switch (name) {
case "onClick":
case "onClickCapture":
case "onDoubleClick":
case "onDoubleClickCapture":
case "onMouseDown":
case "onMouseDownCapture":
case "onMouseMove":
case "onMouseMoveCapture":
case "onMouseUp":
case "onMouseUpCapture":
case "onMouseEnter":
return !!(props.disabled && isInteractive(type));
default:
return false;
}
}
/**
* @param {object} inst The instance, which is the source of events.
* @param {string} registrationName Name of listener (e.g. `onClick`).
* @return {?function} The stored callback.
*/
function getListener(
inst,
/* Fiber */
registrationName
) {
// TODO: shouldPreventMouseEvent is DOM-specific and definitely should not
// live here; needs to be moved to a better place soon
var stateNode = inst.stateNode;
if (!stateNode) {
// Work in progress (ex: onload events in incremental mode).
return null;
}
var props = getFiberCurrentPropsFromNode(stateNode);
if (!props) {
// Work in progress.
return null;
}
var listener = props[registrationName];
if (shouldPreventMouseEvent(registrationName, inst.type, props)) {
return null;
}
if (listener && typeof listener !== "function") {
throw new Error(
"Expected `" +
registrationName +
"` listener to be a function, instead got a value of `" +
typeof listener +
"` type."
);
}
return listener;
}
function listenerAtPhase(inst, event, propagationPhase) {
var registrationName = event._reactName;
if (propagationPhase === "captured") {
registrationName += "Capture";
}
return getListener(inst, registrationName);
}
function accumulateDispatches(inst, ignoredDirection, event) {
if (inst && event && event._reactName) {
var registrationName = event._reactName;
var listener = getListener(inst, registrationName);
if (listener) {
if (event._dispatchListeners == null) {
event._dispatchListeners = [];
}
if (event._dispatchInstances == null) {
event._dispatchInstances = [];
}
event._dispatchListeners.push(listener);
event._dispatchInstances.push(inst);
}
}
}
function accumulateDirectionalDispatches(inst, phase, event) {
{
if (!inst) {
error("Dispatching inst must not be null");
}
}
var listener = listenerAtPhase(inst, event, phase);
if (listener) {
if (event._dispatchListeners == null) {
event._dispatchListeners = [];
}
if (event._dispatchInstances == null) {
event._dispatchInstances = [];
}
event._dispatchListeners.push(listener);
event._dispatchInstances.push(inst);
}
}
function accumulateDirectDispatchesSingle(event) {
if (event && event._reactName) {
accumulateDispatches(event._targetInst, null, event);
}
}
function accumulateTwoPhaseDispatchesSingle(event) {
if (event && event._reactName) {
traverseTwoPhase(
event._targetInst,
accumulateDirectionalDispatches,
event
);
}
} // End of inline
var Simulate = {};
var directDispatchEventTypes = new Set([
"mouseEnter",
"mouseLeave",
"pointerEnter",
"pointerLeave"
]);
/**
* Exports:
*
* - `Simulate.click(Element)`
* - `Simulate.mouseMove(Element)`
* - `Simulate.change(Element)`
* - ... (All keys from event plugin `eventTypes` objects)
*/
function makeSimulator(eventType) {
return function (domNode, eventData) {
if (React.isValidElement(domNode)) {
throw new Error(
"TestUtils.Simulate expected a DOM node as the first argument but received " +
"a React element. Pass the DOM node you wish to simulate the event on instead. " +
"Note that TestUtils.Simulate will not work if you are using shallow rendering."
);
}
if (isCompositeComponent(domNode)) {
throw new Error(
"TestUtils.Simulate expected a DOM node as the first argument but received " +
"a component instance. Pass the DOM node you wish to simulate the event on instead."
);
}
var reactName = "on" + eventType[0].toUpperCase() + eventType.slice(1);
var fakeNativeEvent = new Event();
fakeNativeEvent.target = domNode;
fakeNativeEvent.type = eventType.toLowerCase();
var targetInst = getInstanceFromNode(domNode);
var event = new SyntheticEvent(
reactName,
fakeNativeEvent.type,
targetInst,
fakeNativeEvent,
domNode
); // Since we aren't using pooling, always persist the event. This will make
// sure it's marked and won't warn when setting additional properties.
event.persist();
assign(event, eventData);
if (directDispatchEventTypes.has(eventType)) {
accumulateDirectDispatchesSingle(event);
} else {
accumulateTwoPhaseDispatchesSingle(event);
}
ReactDOM.unstable_batchedUpdates(function () {
// Normally extractEvent enqueues a state restore, but we'll just always
// do that since we're by-passing it here.
enqueueStateRestore(domNode);
executeDispatchesAndRelease(event);
rethrowCaughtError();
});
restoreStateIfNeeded();
};
} // A one-time snapshot with no plans to update. We'll probably want to deprecate Simulate API.
var simulatedEventTypes = [
"blur",
"cancel",
"click",
"close",
"contextMenu",
"copy",
"cut",
"auxClick",
"doubleClick",
"dragEnd",
"dragStart",
"drop",
"focus",
"input",
"invalid",
"keyDown",
"keyPress",
"keyUp",
"mouseDown",
"mouseUp",
"paste",
"pause",
"play",
"pointerCancel",
"pointerDown",
"pointerUp",
"rateChange",
"reset",
"resize",
"seeked",
"submit",
"touchCancel",
"touchEnd",
"touchStart",
"volumeChange",
"drag",
"dragEnter",
"dragExit",
"dragLeave",
"dragOver",
"mouseMove",
"mouseOut",
"mouseOver",
"pointerMove",
"pointerOut",
"pointerOver",
"scroll",
"toggle",
"touchMove",
"wheel",
"abort",
"animationEnd",
"animationIteration",
"animationStart",
"canPlay",
"canPlayThrough",
"durationChange",
"emptied",
"encrypted",
"ended",
"error",
"gotPointerCapture",
"load",
"loadedData",
"loadedMetadata",
"loadStart",
"lostPointerCapture",
"playing",
"progress",
"seeking",
"stalled",
"suspend",
"timeUpdate",
"transitionEnd",
"waiting",
"mouseEnter",
"mouseLeave",
"pointerEnter",
"pointerLeave",
"change",
"select",
"beforeInput",
"compositionEnd",
"compositionStart",
"compositionUpdate"
];
function buildSimulators() {
simulatedEventTypes.forEach(function (eventType) {
Simulate[eventType] = makeSimulator(eventType);
});
}
buildSimulators();
exports.Simulate = Simulate;
exports.act = act;
exports.findAllInRenderedTree = findAllInRenderedTree;
exports.findRenderedComponentWithType = findRenderedComponentWithType;
exports.findRenderedDOMComponentWithClass =
findRenderedDOMComponentWithClass;
exports.findRenderedDOMComponentWithTag = findRenderedDOMComponentWithTag;
exports.isCompositeComponent = isCompositeComponent;
exports.isCompositeComponentWithType = isCompositeComponentWithType;
exports.isDOMComponent = isDOMComponent;
exports.isDOMComponentElement = isDOMComponentElement;
exports.isElement = isElement;
exports.isElementOfType = isElementOfType;
exports.mockComponent = mockComponent;
exports.nativeTouchData = nativeTouchData;
exports.renderIntoDocument = renderIntoDocument;
exports.scryRenderedComponentsWithType = scryRenderedComponentsWithType;
exports.scryRenderedDOMComponentsWithClass =
scryRenderedDOMComponentsWithClass;
exports.scryRenderedDOMComponentsWithTag = scryRenderedDOMComponentsWithTag;
exports.traverseTwoPhase = traverseTwoPhase;
})();
}