mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Modern Event System: Remove TestUtils.SimulateNative support (#18471)
This commit is contained in:
+92
-18
@@ -15,6 +15,8 @@ import * as ReactDOM from 'react-dom';
|
||||
import * as ReactDOMServer from 'react-dom/server';
|
||||
import * as ReactTestUtils from 'react-dom/test-utils';
|
||||
|
||||
const ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
|
||||
function getTestDocument(markup) {
|
||||
const doc = document.implementation.createHTMLDocument('');
|
||||
doc.open();
|
||||
@@ -31,25 +33,97 @@ describe('ReactTestUtils', () => {
|
||||
expect(Object.keys(ReactTestUtils.Simulate).sort()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('SimulateNative should have locally attached media events', () => {
|
||||
expect(Object.keys(ReactTestUtils.SimulateNative).sort()).toMatchSnapshot();
|
||||
});
|
||||
if (!ReactFeatureFlags.enableModernEventSystem) {
|
||||
// SimulateNative API has been removed in the modern event system
|
||||
it('SimulateNative should have locally attached media events', () => {
|
||||
expect(Object.keys(ReactTestUtils.SimulateNative).sort()).toEqual([
|
||||
'abort',
|
||||
'animationEnd',
|
||||
'animationIteration',
|
||||
'animationStart',
|
||||
'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',
|
||||
'input',
|
||||
'keyDown',
|
||||
'keyPress',
|
||||
'keyUp',
|
||||
'load',
|
||||
'loadStart',
|
||||
'loadedData',
|
||||
'loadedMetadata',
|
||||
'mouseDown',
|
||||
'mouseMove',
|
||||
'mouseOut',
|
||||
'mouseOver',
|
||||
'mouseUp',
|
||||
'paste',
|
||||
'pause',
|
||||
'play',
|
||||
'playing',
|
||||
'progress',
|
||||
'rateChange',
|
||||
'scroll',
|
||||
'seeked',
|
||||
'seeking',
|
||||
'selectionChange',
|
||||
'stalled',
|
||||
'suspend',
|
||||
'textInput',
|
||||
'timeUpdate',
|
||||
'toggle',
|
||||
'touchCancel',
|
||||
'touchEnd',
|
||||
'touchMove',
|
||||
'touchStart',
|
||||
'transitionEnd',
|
||||
'volumeChange',
|
||||
'waiting',
|
||||
'wheel',
|
||||
]);
|
||||
});
|
||||
|
||||
it('SimulateNative should warn about deprecation', () => {
|
||||
const container = document.createElement('div');
|
||||
const node = ReactDOM.render(<div />, container);
|
||||
expect(() =>
|
||||
ReactTestUtils.SimulateNative.click(node),
|
||||
).toWarnDev(
|
||||
'ReactTestUtils.SimulateNative is an undocumented API that does not match ' +
|
||||
'how the browser dispatches events, and will be removed in a future major ' +
|
||||
'version of React. If you rely on it for testing, consider attaching the root ' +
|
||||
'DOM container to the document during the test, and then dispatching native browser ' +
|
||||
'events by calling `node.dispatchEvent()` on the DOM nodes. Make sure to set ' +
|
||||
'the `bubbles` flag to `true` when creating the native browser event.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
});
|
||||
it('SimulateNative should warn about deprecation', () => {
|
||||
const container = document.createElement('div');
|
||||
const node = ReactDOM.render(<div />, container);
|
||||
expect(() =>
|
||||
ReactTestUtils.SimulateNative.click(node),
|
||||
).toWarnDev(
|
||||
'ReactTestUtils.SimulateNative is an undocumented API that does not match ' +
|
||||
'how the browser dispatches events, and will be removed in a future major ' +
|
||||
'version of React. If you rely on it for testing, consider attaching the root ' +
|
||||
'DOM container to the document during the test, and then dispatching native browser ' +
|
||||
'events by calling `node.dispatchEvent()` on the DOM nodes. Make sure to set ' +
|
||||
'the `bubbles` flag to `true` when creating the native browser event.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
it('gives Jest mocks a passthrough implementation with mockComponent()', () => {
|
||||
class MockedComponent extends React.Component {
|
||||
|
||||
@@ -88,76 +88,3 @@ Array [
|
||||
"wheel",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`ReactTestUtils SimulateNative should have locally attached media events 1`] = `
|
||||
Array [
|
||||
"abort",
|
||||
"animationEnd",
|
||||
"animationIteration",
|
||||
"animationStart",
|
||||
"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",
|
||||
"input",
|
||||
"keyDown",
|
||||
"keyPress",
|
||||
"keyUp",
|
||||
"load",
|
||||
"loadStart",
|
||||
"loadedData",
|
||||
"loadedMetadata",
|
||||
"mouseDown",
|
||||
"mouseMove",
|
||||
"mouseOut",
|
||||
"mouseOver",
|
||||
"mouseUp",
|
||||
"paste",
|
||||
"pause",
|
||||
"play",
|
||||
"playing",
|
||||
"progress",
|
||||
"rateChange",
|
||||
"scroll",
|
||||
"seeked",
|
||||
"seeking",
|
||||
"selectionChange",
|
||||
"stalled",
|
||||
"suspend",
|
||||
"textInput",
|
||||
"timeUpdate",
|
||||
"toggle",
|
||||
"touchCancel",
|
||||
"touchEnd",
|
||||
"touchMove",
|
||||
"touchStart",
|
||||
"transitionEnd",
|
||||
"volumeChange",
|
||||
"waiting",
|
||||
"wheel",
|
||||
]
|
||||
`;
|
||||
|
||||
+73
-78
@@ -218,7 +218,7 @@ function dispatchEventsForPlugins(
|
||||
eventSystemFlags: EventSystemFlags,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
targetInst: null | Fiber,
|
||||
targetContainer: null | EventTarget,
|
||||
targetContainer: EventTarget,
|
||||
): void {
|
||||
const nativeEventTarget = getEventTarget(nativeEvent);
|
||||
const syntheticEvents: Array<ReactSyntheticEvent> = [];
|
||||
@@ -395,90 +395,85 @@ export function dispatchEventForPluginEventSystem(
|
||||
eventSystemFlags: EventSystemFlags,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
targetInst: null | Fiber,
|
||||
targetContainer: null | EventTarget,
|
||||
targetContainer: EventTarget,
|
||||
): void {
|
||||
let ancestorInst = targetInst;
|
||||
if (targetContainer !== null) {
|
||||
if (eventSystemFlags & IS_TARGET_PHASE_ONLY) {
|
||||
// For TargetEvent nodes (i.e. document, window)
|
||||
ancestorInst = null;
|
||||
} else {
|
||||
const targetContainerNode = ((targetContainer: any): Node);
|
||||
if (eventSystemFlags & IS_TARGET_PHASE_ONLY) {
|
||||
// For TargetEvent nodes (i.e. document, window)
|
||||
ancestorInst = null;
|
||||
} else {
|
||||
const targetContainerNode = ((targetContainer: any): Node);
|
||||
|
||||
// If we are using the legacy FB support flag, we
|
||||
// defer the event to the null with a one
|
||||
// time event listener so we can defer the event.
|
||||
if (
|
||||
enableLegacyFBSupport &&
|
||||
// We do not want to defer if the event system has already been
|
||||
// set to LEGACY_FB_SUPPORT. LEGACY_FB_SUPPORT only gets set when
|
||||
// we call willDeferLaterForLegacyFBSupport, thus not bailing out
|
||||
// will result in endless cycles like an infinite loop.
|
||||
(eventSystemFlags & LEGACY_FB_SUPPORT) === 0 &&
|
||||
// We also don't want to defer during event replaying.
|
||||
(eventSystemFlags & IS_REPLAYED) === 0 &&
|
||||
// We don't want to apply the legacy FB support for the useEvent API.
|
||||
(eventSystemFlags & USE_EVENT_SYSTEM) === 0 &&
|
||||
willDeferLaterForLegacyFBSupport(topLevelType, targetContainer)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (targetInst !== null) {
|
||||
// The below logic attempts to work out if we need to change
|
||||
// the target fiber to a different ancestor. We had similar logic
|
||||
// in the legacy event system, except the big difference between
|
||||
// systems is that the modern event system now has an event listener
|
||||
// attached to each React Root and React Portal Root. Together,
|
||||
// the DOM nodes representing these roots are the "rootContainer".
|
||||
// To figure out which ancestor instance we should use, we traverse
|
||||
// up the fiber tree from the target instance and attempt to find
|
||||
// root boundaries that match that of our current "rootContainer".
|
||||
// If we find that "rootContainer", we find the parent fiber
|
||||
// sub-tree for that root and make that our ancestor instance.
|
||||
let node = targetInst;
|
||||
// If we are using the legacy FB support flag, we
|
||||
// defer the event to the null with a one
|
||||
// time event listener so we can defer the event.
|
||||
if (
|
||||
enableLegacyFBSupport &&
|
||||
// We do not want to defer if the event system has already been
|
||||
// set to LEGACY_FB_SUPPORT. LEGACY_FB_SUPPORT only gets set when
|
||||
// we call willDeferLaterForLegacyFBSupport, thus not bailing out
|
||||
// will result in endless cycles like an infinite loop.
|
||||
(eventSystemFlags & LEGACY_FB_SUPPORT) === 0 &&
|
||||
// We also don't want to defer during event replaying.
|
||||
(eventSystemFlags & IS_REPLAYED) === 0 &&
|
||||
// We don't want to apply the legacy FB support for the useEvent API.
|
||||
(eventSystemFlags & USE_EVENT_SYSTEM) === 0 &&
|
||||
willDeferLaterForLegacyFBSupport(topLevelType, targetContainer)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (targetInst !== null) {
|
||||
// The below logic attempts to work out if we need to change
|
||||
// the target fiber to a different ancestor. We had similar logic
|
||||
// in the legacy event system, except the big difference between
|
||||
// systems is that the modern event system now has an event listener
|
||||
// attached to each React Root and React Portal Root. Together,
|
||||
// the DOM nodes representing these roots are the "rootContainer".
|
||||
// To figure out which ancestor instance we should use, we traverse
|
||||
// up the fiber tree from the target instance and attempt to find
|
||||
// root boundaries that match that of our current "rootContainer".
|
||||
// If we find that "rootContainer", we find the parent fiber
|
||||
// sub-tree for that root and make that our ancestor instance.
|
||||
let node = targetInst;
|
||||
|
||||
while (true) {
|
||||
if (node === null) {
|
||||
while (true) {
|
||||
if (node === null) {
|
||||
return;
|
||||
}
|
||||
if (node.tag === HostRoot || node.tag === HostPortal) {
|
||||
const container = node.stateNode.containerInfo;
|
||||
if (isMatchingRootContainer(container, targetContainerNode)) {
|
||||
break;
|
||||
}
|
||||
if (node.tag === HostPortal) {
|
||||
// The target is a portal, but it's not the rootContainer we're looking for.
|
||||
// Normally portals handle their own events all the way down to the root.
|
||||
// So we should be able to stop now. However, we don't know if this portal
|
||||
// was part of *our* root.
|
||||
let grandNode = node.return;
|
||||
while (grandNode !== null) {
|
||||
if (grandNode.tag === HostRoot || grandNode.tag === HostPortal) {
|
||||
const grandContainer = grandNode.stateNode.containerInfo;
|
||||
if (
|
||||
isMatchingRootContainer(grandContainer, targetContainerNode)
|
||||
) {
|
||||
// This is the rootContainer we're looking for and we found it as
|
||||
// a parent of the Portal. That means we can ignore it because the
|
||||
// Portal will bubble through to us.
|
||||
return;
|
||||
}
|
||||
}
|
||||
grandNode = grandNode.return;
|
||||
}
|
||||
}
|
||||
const parentSubtreeInst = getClosestInstanceFromNode(container);
|
||||
if (parentSubtreeInst === null) {
|
||||
return;
|
||||
}
|
||||
if (node.tag === HostRoot || node.tag === HostPortal) {
|
||||
const container = node.stateNode.containerInfo;
|
||||
if (isMatchingRootContainer(container, targetContainerNode)) {
|
||||
break;
|
||||
}
|
||||
if (node.tag === HostPortal) {
|
||||
// The target is a portal, but it's not the rootContainer we're looking for.
|
||||
// Normally portals handle their own events all the way down to the root.
|
||||
// So we should be able to stop now. However, we don't know if this portal
|
||||
// was part of *our* root.
|
||||
let grandNode = node.return;
|
||||
while (grandNode !== null) {
|
||||
if (
|
||||
grandNode.tag === HostRoot ||
|
||||
grandNode.tag === HostPortal
|
||||
) {
|
||||
const grandContainer = grandNode.stateNode.containerInfo;
|
||||
if (
|
||||
isMatchingRootContainer(grandContainer, targetContainerNode)
|
||||
) {
|
||||
// This is the rootContainer we're looking for and we found it as
|
||||
// a parent of the Portal. That means we can ignore it because the
|
||||
// Portal will bubble through to us.
|
||||
return;
|
||||
}
|
||||
}
|
||||
grandNode = grandNode.return;
|
||||
}
|
||||
}
|
||||
const parentSubtreeInst = getClosestInstanceFromNode(container);
|
||||
if (parentSubtreeInst === null) {
|
||||
return;
|
||||
}
|
||||
node = ancestorInst = parentSubtreeInst;
|
||||
continue;
|
||||
}
|
||||
node = node.return;
|
||||
node = ancestorInst = parentSubtreeInst;
|
||||
continue;
|
||||
}
|
||||
node = node.return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-12
@@ -165,16 +165,6 @@ export function addTrappedEventListener(
|
||||
targetContainer,
|
||||
);
|
||||
|
||||
// When the targetContainer is null, it means that the container
|
||||
// target is null, but really we need a real DOM node to attach to.
|
||||
// In this case, we fallback to the "document" node, but leave the
|
||||
// targetContainer (which is bound in the above function) to null.
|
||||
// Really, this only happens for TestUtils.Simulate, so when we
|
||||
// remove that support, we can remove this block of code.
|
||||
if (targetContainer === null) {
|
||||
targetContainer = document;
|
||||
}
|
||||
|
||||
targetContainer =
|
||||
enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport
|
||||
? (targetContainer: any).ownerDocument
|
||||
@@ -299,7 +289,7 @@ function dispatchUserBlockingUpdate(
|
||||
export function dispatchEvent(
|
||||
topLevelType: DOMTopLevelEventType,
|
||||
eventSystemFlags: EventSystemFlags,
|
||||
targetContainer: null | EventTarget,
|
||||
targetContainer: EventTarget,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
): void {
|
||||
if (!_enabled) {
|
||||
@@ -415,7 +405,7 @@ export function dispatchEvent(
|
||||
export function attemptToDispatchEvent(
|
||||
topLevelType: DOMTopLevelEventType,
|
||||
eventSystemFlags: EventSystemFlags,
|
||||
targetContainer: EventTarget | null,
|
||||
targetContainer: EventTarget,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
): null | Container | SuspenseInstance {
|
||||
// TODO: Warn if _enabled is false.
|
||||
|
||||
+5
-5
@@ -291,7 +291,7 @@ function createQueuedReplayableEvent(
|
||||
blockedOn: null | Container | SuspenseInstance,
|
||||
topLevelType: DOMTopLevelEventType,
|
||||
eventSystemFlags: EventSystemFlags,
|
||||
targetContainer: EventTarget | null,
|
||||
targetContainer: EventTarget,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
): QueuedReplayableEvent {
|
||||
return {
|
||||
@@ -299,7 +299,7 @@ function createQueuedReplayableEvent(
|
||||
topLevelType,
|
||||
eventSystemFlags: eventSystemFlags | IS_REPLAYED,
|
||||
nativeEvent,
|
||||
targetContainers: targetContainer !== null ? [targetContainer] : [],
|
||||
targetContainers: [targetContainer],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -307,7 +307,7 @@ export function queueDiscreteEvent(
|
||||
blockedOn: null | Container | SuspenseInstance,
|
||||
topLevelType: DOMTopLevelEventType,
|
||||
eventSystemFlags: EventSystemFlags,
|
||||
targetContainer: EventTarget | null,
|
||||
targetContainer: EventTarget,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
): void {
|
||||
const queuedEvent = createQueuedReplayableEvent(
|
||||
@@ -382,7 +382,7 @@ function accumulateOrCreateContinuousQueuedReplayableEvent(
|
||||
blockedOn: null | Container | SuspenseInstance,
|
||||
topLevelType: DOMTopLevelEventType,
|
||||
eventSystemFlags: EventSystemFlags,
|
||||
targetContainer: EventTarget | null,
|
||||
targetContainer: EventTarget,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
): QueuedReplayableEvent {
|
||||
if (
|
||||
@@ -424,7 +424,7 @@ export function queueIfContinuousEvent(
|
||||
blockedOn: null | Container | SuspenseInstance,
|
||||
topLevelType: DOMTopLevelEventType,
|
||||
eventSystemFlags: EventSystemFlags,
|
||||
targetContainer: EventTarget | null,
|
||||
targetContainer: EventTarget,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
): boolean {
|
||||
// These set relatedTarget to null because the replayed event will be treated as if we
|
||||
|
||||
+161
-152
@@ -22,6 +22,7 @@ import * as DOMTopLevelEventTypes from '../events/DOMTopLevelEventTypes';
|
||||
import act from './ReactTestUtilsAct';
|
||||
import forEachAccumulated from 'legacy-events/forEachAccumulated';
|
||||
import accumulateInto from 'legacy-events/accumulateInto';
|
||||
import {enableModernEventSystem} from 'shared/ReactFeatureFlags';
|
||||
|
||||
const {findDOMNode} = ReactDOM;
|
||||
// Keep in sync with ReactDOMUnstableNativeDependencies.js
|
||||
@@ -53,34 +54,6 @@ let didWarnSimulateNativeDeprecated = false;
|
||||
* @class ReactTestUtils
|
||||
*/
|
||||
|
||||
/**
|
||||
* Simulates a top level event being dispatched from a raw event that occurred
|
||||
* on an `Element` node.
|
||||
* @param {number} topLevelType A number from `TopLevelEventTypes`
|
||||
* @param {!Element} node The dom to simulate an event occurring on.
|
||||
* @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
|
||||
*/
|
||||
function simulateNativeEventOnNode(topLevelType, node, fakeNativeEvent) {
|
||||
fakeNativeEvent.target = node;
|
||||
const PLUGIN_EVENT_SYSTEM = 1;
|
||||
dispatchEvent(topLevelType, PLUGIN_EVENT_SYSTEM, null, fakeNativeEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates a top level event being dispatched from a raw event that occurred
|
||||
* on the `ReactDOMComponent` `comp`.
|
||||
* @param {Object} topLevelType A type from `BrowserEventConstants.topLevelTypes`.
|
||||
* @param {!ReactDOMComponent} comp
|
||||
* @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
|
||||
*/
|
||||
function simulateNativeEventOnDOMComponent(
|
||||
topLevelType,
|
||||
comp,
|
||||
fakeNativeEvent,
|
||||
) {
|
||||
simulateNativeEventOnNode(topLevelType, findDOMNode(comp), fakeNativeEvent);
|
||||
}
|
||||
|
||||
function findAllInRenderedFiberTreeInternal(fiber, test) {
|
||||
if (!fiber) {
|
||||
return [];
|
||||
@@ -531,7 +504,7 @@ function accumulateTwoPhaseDispatches(events) {
|
||||
// End of inline
|
||||
|
||||
const Simulate = {};
|
||||
const SimulateNative = {};
|
||||
let SimulateNative;
|
||||
|
||||
/**
|
||||
* Exports:
|
||||
@@ -605,134 +578,170 @@ function buildSimulators() {
|
||||
|
||||
buildSimulators();
|
||||
|
||||
/**
|
||||
* Exports:
|
||||
*
|
||||
* - `SimulateNative.click(Element/ReactDOMComponent)`
|
||||
* - `SimulateNative.mouseMove(Element/ReactDOMComponent)`
|
||||
* - `SimulateNative.mouseIn/ReactDOMComponent)`
|
||||
* - `SimulateNative.mouseOut(Element/ReactDOMComponent)`
|
||||
* - ... (All keys from `BrowserEventConstants.topLevelTypes`)
|
||||
*
|
||||
* Note: Top level event types are a subset of the entire set of handler types
|
||||
* (which include a broader set of "synthetic" events). For example, onDragDone
|
||||
* is a synthetic event. Except when testing an event plugin or React's event
|
||||
* handling code specifically, you probably want to use Simulate
|
||||
* to dispatch synthetic events.
|
||||
*/
|
||||
if (!enableModernEventSystem) {
|
||||
SimulateNative = {};
|
||||
|
||||
function makeNativeSimulator(eventType, topLevelType) {
|
||||
return function(domComponentOrNode, nativeEventData) {
|
||||
if (__DEV__) {
|
||||
if (!didWarnSimulateNativeDeprecated) {
|
||||
didWarnSimulateNativeDeprecated = true;
|
||||
console.warn(
|
||||
'ReactTestUtils.SimulateNative is an undocumented API that does not match ' +
|
||||
'how the browser dispatches events, and will be removed in a future major ' +
|
||||
'version of React. If you rely on it for testing, consider attaching the root ' +
|
||||
'DOM container to the document during the test, and then dispatching native browser ' +
|
||||
'events by calling `node.dispatchEvent()` on the DOM nodes. Make sure to set ' +
|
||||
'the `bubbles` flag to `true` when creating the native browser event.',
|
||||
/**
|
||||
* Simulates a top level event being dispatched from a raw event that occurred
|
||||
* on an `Element` node.
|
||||
* @param {number} topLevelType A number from `TopLevelEventTypes`
|
||||
* @param {!Element} node The dom to simulate an event occurring on.
|
||||
* @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
|
||||
*/
|
||||
const simulateNativeEventOnNode = function(
|
||||
topLevelType,
|
||||
node,
|
||||
fakeNativeEvent,
|
||||
) {
|
||||
fakeNativeEvent.target = node;
|
||||
const PLUGIN_EVENT_SYSTEM = 1;
|
||||
dispatchEvent(topLevelType, PLUGIN_EVENT_SYSTEM, null, fakeNativeEvent);
|
||||
};
|
||||
|
||||
/**
|
||||
* Simulates a top level event being dispatched from a raw event that occurred
|
||||
* on the `ReactDOMComponent` `comp`.
|
||||
* @param {Object} topLevelType A type from `BrowserEventConstants.topLevelTypes`.
|
||||
* @param {!ReactDOMComponent} comp
|
||||
* @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
|
||||
*/
|
||||
const simulateNativeEventOnDOMComponent = function(
|
||||
topLevelType,
|
||||
comp,
|
||||
fakeNativeEvent,
|
||||
) {
|
||||
simulateNativeEventOnNode(topLevelType, findDOMNode(comp), fakeNativeEvent);
|
||||
};
|
||||
|
||||
/**
|
||||
* Exports:
|
||||
*
|
||||
* - `SimulateNative.click(Element/ReactDOMComponent)`
|
||||
* - `SimulateNative.mouseMove(Element/ReactDOMComponent)`
|
||||
* - `SimulateNative.mouseIn/ReactDOMComponent)`
|
||||
* - `SimulateNative.mouseOut(Element/ReactDOMComponent)`
|
||||
* - ... (All keys from `BrowserEventConstants.topLevelTypes`)
|
||||
*
|
||||
* Note: Top level event types are a subset of the entire set of handler types
|
||||
* (which include a broader set of "synthetic" events). For example, onDragDone
|
||||
* is a synthetic event. Except when testing an event plugin or React's event
|
||||
* handling code specifically, you probably want to use Simulate
|
||||
* to dispatch synthetic events.
|
||||
*/
|
||||
|
||||
const makeNativeSimulator = function(eventType, topLevelType) {
|
||||
return function(domComponentOrNode, nativeEventData) {
|
||||
if (__DEV__) {
|
||||
if (!didWarnSimulateNativeDeprecated) {
|
||||
didWarnSimulateNativeDeprecated = true;
|
||||
console.warn(
|
||||
'ReactTestUtils.SimulateNative is an undocumented API that does not match ' +
|
||||
'how the browser dispatches events, and will be removed in a future major ' +
|
||||
'version of React. If you rely on it for testing, consider attaching the root ' +
|
||||
'DOM container to the document during the test, and then dispatching native browser ' +
|
||||
'events by calling `node.dispatchEvent()` on the DOM nodes. Make sure to set ' +
|
||||
'the `bubbles` flag to `true` when creating the native browser event.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const fakeNativeEvent = new Event(eventType);
|
||||
Object.assign(fakeNativeEvent, nativeEventData);
|
||||
if (isDOMComponent(domComponentOrNode)) {
|
||||
simulateNativeEventOnDOMComponent(
|
||||
topLevelType,
|
||||
domComponentOrNode,
|
||||
fakeNativeEvent,
|
||||
);
|
||||
} else if (domComponentOrNode.tagName) {
|
||||
// Will allow on actual dom nodes.
|
||||
simulateNativeEventOnNode(
|
||||
topLevelType,
|
||||
domComponentOrNode,
|
||||
fakeNativeEvent,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const fakeNativeEvent = new Event(eventType);
|
||||
Object.assign(fakeNativeEvent, nativeEventData);
|
||||
if (isDOMComponent(domComponentOrNode)) {
|
||||
simulateNativeEventOnDOMComponent(
|
||||
topLevelType,
|
||||
domComponentOrNode,
|
||||
fakeNativeEvent,
|
||||
);
|
||||
} else if (domComponentOrNode.tagName) {
|
||||
// Will allow on actual dom nodes.
|
||||
simulateNativeEventOnNode(
|
||||
topLevelType,
|
||||
domComponentOrNode,
|
||||
fakeNativeEvent,
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
[
|
||||
[DOMTopLevelEventTypes.TOP_ABORT, 'abort'],
|
||||
[DOMTopLevelEventTypes.TOP_ANIMATION_END, 'animationEnd'],
|
||||
[DOMTopLevelEventTypes.TOP_ANIMATION_ITERATION, 'animationIteration'],
|
||||
[DOMTopLevelEventTypes.TOP_ANIMATION_START, 'animationStart'],
|
||||
[DOMTopLevelEventTypes.TOP_BLUR, 'blur'],
|
||||
[DOMTopLevelEventTypes.TOP_CAN_PLAY_THROUGH, 'canPlayThrough'],
|
||||
[DOMTopLevelEventTypes.TOP_CAN_PLAY, 'canPlay'],
|
||||
[DOMTopLevelEventTypes.TOP_CANCEL, 'cancel'],
|
||||
[DOMTopLevelEventTypes.TOP_CHANGE, 'change'],
|
||||
[DOMTopLevelEventTypes.TOP_CLICK, 'click'],
|
||||
[DOMTopLevelEventTypes.TOP_CLOSE, 'close'],
|
||||
[DOMTopLevelEventTypes.TOP_COMPOSITION_END, 'compositionEnd'],
|
||||
[DOMTopLevelEventTypes.TOP_COMPOSITION_START, 'compositionStart'],
|
||||
[DOMTopLevelEventTypes.TOP_COMPOSITION_UPDATE, 'compositionUpdate'],
|
||||
[DOMTopLevelEventTypes.TOP_CONTEXT_MENU, 'contextMenu'],
|
||||
[DOMTopLevelEventTypes.TOP_COPY, 'copy'],
|
||||
[DOMTopLevelEventTypes.TOP_CUT, 'cut'],
|
||||
[DOMTopLevelEventTypes.TOP_DOUBLE_CLICK, 'doubleClick'],
|
||||
[DOMTopLevelEventTypes.TOP_DRAG_END, 'dragEnd'],
|
||||
[DOMTopLevelEventTypes.TOP_DRAG_ENTER, 'dragEnter'],
|
||||
[DOMTopLevelEventTypes.TOP_DRAG_EXIT, 'dragExit'],
|
||||
[DOMTopLevelEventTypes.TOP_DRAG_LEAVE, 'dragLeave'],
|
||||
[DOMTopLevelEventTypes.TOP_DRAG_OVER, 'dragOver'],
|
||||
[DOMTopLevelEventTypes.TOP_DRAG_START, 'dragStart'],
|
||||
[DOMTopLevelEventTypes.TOP_DRAG, 'drag'],
|
||||
[DOMTopLevelEventTypes.TOP_DROP, 'drop'],
|
||||
[DOMTopLevelEventTypes.TOP_DURATION_CHANGE, 'durationChange'],
|
||||
[DOMTopLevelEventTypes.TOP_EMPTIED, 'emptied'],
|
||||
[DOMTopLevelEventTypes.TOP_ENCRYPTED, 'encrypted'],
|
||||
[DOMTopLevelEventTypes.TOP_ENDED, 'ended'],
|
||||
[DOMTopLevelEventTypes.TOP_ERROR, 'error'],
|
||||
[DOMTopLevelEventTypes.TOP_FOCUS, 'focus'],
|
||||
[DOMTopLevelEventTypes.TOP_INPUT, 'input'],
|
||||
[DOMTopLevelEventTypes.TOP_KEY_DOWN, 'keyDown'],
|
||||
[DOMTopLevelEventTypes.TOP_KEY_PRESS, 'keyPress'],
|
||||
[DOMTopLevelEventTypes.TOP_KEY_UP, 'keyUp'],
|
||||
[DOMTopLevelEventTypes.TOP_LOAD_START, 'loadStart'],
|
||||
[DOMTopLevelEventTypes.TOP_LOAD_START, 'loadStart'],
|
||||
[DOMTopLevelEventTypes.TOP_LOAD, 'load'],
|
||||
[DOMTopLevelEventTypes.TOP_LOADED_DATA, 'loadedData'],
|
||||
[DOMTopLevelEventTypes.TOP_LOADED_METADATA, 'loadedMetadata'],
|
||||
[DOMTopLevelEventTypes.TOP_MOUSE_DOWN, 'mouseDown'],
|
||||
[DOMTopLevelEventTypes.TOP_MOUSE_MOVE, 'mouseMove'],
|
||||
[DOMTopLevelEventTypes.TOP_MOUSE_OUT, 'mouseOut'],
|
||||
[DOMTopLevelEventTypes.TOP_MOUSE_OVER, 'mouseOver'],
|
||||
[DOMTopLevelEventTypes.TOP_MOUSE_UP, 'mouseUp'],
|
||||
[DOMTopLevelEventTypes.TOP_PASTE, 'paste'],
|
||||
[DOMTopLevelEventTypes.TOP_PAUSE, 'pause'],
|
||||
[DOMTopLevelEventTypes.TOP_PLAY, 'play'],
|
||||
[DOMTopLevelEventTypes.TOP_PLAYING, 'playing'],
|
||||
[DOMTopLevelEventTypes.TOP_PROGRESS, 'progress'],
|
||||
[DOMTopLevelEventTypes.TOP_RATE_CHANGE, 'rateChange'],
|
||||
[DOMTopLevelEventTypes.TOP_SCROLL, 'scroll'],
|
||||
[DOMTopLevelEventTypes.TOP_SEEKED, 'seeked'],
|
||||
[DOMTopLevelEventTypes.TOP_SEEKING, 'seeking'],
|
||||
[DOMTopLevelEventTypes.TOP_SELECTION_CHANGE, 'selectionChange'],
|
||||
[DOMTopLevelEventTypes.TOP_STALLED, 'stalled'],
|
||||
[DOMTopLevelEventTypes.TOP_SUSPEND, 'suspend'],
|
||||
[DOMTopLevelEventTypes.TOP_TEXT_INPUT, 'textInput'],
|
||||
[DOMTopLevelEventTypes.TOP_TIME_UPDATE, 'timeUpdate'],
|
||||
[DOMTopLevelEventTypes.TOP_TOGGLE, 'toggle'],
|
||||
[DOMTopLevelEventTypes.TOP_TOUCH_CANCEL, 'touchCancel'],
|
||||
[DOMTopLevelEventTypes.TOP_TOUCH_END, 'touchEnd'],
|
||||
[DOMTopLevelEventTypes.TOP_TOUCH_MOVE, 'touchMove'],
|
||||
[DOMTopLevelEventTypes.TOP_TOUCH_START, 'touchStart'],
|
||||
[DOMTopLevelEventTypes.TOP_TRANSITION_END, 'transitionEnd'],
|
||||
[DOMTopLevelEventTypes.TOP_VOLUME_CHANGE, 'volumeChange'],
|
||||
[DOMTopLevelEventTypes.TOP_WAITING, 'waiting'],
|
||||
[DOMTopLevelEventTypes.TOP_WHEEL, 'wheel'],
|
||||
].forEach(([topLevelType, eventType]) => {
|
||||
/**
|
||||
* @param {!Element|ReactDOMComponent} domComponentOrNode
|
||||
* @param {?Event} nativeEventData Fake native event to use in SyntheticEvent.
|
||||
*/
|
||||
SimulateNative[eventType] = makeNativeSimulator(eventType, topLevelType);
|
||||
});
|
||||
[
|
||||
[DOMTopLevelEventTypes.TOP_ABORT, 'abort'],
|
||||
[DOMTopLevelEventTypes.TOP_ANIMATION_END, 'animationEnd'],
|
||||
[DOMTopLevelEventTypes.TOP_ANIMATION_ITERATION, 'animationIteration'],
|
||||
[DOMTopLevelEventTypes.TOP_ANIMATION_START, 'animationStart'],
|
||||
[DOMTopLevelEventTypes.TOP_BLUR, 'blur'],
|
||||
[DOMTopLevelEventTypes.TOP_CAN_PLAY_THROUGH, 'canPlayThrough'],
|
||||
[DOMTopLevelEventTypes.TOP_CAN_PLAY, 'canPlay'],
|
||||
[DOMTopLevelEventTypes.TOP_CANCEL, 'cancel'],
|
||||
[DOMTopLevelEventTypes.TOP_CHANGE, 'change'],
|
||||
[DOMTopLevelEventTypes.TOP_CLICK, 'click'],
|
||||
[DOMTopLevelEventTypes.TOP_CLOSE, 'close'],
|
||||
[DOMTopLevelEventTypes.TOP_COMPOSITION_END, 'compositionEnd'],
|
||||
[DOMTopLevelEventTypes.TOP_COMPOSITION_START, 'compositionStart'],
|
||||
[DOMTopLevelEventTypes.TOP_COMPOSITION_UPDATE, 'compositionUpdate'],
|
||||
[DOMTopLevelEventTypes.TOP_CONTEXT_MENU, 'contextMenu'],
|
||||
[DOMTopLevelEventTypes.TOP_COPY, 'copy'],
|
||||
[DOMTopLevelEventTypes.TOP_CUT, 'cut'],
|
||||
[DOMTopLevelEventTypes.TOP_DOUBLE_CLICK, 'doubleClick'],
|
||||
[DOMTopLevelEventTypes.TOP_DRAG_END, 'dragEnd'],
|
||||
[DOMTopLevelEventTypes.TOP_DRAG_ENTER, 'dragEnter'],
|
||||
[DOMTopLevelEventTypes.TOP_DRAG_EXIT, 'dragExit'],
|
||||
[DOMTopLevelEventTypes.TOP_DRAG_LEAVE, 'dragLeave'],
|
||||
[DOMTopLevelEventTypes.TOP_DRAG_OVER, 'dragOver'],
|
||||
[DOMTopLevelEventTypes.TOP_DRAG_START, 'dragStart'],
|
||||
[DOMTopLevelEventTypes.TOP_DRAG, 'drag'],
|
||||
[DOMTopLevelEventTypes.TOP_DROP, 'drop'],
|
||||
[DOMTopLevelEventTypes.TOP_DURATION_CHANGE, 'durationChange'],
|
||||
[DOMTopLevelEventTypes.TOP_EMPTIED, 'emptied'],
|
||||
[DOMTopLevelEventTypes.TOP_ENCRYPTED, 'encrypted'],
|
||||
[DOMTopLevelEventTypes.TOP_ENDED, 'ended'],
|
||||
[DOMTopLevelEventTypes.TOP_ERROR, 'error'],
|
||||
[DOMTopLevelEventTypes.TOP_FOCUS, 'focus'],
|
||||
[DOMTopLevelEventTypes.TOP_INPUT, 'input'],
|
||||
[DOMTopLevelEventTypes.TOP_KEY_DOWN, 'keyDown'],
|
||||
[DOMTopLevelEventTypes.TOP_KEY_PRESS, 'keyPress'],
|
||||
[DOMTopLevelEventTypes.TOP_KEY_UP, 'keyUp'],
|
||||
[DOMTopLevelEventTypes.TOP_LOAD_START, 'loadStart'],
|
||||
[DOMTopLevelEventTypes.TOP_LOAD_START, 'loadStart'],
|
||||
[DOMTopLevelEventTypes.TOP_LOAD, 'load'],
|
||||
[DOMTopLevelEventTypes.TOP_LOADED_DATA, 'loadedData'],
|
||||
[DOMTopLevelEventTypes.TOP_LOADED_METADATA, 'loadedMetadata'],
|
||||
[DOMTopLevelEventTypes.TOP_MOUSE_DOWN, 'mouseDown'],
|
||||
[DOMTopLevelEventTypes.TOP_MOUSE_MOVE, 'mouseMove'],
|
||||
[DOMTopLevelEventTypes.TOP_MOUSE_OUT, 'mouseOut'],
|
||||
[DOMTopLevelEventTypes.TOP_MOUSE_OVER, 'mouseOver'],
|
||||
[DOMTopLevelEventTypes.TOP_MOUSE_UP, 'mouseUp'],
|
||||
[DOMTopLevelEventTypes.TOP_PASTE, 'paste'],
|
||||
[DOMTopLevelEventTypes.TOP_PAUSE, 'pause'],
|
||||
[DOMTopLevelEventTypes.TOP_PLAY, 'play'],
|
||||
[DOMTopLevelEventTypes.TOP_PLAYING, 'playing'],
|
||||
[DOMTopLevelEventTypes.TOP_PROGRESS, 'progress'],
|
||||
[DOMTopLevelEventTypes.TOP_RATE_CHANGE, 'rateChange'],
|
||||
[DOMTopLevelEventTypes.TOP_SCROLL, 'scroll'],
|
||||
[DOMTopLevelEventTypes.TOP_SEEKED, 'seeked'],
|
||||
[DOMTopLevelEventTypes.TOP_SEEKING, 'seeking'],
|
||||
[DOMTopLevelEventTypes.TOP_SELECTION_CHANGE, 'selectionChange'],
|
||||
[DOMTopLevelEventTypes.TOP_STALLED, 'stalled'],
|
||||
[DOMTopLevelEventTypes.TOP_SUSPEND, 'suspend'],
|
||||
[DOMTopLevelEventTypes.TOP_TEXT_INPUT, 'textInput'],
|
||||
[DOMTopLevelEventTypes.TOP_TIME_UPDATE, 'timeUpdate'],
|
||||
[DOMTopLevelEventTypes.TOP_TOGGLE, 'toggle'],
|
||||
[DOMTopLevelEventTypes.TOP_TOUCH_CANCEL, 'touchCancel'],
|
||||
[DOMTopLevelEventTypes.TOP_TOUCH_END, 'touchEnd'],
|
||||
[DOMTopLevelEventTypes.TOP_TOUCH_MOVE, 'touchMove'],
|
||||
[DOMTopLevelEventTypes.TOP_TOUCH_START, 'touchStart'],
|
||||
[DOMTopLevelEventTypes.TOP_TRANSITION_END, 'transitionEnd'],
|
||||
[DOMTopLevelEventTypes.TOP_VOLUME_CHANGE, 'volumeChange'],
|
||||
[DOMTopLevelEventTypes.TOP_WAITING, 'waiting'],
|
||||
[DOMTopLevelEventTypes.TOP_WHEEL, 'wheel'],
|
||||
].forEach(([topLevelType, eventType]) => {
|
||||
/**
|
||||
* @param {!Element|ReactDOMComponent} domComponentOrNode
|
||||
* @param {?Event} nativeEventData Fake native event to use in SyntheticEvent.
|
||||
*/
|
||||
SimulateNative[eventType] = makeNativeSimulator(eventType, topLevelType);
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
renderIntoDocument,
|
||||
|
||||
Reference in New Issue
Block a user