Skip to content

Commit

Permalink
Modern Event System: Remove TestUtils.SimulateNative support (faceboo…
Browse files Browse the repository at this point in the history
  • Loading branch information
trueadm committed Apr 2, 2020
1 parent e55855e commit 91c9d69
Show file tree
Hide file tree
Showing 6 changed files with 329 additions and 334 deletions.
110 changes: 92 additions & 18 deletions packages/react-dom/src/__tests__/ReactTestUtils-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]
`;
145 changes: 70 additions & 75 deletions packages/react-dom/src/events/DOMModernPluginEventSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -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> = [];
Expand Down Expand Up @@ -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 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) {
return;
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;

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 === 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 (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 (
grandNode.tag === HostRoot ||
grandNode.tag === HostPortal
isMatchingRootContainer(grandContainer, targetContainerNode)
) {
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;
}
// 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;
}
grandNode = grandNode.return;
}
const parentSubtreeInst = getClosestInstanceFromNode(container);
if (parentSubtreeInst === null) {
return;
}
node = ancestorInst = parentSubtreeInst;
continue;
}
node = node.return;
const parentSubtreeInst = getClosestInstanceFromNode(container);
if (parentSubtreeInst === null) {
return;
}
node = ancestorInst = parentSubtreeInst;
continue;
}
node = node.return;
}
}
}
Expand Down
Loading

0 comments on commit 91c9d69

Please sign in to comment.