mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[TestUtils] Build limited test-utils (#28782)
We landed a flag to disable test utils in many builds but we need to fork the entrypoint to make it work with tests properly. This also removes test-utils implementations from builds that do not support it. Currently in OSS builds the only thing in test-utils is a reexport of `act`
This commit is contained in:
+103
-22
@@ -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(() => {
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
`;
|
||||
+1
-858
@@ -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
|
||||
* <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 (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,
|
||||
};
|
||||
|
||||
@@ -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
|
||||
* <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 (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,
|
||||
};
|
||||
@@ -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';
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user