diff --git a/packages/react-dom/src/__tests__/ReactTestUtils-test.js b/packages/react-dom/src/__tests__/ReactTestUtils-test.js
index 80871a12d8e52..842fb4c1ebc72 100644
--- a/packages/react-dom/src/__tests__/ReactTestUtils-test.js
+++ b/packages/react-dom/src/__tests__/ReactTestUtils-test.js
@@ -9,11 +9,11 @@
'use strict';
-import {act} from 'internal-test-utils';
-import * as React from 'react';
-import * as ReactDOMClient from 'react-dom/client';
-import * as ReactDOMServer from 'react-dom/server';
-import * as ReactTestUtils from 'react-dom/test-utils';
+let React;
+let ReactDOMClient;
+let ReactDOMServer;
+let ReactTestUtils;
+let act;
function getTestDocument(markup) {
const doc = document.implementation.createHTMLDocument('');
@@ -27,8 +27,105 @@ function getTestDocument(markup) {
}
describe('ReactTestUtils', () => {
+ beforeEach(() => {
+ React = require('react');
+ ReactDOMClient = require('react-dom/client');
+ ReactDOMServer = require('react-dom/server');
+ ReactTestUtils = require('react-dom/test-utils');
+ act = require('internal-test-utils').act;
+ });
+
+ // @gate !disableDOMTestUtils
it('Simulate should have locally attached media events', () => {
- expect(Object.keys(ReactTestUtils.Simulate).sort()).toMatchSnapshot();
+ expect(Object.keys(ReactTestUtils.Simulate).sort()).toMatchInlineSnapshot(`
+ [
+ "abort",
+ "animationEnd",
+ "animationIteration",
+ "animationStart",
+ "auxClick",
+ "beforeInput",
+ "blur",
+ "canPlay",
+ "canPlayThrough",
+ "cancel",
+ "change",
+ "click",
+ "close",
+ "compositionEnd",
+ "compositionStart",
+ "compositionUpdate",
+ "contextMenu",
+ "copy",
+ "cut",
+ "doubleClick",
+ "drag",
+ "dragEnd",
+ "dragEnter",
+ "dragExit",
+ "dragLeave",
+ "dragOver",
+ "dragStart",
+ "drop",
+ "durationChange",
+ "emptied",
+ "encrypted",
+ "ended",
+ "error",
+ "focus",
+ "gotPointerCapture",
+ "input",
+ "invalid",
+ "keyDown",
+ "keyPress",
+ "keyUp",
+ "load",
+ "loadStart",
+ "loadedData",
+ "loadedMetadata",
+ "lostPointerCapture",
+ "mouseDown",
+ "mouseEnter",
+ "mouseLeave",
+ "mouseMove",
+ "mouseOut",
+ "mouseOver",
+ "mouseUp",
+ "paste",
+ "pause",
+ "play",
+ "playing",
+ "pointerCancel",
+ "pointerDown",
+ "pointerEnter",
+ "pointerLeave",
+ "pointerMove",
+ "pointerOut",
+ "pointerOver",
+ "pointerUp",
+ "progress",
+ "rateChange",
+ "reset",
+ "resize",
+ "scroll",
+ "seeked",
+ "seeking",
+ "select",
+ "stalled",
+ "submit",
+ "suspend",
+ "timeUpdate",
+ "toggle",
+ "touchCancel",
+ "touchEnd",
+ "touchMove",
+ "touchStart",
+ "transitionEnd",
+ "volumeChange",
+ "waiting",
+ "wheel",
+ ]
+ `);
});
// @gate !disableDOMTestUtils
@@ -575,14 +672,6 @@ describe('ReactTestUtils', () => {
});
expect(idCallOrder).toEqual([CHILD]);
});
-
- // @gate disableDOMTestUtils
- it('throws', async () => {
- expect(ReactTestUtils.Simulate.click).toThrow(
- '`Simulate` was removed from `react-dom/test-utils`. ' +
- 'See https://react.dev/warnings/react-dom-test-utils for more info.',
- );
- });
});
// @gate !disableDOMTestUtils
@@ -626,14 +715,6 @@ describe('ReactTestUtils', () => {
expect(renderedComponentType).toBe(instance);
});
- // @gate disableDOMTestUtils
- it('throws on every removed function', async () => {
- expect(ReactTestUtils.isDOMComponent).toThrow(
- '`isDOMComponent` was removed from `react-dom/test-utils`. ' +
- 'See https://react.dev/warnings/react-dom-test-utils for more info.',
- );
- });
-
// @gate __DEV__
it('warns when using `act`', () => {
expect(() => {
diff --git a/packages/react-dom/src/__tests__/__snapshots__/ReactTestUtils-test.js.snap b/packages/react-dom/src/__tests__/__snapshots__/ReactTestUtils-test.js.snap
deleted file mode 100644
index 8dda81dcae42b..0000000000000
--- a/packages/react-dom/src/__tests__/__snapshots__/ReactTestUtils-test.js.snap
+++ /dev/null
@@ -1,91 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ReactTestUtils Simulate should have locally attached media events 1`] = `
-[
- "abort",
- "animationEnd",
- "animationIteration",
- "animationStart",
- "auxClick",
- "beforeInput",
- "blur",
- "canPlay",
- "canPlayThrough",
- "cancel",
- "change",
- "click",
- "close",
- "compositionEnd",
- "compositionStart",
- "compositionUpdate",
- "contextMenu",
- "copy",
- "cut",
- "doubleClick",
- "drag",
- "dragEnd",
- "dragEnter",
- "dragExit",
- "dragLeave",
- "dragOver",
- "dragStart",
- "drop",
- "durationChange",
- "emptied",
- "encrypted",
- "ended",
- "error",
- "focus",
- "gotPointerCapture",
- "input",
- "invalid",
- "keyDown",
- "keyPress",
- "keyUp",
- "load",
- "loadStart",
- "loadedData",
- "loadedMetadata",
- "lostPointerCapture",
- "mouseDown",
- "mouseEnter",
- "mouseLeave",
- "mouseMove",
- "mouseOut",
- "mouseOver",
- "mouseUp",
- "paste",
- "pause",
- "play",
- "playing",
- "pointerCancel",
- "pointerDown",
- "pointerEnter",
- "pointerLeave",
- "pointerMove",
- "pointerOut",
- "pointerOver",
- "pointerUp",
- "progress",
- "rateChange",
- "reset",
- "resize",
- "scroll",
- "seeked",
- "seeking",
- "select",
- "stalled",
- "submit",
- "suspend",
- "timeUpdate",
- "toggle",
- "touchCancel",
- "touchEnd",
- "touchMove",
- "touchStart",
- "transitionEnd",
- "volumeChange",
- "waiting",
- "wheel",
-]
-`;
diff --git a/packages/react-dom/src/test-utils/ReactTestUtils.js b/packages/react-dom/src/test-utils/ReactTestUtils.js
index 1b4125c819e61..942600954d5d6 100644
--- a/packages/react-dom/src/test-utils/ReactTestUtils.js
+++ b/packages/react-dom/src/test-utils/ReactTestUtils.js
@@ -8,35 +8,9 @@
*/
import * as React from 'react';
-import * as ReactDOM from 'react-dom';
-import {findCurrentFiberUsingSlowPath} from 'react-reconciler/src/ReactFiberTreeReflection';
-import {get as getInstance} from 'shared/ReactInstanceMap';
-import {
- ClassComponent,
- FunctionComponent,
- HostComponent,
- HostHoistable,
- HostSingleton,
- HostText,
-} from 'react-reconciler/src/ReactWorkTags';
-import {SyntheticEvent} from 'react-dom-bindings/src/events/SyntheticEvent';
-import {ELEMENT_NODE} from 'react-dom-bindings/src/client/HTMLNodeType';
-import {disableDOMTestUtils} from 'shared/ReactFeatureFlags';
-import assign from 'shared/assign';
-import isArray from 'shared/isArray';
-
-// Keep in sync with ReactDOM.js:
-const SecretInternals =
- ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
-const EventInternals = SecretInternals.Events;
-const getInstanceFromNode = EventInternals[0];
-const getNodeFromInstance = EventInternals[1];
-const getFiberCurrentPropsFromNode = EventInternals[2];
-const enqueueStateRestore = EventInternals[3];
-const restoreStateIfNeeded = EventInternals[4];
let didWarnAboutUsingAct = false;
-function act(callback) {
+export function act(callback) {
if (didWarnAboutUsingAct === false) {
didWarnAboutUsingAct = true;
console.error(
@@ -47,834 +21,3 @@ function act(callback) {
}
return React.act(callback);
}
-
-function Event(suffix) {}
-
-let hasWarnedAboutDeprecatedMockComponent = false;
-
-/**
- * @class ReactTestUtils
- */
-
-function findAllInRenderedFiberTreeInternal(fiber, test) {
- if (!fiber) {
- return [];
- }
- const currentParent = findCurrentFiberUsingSlowPath(fiber);
- if (!currentParent) {
- return [];
- }
- let node = currentParent;
- const ret = [];
- while (true) {
- if (
- node.tag === HostComponent ||
- node.tag === HostText ||
- node.tag === ClassComponent ||
- node.tag === FunctionComponent ||
- node.tag === HostHoistable ||
- node.tag === HostSingleton
- ) {
- const 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 (getInstance(inst)) {
- // This is a public instance indeed.
- return;
- }
- let received;
- const 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(
- `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) {
- if (disableDOMTestUtils) {
- throw new Error(
- '`renderIntoDocument` was removed from `react-dom/test-utils`. ' +
- 'See https://react.dev/warnings/react-dom-test-utils for more info.',
- );
- }
-
- const 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) {
- if (disableDOMTestUtils) {
- throw new Error(
- '`isElement` was removed from `react-dom/test-utils`. ' +
- 'See https://react.dev/warnings/react-dom-test-utils for more info.',
- );
- }
-
- return React.isValidElement(element);
-}
-
-function isElementOfType(inst, convenienceConstructor) {
- if (disableDOMTestUtils) {
- throw new Error(
- '`isElementOfType` was removed from `react-dom/test-utils`. ' +
- 'See https://react.dev/warnings/react-dom-test-utils for more info.',
- );
- }
-
- return React.isValidElement(inst) && inst.type === convenienceConstructor;
-}
-
-function isDOMComponent(inst) {
- if (disableDOMTestUtils) {
- throw new Error(
- '`isDOMComponent` was removed from `react-dom/test-utils`. ' +
- 'See https://react.dev/warnings/react-dom-test-utils for more info.',
- );
- }
-
- return !!(inst && inst.nodeType === ELEMENT_NODE && inst.tagName);
-}
-
-function isDOMComponentElement(inst) {
- if (disableDOMTestUtils) {
- throw new Error(
- '`isDOMComponentElement` was removed from `react-dom/test-utils`. ' +
- 'See https://react.dev/warnings/react-dom-test-utils for more info.',
- );
- }
-
- return !!(inst && React.isValidElement(inst) && !!inst.tagName);
-}
-
-function isCompositeComponent(inst) {
- if (disableDOMTestUtils) {
- throw new Error(
- '`isCompositeComponent` was removed from `react-dom/test-utils`. ' +
- 'See https://react.dev/warnings/react-dom-test-utils for more info.',
- );
- }
-
- 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 (disableDOMTestUtils) {
- throw new Error(
- '`isCompositeComponentWithType` was removed from `react-dom/test-utils`. ' +
- 'See https://react.dev/warnings/react-dom-test-utils for more info.',
- );
- }
-
- if (!isCompositeComponent(inst)) {
- return false;
- }
- const internalInstance = getInstance(inst);
- const constructor = internalInstance.type;
- return constructor === type;
-}
-
-function findAllInRenderedTree(inst, test) {
- if (disableDOMTestUtils) {
- throw new Error(
- '`findAllInRenderedTree` was removed from `react-dom/test-utils`. ' +
- 'See https://react.dev/warnings/react-dom-test-utils for more info.',
- );
- }
-
- validateClassInstance(inst, 'findAllInRenderedTree');
- if (!inst) {
- return [];
- }
- const internalInstance = getInstance(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) {
- if (disableDOMTestUtils) {
- throw new Error(
- '`scryRenderedDOMComponentsWithClass` was removed from `react-dom/test-utils`. ' +
- 'See https://react.dev/warnings/react-dom-test-utils for more info.',
- );
- }
-
- validateClassInstance(root, 'scryRenderedDOMComponentsWithClass');
- return findAllInRenderedTree(root, function (inst) {
- if (isDOMComponent(inst)) {
- let className = inst.className;
- if (typeof className !== 'string') {
- // SVG, probably.
- className = inst.getAttribute('class') || '';
- }
- const 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) {
- if (disableDOMTestUtils) {
- throw new Error(
- '`findRenderedDOMComponentWithClass` was removed from `react-dom/test-utils`. ' +
- 'See https://react.dev/warnings/react-dom-test-utils for more info.',
- );
- }
-
- validateClassInstance(root, 'findRenderedDOMComponentWithClass');
- const 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) {
- if (disableDOMTestUtils) {
- throw new Error(
- '`scryRenderedDOMComponentsWithTag` was removed from `react-dom/test-utils`. ' +
- 'See https://react.dev/warnings/react-dom-test-utils for more info.',
- );
- }
-
- 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) {
- if (disableDOMTestUtils) {
- throw new Error(
- '`findRenderedDOMComponentWithTag` was removed from `react-dom/test-utils`. ' +
- 'See https://react.dev/warnings/react-dom-test-utils for more info.',
- );
- }
-
- validateClassInstance(root, 'findRenderedDOMComponentWithTag');
- const 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) {
- if (disableDOMTestUtils) {
- throw new Error(
- '`scryRenderedComponentsWithType` was removed from `react-dom/test-utils`. ' +
- 'See https://react.dev/warnings/react-dom-test-utils for more info.',
- );
- }
-
- 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) {
- if (disableDOMTestUtils) {
- throw new Error(
- '`findRenderedComponentWithType` was removed from `react-dom/test-utils`. ' +
- 'See https://react.dev/warnings/react-dom-test-utils for more info.',
- );
- }
-
- validateClassInstance(root, 'findRenderedComponentWithType');
- const 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
- *
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 (disableDOMTestUtils) {
- throw new Error(
- '`mockComponent` was removed from `react-dom/test-utils`. ' +
- 'See https://react.dev/warnings/react-dom-test-utils for more info.',
- );
- }
-
- if (__DEV__) {
- if (!hasWarnedAboutDeprecatedMockComponent) {
- hasWarnedAboutDeprecatedMockComponent = true;
- console.warn(
- 'ReactTestUtils.mockComponent() is deprecated. ' +
- 'Use shallow rendering or jest.mock() instead.\n\n' +
- 'See https://react.dev/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) {
- if (disableDOMTestUtils) {
- throw new Error(
- '`nativeTouchData` was removed from `react-dom/test-utils`. ' +
- 'See https://react.dev/warnings/react-dom-test-utils for more info.',
- );
- }
-
- 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.
-
-let hasError: boolean = false;
-let caughtError: mixed = null;
-
-/**
- * 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) {
- event.currentTarget = getNodeFromInstance(inst);
- try {
- listener(event);
- } catch (error) {
- if (!hasError) {
- hasError = true;
- caughtError = error;
- }
- }
- event.currentTarget = null;
-}
-
-/**
- * Standard/simple iteration through an event's collected dispatches.
- */
-function executeDispatchesInOrder(event) {
- const dispatchListeners = event._dispatchListeners;
- const dispatchInstances = event._dispatchInstances;
- if (isArray(dispatchListeners)) {
- for (let 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.
- */
-export function traverseTwoPhase(inst, fn, arg) {
- const path = [];
- while (inst) {
- path.push(inst);
- inst = getParent(inst);
- }
- let 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: string) {
- // TODO: shouldPreventMouseEvent is DOM-specific and definitely should not
- // live here; needs to be moved to a better place soon
- const stateNode = inst.stateNode;
- if (!stateNode) {
- // Work in progress (ex: onload events in incremental mode).
- return null;
- }
- const props = getFiberCurrentPropsFromNode(stateNode);
- if (!props) {
- // Work in progress.
- return null;
- }
- const 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: PropagationPhases) {
- let registrationName = event._reactName;
- if (propagationPhase === 'captured') {
- registrationName += 'Capture';
- }
- return getListener(inst, registrationName);
-}
-
-function accumulateDispatches(inst, ignoredDirection, event) {
- if (inst && event && event._reactName) {
- const registrationName = event._reactName;
- const 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 (__DEV__) {
- if (!inst) {
- console.error('Dispatching inst must not be null');
- }
- }
- const 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
-
-const Simulate = {};
-
-const 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 (disableDOMTestUtils) {
- throw new Error(
- '`Simulate` was removed from `react-dom/test-utils`. ' +
- 'See https://react.dev/warnings/react-dom-test-utils for more info.',
- );
- }
-
- 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.',
- );
- }
-
- const reactName = 'on' + eventType[0].toUpperCase() + eventType.slice(1);
- const fakeNativeEvent = new Event();
- fakeNativeEvent.target = domNode;
- fakeNativeEvent.type = eventType.toLowerCase();
-
- const targetInst = getInstanceFromNode(domNode);
- const 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);
- if (hasError) {
- const error = caughtError;
- hasError = false;
- caughtError = null;
- throw error;
- }
- });
- restoreStateIfNeeded();
- };
-}
-
-// A one-time snapshot with no plans to update. We'll probably want to deprecate Simulate API.
-const 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(eventType => {
- Simulate[eventType] = makeSimulator(eventType);
- });
-}
-buildSimulators();
-
-export {
- renderIntoDocument,
- isElement,
- isElementOfType,
- isDOMComponent,
- isDOMComponentElement,
- isCompositeComponent,
- isCompositeComponentWithType,
- findAllInRenderedTree,
- scryRenderedDOMComponentsWithClass,
- findRenderedDOMComponentWithClass,
- scryRenderedDOMComponentsWithTag,
- findRenderedDOMComponentWithTag,
- scryRenderedComponentsWithType,
- findRenderedComponentWithType,
- mockComponent,
- nativeTouchData,
- Simulate,
- act,
-};
diff --git a/packages/react-dom/src/test-utils/ReactTestUtilsFB.js b/packages/react-dom/src/test-utils/ReactTestUtilsFB.js
new file mode 100644
index 0000000000000..1b4125c819e61
--- /dev/null
+++ b/packages/react-dom/src/test-utils/ReactTestUtilsFB.js
@@ -0,0 +1,880 @@
+/**
+ * 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
+ */
+
+import * as React from 'react';
+import * as ReactDOM from 'react-dom';
+import {findCurrentFiberUsingSlowPath} from 'react-reconciler/src/ReactFiberTreeReflection';
+import {get as getInstance} from 'shared/ReactInstanceMap';
+import {
+ ClassComponent,
+ FunctionComponent,
+ HostComponent,
+ HostHoistable,
+ HostSingleton,
+ HostText,
+} from 'react-reconciler/src/ReactWorkTags';
+import {SyntheticEvent} from 'react-dom-bindings/src/events/SyntheticEvent';
+import {ELEMENT_NODE} from 'react-dom-bindings/src/client/HTMLNodeType';
+import {disableDOMTestUtils} from 'shared/ReactFeatureFlags';
+import assign from 'shared/assign';
+import isArray from 'shared/isArray';
+
+// Keep in sync with ReactDOM.js:
+const SecretInternals =
+ ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+const EventInternals = SecretInternals.Events;
+const getInstanceFromNode = EventInternals[0];
+const getNodeFromInstance = EventInternals[1];
+const getFiberCurrentPropsFromNode = EventInternals[2];
+const enqueueStateRestore = EventInternals[3];
+const restoreStateIfNeeded = EventInternals[4];
+
+let didWarnAboutUsingAct = false;
+function act(callback) {
+ if (didWarnAboutUsingAct === false) {
+ didWarnAboutUsingAct = true;
+ console.error(
+ '`ReactDOMTestUtils.act` is deprecated in favor of `React.act`. ' +
+ 'Import `act` from `react` instead of `react-dom/test-utils`. ' +
+ 'See https://react.dev/warnings/react-dom-test-utils for more info.',
+ );
+ }
+ return React.act(callback);
+}
+
+function Event(suffix) {}
+
+let hasWarnedAboutDeprecatedMockComponent = false;
+
+/**
+ * @class ReactTestUtils
+ */
+
+function findAllInRenderedFiberTreeInternal(fiber, test) {
+ if (!fiber) {
+ return [];
+ }
+ const currentParent = findCurrentFiberUsingSlowPath(fiber);
+ if (!currentParent) {
+ return [];
+ }
+ let node = currentParent;
+ const ret = [];
+ while (true) {
+ if (
+ node.tag === HostComponent ||
+ node.tag === HostText ||
+ node.tag === ClassComponent ||
+ node.tag === FunctionComponent ||
+ node.tag === HostHoistable ||
+ node.tag === HostSingleton
+ ) {
+ const 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 (getInstance(inst)) {
+ // This is a public instance indeed.
+ return;
+ }
+ let received;
+ const 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(
+ `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) {
+ if (disableDOMTestUtils) {
+ throw new Error(
+ '`renderIntoDocument` was removed from `react-dom/test-utils`. ' +
+ 'See https://react.dev/warnings/react-dom-test-utils for more info.',
+ );
+ }
+
+ const 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) {
+ if (disableDOMTestUtils) {
+ throw new Error(
+ '`isElement` was removed from `react-dom/test-utils`. ' +
+ 'See https://react.dev/warnings/react-dom-test-utils for more info.',
+ );
+ }
+
+ return React.isValidElement(element);
+}
+
+function isElementOfType(inst, convenienceConstructor) {
+ if (disableDOMTestUtils) {
+ throw new Error(
+ '`isElementOfType` was removed from `react-dom/test-utils`. ' +
+ 'See https://react.dev/warnings/react-dom-test-utils for more info.',
+ );
+ }
+
+ return React.isValidElement(inst) && inst.type === convenienceConstructor;
+}
+
+function isDOMComponent(inst) {
+ if (disableDOMTestUtils) {
+ throw new Error(
+ '`isDOMComponent` was removed from `react-dom/test-utils`. ' +
+ 'See https://react.dev/warnings/react-dom-test-utils for more info.',
+ );
+ }
+
+ return !!(inst && inst.nodeType === ELEMENT_NODE && inst.tagName);
+}
+
+function isDOMComponentElement(inst) {
+ if (disableDOMTestUtils) {
+ throw new Error(
+ '`isDOMComponentElement` was removed from `react-dom/test-utils`. ' +
+ 'See https://react.dev/warnings/react-dom-test-utils for more info.',
+ );
+ }
+
+ return !!(inst && React.isValidElement(inst) && !!inst.tagName);
+}
+
+function isCompositeComponent(inst) {
+ if (disableDOMTestUtils) {
+ throw new Error(
+ '`isCompositeComponent` was removed from `react-dom/test-utils`. ' +
+ 'See https://react.dev/warnings/react-dom-test-utils for more info.',
+ );
+ }
+
+ 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 (disableDOMTestUtils) {
+ throw new Error(
+ '`isCompositeComponentWithType` was removed from `react-dom/test-utils`. ' +
+ 'See https://react.dev/warnings/react-dom-test-utils for more info.',
+ );
+ }
+
+ if (!isCompositeComponent(inst)) {
+ return false;
+ }
+ const internalInstance = getInstance(inst);
+ const constructor = internalInstance.type;
+ return constructor === type;
+}
+
+function findAllInRenderedTree(inst, test) {
+ if (disableDOMTestUtils) {
+ throw new Error(
+ '`findAllInRenderedTree` was removed from `react-dom/test-utils`. ' +
+ 'See https://react.dev/warnings/react-dom-test-utils for more info.',
+ );
+ }
+
+ validateClassInstance(inst, 'findAllInRenderedTree');
+ if (!inst) {
+ return [];
+ }
+ const internalInstance = getInstance(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) {
+ if (disableDOMTestUtils) {
+ throw new Error(
+ '`scryRenderedDOMComponentsWithClass` was removed from `react-dom/test-utils`. ' +
+ 'See https://react.dev/warnings/react-dom-test-utils for more info.',
+ );
+ }
+
+ validateClassInstance(root, 'scryRenderedDOMComponentsWithClass');
+ return findAllInRenderedTree(root, function (inst) {
+ if (isDOMComponent(inst)) {
+ let className = inst.className;
+ if (typeof className !== 'string') {
+ // SVG, probably.
+ className = inst.getAttribute('class') || '';
+ }
+ const 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) {
+ if (disableDOMTestUtils) {
+ throw new Error(
+ '`findRenderedDOMComponentWithClass` was removed from `react-dom/test-utils`. ' +
+ 'See https://react.dev/warnings/react-dom-test-utils for more info.',
+ );
+ }
+
+ validateClassInstance(root, 'findRenderedDOMComponentWithClass');
+ const 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) {
+ if (disableDOMTestUtils) {
+ throw new Error(
+ '`scryRenderedDOMComponentsWithTag` was removed from `react-dom/test-utils`. ' +
+ 'See https://react.dev/warnings/react-dom-test-utils for more info.',
+ );
+ }
+
+ 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) {
+ if (disableDOMTestUtils) {
+ throw new Error(
+ '`findRenderedDOMComponentWithTag` was removed from `react-dom/test-utils`. ' +
+ 'See https://react.dev/warnings/react-dom-test-utils for more info.',
+ );
+ }
+
+ validateClassInstance(root, 'findRenderedDOMComponentWithTag');
+ const 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) {
+ if (disableDOMTestUtils) {
+ throw new Error(
+ '`scryRenderedComponentsWithType` was removed from `react-dom/test-utils`. ' +
+ 'See https://react.dev/warnings/react-dom-test-utils for more info.',
+ );
+ }
+
+ 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) {
+ if (disableDOMTestUtils) {
+ throw new Error(
+ '`findRenderedComponentWithType` was removed from `react-dom/test-utils`. ' +
+ 'See https://react.dev/warnings/react-dom-test-utils for more info.',
+ );
+ }
+
+ validateClassInstance(root, 'findRenderedComponentWithType');
+ const 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
+ *
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 (disableDOMTestUtils) {
+ throw new Error(
+ '`mockComponent` was removed from `react-dom/test-utils`. ' +
+ 'See https://react.dev/warnings/react-dom-test-utils for more info.',
+ );
+ }
+
+ if (__DEV__) {
+ if (!hasWarnedAboutDeprecatedMockComponent) {
+ hasWarnedAboutDeprecatedMockComponent = true;
+ console.warn(
+ 'ReactTestUtils.mockComponent() is deprecated. ' +
+ 'Use shallow rendering or jest.mock() instead.\n\n' +
+ 'See https://react.dev/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) {
+ if (disableDOMTestUtils) {
+ throw new Error(
+ '`nativeTouchData` was removed from `react-dom/test-utils`. ' +
+ 'See https://react.dev/warnings/react-dom-test-utils for more info.',
+ );
+ }
+
+ 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.
+
+let hasError: boolean = false;
+let caughtError: mixed = null;
+
+/**
+ * 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) {
+ event.currentTarget = getNodeFromInstance(inst);
+ try {
+ listener(event);
+ } catch (error) {
+ if (!hasError) {
+ hasError = true;
+ caughtError = error;
+ }
+ }
+ event.currentTarget = null;
+}
+
+/**
+ * Standard/simple iteration through an event's collected dispatches.
+ */
+function executeDispatchesInOrder(event) {
+ const dispatchListeners = event._dispatchListeners;
+ const dispatchInstances = event._dispatchInstances;
+ if (isArray(dispatchListeners)) {
+ for (let 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.
+ */
+export function traverseTwoPhase(inst, fn, arg) {
+ const path = [];
+ while (inst) {
+ path.push(inst);
+ inst = getParent(inst);
+ }
+ let 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: string) {
+ // TODO: shouldPreventMouseEvent is DOM-specific and definitely should not
+ // live here; needs to be moved to a better place soon
+ const stateNode = inst.stateNode;
+ if (!stateNode) {
+ // Work in progress (ex: onload events in incremental mode).
+ return null;
+ }
+ const props = getFiberCurrentPropsFromNode(stateNode);
+ if (!props) {
+ // Work in progress.
+ return null;
+ }
+ const 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: PropagationPhases) {
+ let registrationName = event._reactName;
+ if (propagationPhase === 'captured') {
+ registrationName += 'Capture';
+ }
+ return getListener(inst, registrationName);
+}
+
+function accumulateDispatches(inst, ignoredDirection, event) {
+ if (inst && event && event._reactName) {
+ const registrationName = event._reactName;
+ const 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 (__DEV__) {
+ if (!inst) {
+ console.error('Dispatching inst must not be null');
+ }
+ }
+ const 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
+
+const Simulate = {};
+
+const 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 (disableDOMTestUtils) {
+ throw new Error(
+ '`Simulate` was removed from `react-dom/test-utils`. ' +
+ 'See https://react.dev/warnings/react-dom-test-utils for more info.',
+ );
+ }
+
+ 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.',
+ );
+ }
+
+ const reactName = 'on' + eventType[0].toUpperCase() + eventType.slice(1);
+ const fakeNativeEvent = new Event();
+ fakeNativeEvent.target = domNode;
+ fakeNativeEvent.type = eventType.toLowerCase();
+
+ const targetInst = getInstanceFromNode(domNode);
+ const 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);
+ if (hasError) {
+ const error = caughtError;
+ hasError = false;
+ caughtError = null;
+ throw error;
+ }
+ });
+ restoreStateIfNeeded();
+ };
+}
+
+// A one-time snapshot with no plans to update. We'll probably want to deprecate Simulate API.
+const 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(eventType => {
+ Simulate[eventType] = makeSimulator(eventType);
+ });
+}
+buildSimulators();
+
+export {
+ renderIntoDocument,
+ isElement,
+ isElementOfType,
+ isDOMComponent,
+ isDOMComponentElement,
+ isCompositeComponent,
+ isCompositeComponentWithType,
+ findAllInRenderedTree,
+ scryRenderedDOMComponentsWithClass,
+ findRenderedDOMComponentWithClass,
+ scryRenderedDOMComponentsWithTag,
+ findRenderedDOMComponentWithTag,
+ scryRenderedComponentsWithType,
+ findRenderedComponentWithType,
+ mockComponent,
+ nativeTouchData,
+ Simulate,
+ act,
+};
diff --git a/packages/react-dom/test-utils.fb.js b/packages/react-dom/test-utils.fb.js
new file mode 100644
index 0000000000000..dc43cbed3fcbd
--- /dev/null
+++ b/packages/react-dom/test-utils.fb.js
@@ -0,0 +1,10 @@
+/**
+ * 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.
+ *
+ * @flow
+ */
+
+export * from './src/test-utils/ReactTestUtilsFB';
diff --git a/scripts/shared/inlinedHostConfigs.js b/scripts/shared/inlinedHostConfigs.js
index fa6916cee621b..63ec4db4dbe39 100644
--- a/scripts/shared/inlinedHostConfigs.js
+++ b/scripts/shared/inlinedHostConfigs.js
@@ -15,6 +15,7 @@ module.exports = [
'react-dom/unstable_testing',
'react-dom/src/server/react-dom-server.node.js',
'react-dom/static.node',
+ 'react-dom/test-utils',
'react-dom/server-rendering-stub',
'react-dom/unstable_server-external-runtime',
'react-server-dom-webpack/server.node.unbundled',
@@ -29,6 +30,7 @@ module.exports = [
'react-dom/server.node',
'react-dom/static',
'react-dom/static.node',
+ 'react-dom/test-utils',
'react-dom/src/server/react-dom-server.node',
'react-dom/src/server/ReactDOMFizzServerNode.js', // react-dom/server.node
'react-dom/src/server/ReactDOMFizzStaticNode.js',