Skip to content

Commit

Permalink
update test description
Browse files Browse the repository at this point in the history
  • Loading branch information
salazarm committed Nov 17, 2021
1 parent 353e011 commit 6bf50cb
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,117 @@ describe('ReactDOMServerSelectiveHydration', () => {
let suspend = false;
let resolve;
const promise = new Promise(resolvePromise => (resolve = resolvePromise));
function Child({text}) {
if ((text === 'A' || text === 'D') && suspend) {
throw promise;
}
Scheduler.unstable_yieldValue(text);
return (
<span
onClick={e => {
e.preventDefault();
Scheduler.unstable_yieldValue('Clicked ' + text);
}}
onMouseEnter={e => {
e.preventDefault();
Scheduler.unstable_yieldValue('Hover ' + text);
}}>
{text}
</span>
);
}

function App() {
Scheduler.unstable_yieldValue('App');
return (
<div>
<Suspense fallback="Loading...">
<Child text="A" />
</Suspense>
<Suspense fallback="Loading...">
<Child text="B" />
</Suspense>
<Suspense fallback="Loading...">
<Child text="C" />
</Suspense>
<Suspense fallback="Loading...">
<Child text="D" />
</Suspense>
</div>
);
}
const finalHTML = ReactDOMServer.renderToString(<App />);
expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C', 'D']);
const container = document.createElement('div');
// We need this to be in the document since we'll dispatch events on it.
document.body.appendChild(container);

container.innerHTML = finalHTML;

const spanB = container.getElementsByTagName('span')[1];
const spanC = container.getElementsByTagName('span')[2];
const spanD = container.getElementsByTagName('span')[3];

suspend = true;

// A and D will be suspended. We'll click on D which should take
// priority, after we unsuspend.
const root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);

// Nothing has been hydrated so far.
expect(Scheduler).toHaveYielded([]);
// Click D
dispatchMouseHoverEvent(spanD, null);
dispatchClickEvent(spanD);
// Hover over B and then C.
dispatchMouseHoverEvent(spanB, spanD);
dispatchMouseHoverEvent(spanC, spanB);
expect(Scheduler).toHaveYielded(['App']);
await act(async () => {
suspend = false;
resolve();
await promise;
});
if (
gate(
flags =>
flags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay,
)
) {
// We should prioritize hydrating D first because we clicked it.
// but event isnt replayed
expect(Scheduler).toHaveYielded([
'D',
'B', // Ideally this should be later.
'C',
'Hover C',
'A',
]);
} else {
// We should prioritize hydrating D first because we clicked it.
// Next we should hydrate C since that's the current hover target.
// To simplify implementation details we hydrate both B and C at
// the same time since B was already scheduled.
// This is ok because it will at least not continue for nested
// boundary. See the next test below.
expect(Scheduler).toHaveYielded([
'D',
'Clicked D',
'B', // Ideally this should be later.
'C',
'Hover C',
'A',
]);
}

document.body.removeChild(container);
});

it('replays capture phase for continuous events and respects stopPropagation', async () => {
let suspend = false;
let resolve;
const promise = new Promise(resolvePromise => (resolve = resolvePromise));

function Child({text}) {
if ((text === 'A' || text === 'D') && suspend) {
Expand Down Expand Up @@ -859,7 +970,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
document.body.removeChild(container);
});

describe('can handle replaying events as part of multiple instance of React', () => {
describe('can handle replaying events as part of multiple instances of React', () => {
let resolveInner;
let resolveOuter;
let innerPromise;
Expand All @@ -873,8 +984,6 @@ describe('ReactDOMServerSelectiveHydration', () => {
jest.resetModuleRegistry();
let OuterReactDOM;
let InnerReactDOM;
let InnerAct;
let OuterAct;
jest.isolateModules(() => {
OuterReactDOM = require('react-dom');
OuterScheduler = require('scheduler');
Expand All @@ -898,7 +1007,6 @@ describe('ReactDOMServerSelectiveHydration', () => {
};
});

let isServer = true;
function Outer() {
if (suspendOuter) {
OuterScheduler.unstable_yieldValue('Suspend Outer');
Expand All @@ -914,7 +1022,8 @@ describe('ReactDOMServerSelectiveHydration', () => {
}}
dangerouslySetInnerHTML={{
__html: innerRoot ? innerRoot.innerHTML : '',
}}></div>
}}
/>
);
}
const OuterApp = () => {
Expand Down Expand Up @@ -943,7 +1052,8 @@ describe('ReactDOMServerSelectiveHydration', () => {
id="inner"
onMouseEnter={() => {
Scheduler.unstable_yieldValue('Inner Mouse Enter');
}}></div>
}}
/>
);
}
const InnerApp = () => {
Expand All @@ -968,7 +1078,6 @@ describe('ReactDOMServerSelectiveHydration', () => {

suspendOuter = true;
suspendInner = true;
isServer = false;

OuterReactDOM.createRoot(outerContainer, {hydrate: true}).render(
<OuterApp />,
Expand Down Expand Up @@ -1047,7 +1156,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
});

// Nothing happens to inner app yet.
// Its blocked on the outer app replaing the event
// Its blocked on the outer app replaying the event
expect(InnerScheduler).toHaveYielded([]);
// Outer hydrates and schedules Replay
expect(OuterScheduler).toHaveYielded(['Outer']);
Expand Down
4 changes: 2 additions & 2 deletions packages/react-dom/src/events/ReactDOMEventListener.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ function dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEve
eventSystemFlags & IS_CAPTURE_PHASE &&
isDiscreteEventThatRequiresHydration(domEventName)
) {
// Intentionally not strick equal. Could be `null` or `undefined`
// Intentionally not strict equal. Could be `null` or `undefined`
while (blockedOn != null) {
const fiber = getInstanceFromNode(blockedOn);
if (fiber !== null) {
Expand Down Expand Up @@ -365,7 +365,7 @@ export function findInstanceBlockingEvent(
eventSystemFlags: EventSystemFlags,
targetContainer: EventTarget,
nativeEvent: AnyNativeEvent,
): null | Container | SuspenseInstance {
): typeof undefined | null | Container | SuspenseInstance {
// TODO: Warn if _enabled is false.

const nativeEventTarget = getEventTarget(nativeEvent);
Expand Down
34 changes: 17 additions & 17 deletions packages/react-dom/src/events/ReactDOMEventReplaying.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
import type {EventPriority} from 'react-reconciler/src/ReactEventPriorities';
import {dispatchEventForPluginEventSystem} from './DOMPluginEventSystem';
import getEventTarget from './getEventTarget';
import {isReplayingEvent, replayEventWrapper} from './replayedEvent';
import {replayEventWrapper} from './replayedEvent';

import {
enableSelectiveHydration,
Expand Down Expand Up @@ -91,7 +91,7 @@ type PointerEvent = Event & {
};

type QueuedReplayableEvent = {|
blockedOn: null | Container | SuspenseInstance,
blockedOn: typeof undefined | null | Container | SuspenseInstance,
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
nativeEvent: AnyNativeEvent,
Expand All @@ -116,7 +116,7 @@ const queuedPointerCaptures: Map<number, QueuedReplayableEvent> = new Map();
// We could consider replaying selectionchange and touchmoves too.

type QueuedHydrationTarget = {|
blockedOn: null | Container | SuspenseInstance,
blockedOn: typeof undefined | null | Container | SuspenseInstance,
target: Node,
priority: EventPriority,
|};
Expand Down Expand Up @@ -168,7 +168,7 @@ export function isDiscreteEventThatRequiresHydration(
}

function createQueuedReplayableEvent(
blockedOn: null | Container | SuspenseInstance,
blockedOn: typeof undefined | null | Container | SuspenseInstance,
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
targetContainer: EventTarget,
Expand All @@ -184,7 +184,7 @@ function createQueuedReplayableEvent(
}

export function queueDiscreteEvent(
blockedOn: null | Container | SuspenseInstance,
blockedOn: typeof undefined | null | Container | SuspenseInstance,
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
targetContainer: EventTarget,
Expand All @@ -205,13 +205,13 @@ export function queueDiscreteEvent(
if (queuedDiscreteEvents.length === 1) {
// If this was the first discrete event, we might be able to
// synchronously unblock it so that preventDefault still works.
while (queuedEvent.blockedOn !== null) {
while (queuedEvent.blockedOn != null) {
const fiber = getInstanceFromNode(queuedEvent.blockedOn);
if (fiber === null) {
break;
}
attemptSynchronousHydration(fiber);
if (queuedEvent.blockedOn === null) {
if (queuedEvent.blockedOn == null) {
// We got unblocked by hydration. Let's try again.
replayUnblockedEvents();
// If we're reblocked, on an inner boundary, we might need
Expand Down Expand Up @@ -262,7 +262,7 @@ export function clearIfContinuousEvent(

function accumulateOrCreateContinuousQueuedReplayableEvent(
existingQueuedEvent: null | QueuedReplayableEvent,
blockedOn: null | Container | SuspenseInstance,
blockedOn: typeof undefined | null | Container | SuspenseInstance,
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
targetContainer: EventTarget,
Expand All @@ -279,7 +279,7 @@ function accumulateOrCreateContinuousQueuedReplayableEvent(
targetContainer,
nativeEvent,
);
if (blockedOn !== null) {
if (blockedOn != null) {
const fiber = getInstanceFromNode(blockedOn);
if (fiber !== null) {
// Attempt to increase the priority of this target.
Expand All @@ -304,7 +304,7 @@ function accumulateOrCreateContinuousQueuedReplayableEvent(
}

export function queueIfContinuousEvent(
blockedOn: null | Container | SuspenseInstance,
blockedOn: typeof undefined | null | Container | SuspenseInstance,
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
targetContainer: EventTarget,
Expand Down Expand Up @@ -454,7 +454,7 @@ export function queueExplicitHydrationTarget(target: Node): void {
function attemptReplayContinuousQueuedEvent(
queuedEvent: QueuedReplayableEvent,
): boolean {
if (queuedEvent.blockedOn !== null) {
if (queuedEvent.blockedOn != null) {
return false;
}
const targetContainers = queuedEvent.targetContainers;
Expand All @@ -466,7 +466,7 @@ function attemptReplayContinuousQueuedEvent(
targetContainer,
queuedEvent.nativeEvent,
);
if (nextBlockedOn !== null) {
if (nextBlockedOn != null) {
// We're still blocked. Try again later.
const fiber = getInstanceFromNode(nextBlockedOn);
if (fiber !== null) {
Expand Down Expand Up @@ -498,7 +498,7 @@ function replayUnblockedEvents() {
// First replay discrete events.
while (queuedDiscreteEvents.length > 0) {
const nextDiscreteEvent = queuedDiscreteEvents[0];
if (nextDiscreteEvent.blockedOn !== null) {
if (nextDiscreteEvent.blockedOn != null) {
// We're still blocked.
// Increase the priority of this boundary to unblock
// the next discrete event.
Expand All @@ -517,7 +517,7 @@ function replayUnblockedEvents() {
targetContainer,
nextDiscreteEvent.nativeEvent,
);
if (nextBlockedOn !== null) {
if (nextBlockedOn != null) {
// We're still blocked. Try again later.
nextDiscreteEvent.blockedOn = nextBlockedOn;
break;
Expand All @@ -526,7 +526,7 @@ function replayUnblockedEvents() {
// This target container was successfully dispatched. Try the next.
targetContainers.shift();
}
if (nextDiscreteEvent.blockedOn === null) {
if (nextDiscreteEvent.blockedOn == null) {
// We've successfully replayed the first event. Let's try the next one.
queuedDiscreteEvents.shift();
}
Expand Down Expand Up @@ -603,12 +603,12 @@ export function retryIfBlockedOn(

while (queuedExplicitHydrationTargets.length > 0) {
const nextExplicitTarget = queuedExplicitHydrationTargets[0];
if (nextExplicitTarget.blockedOn !== null) {
if (nextExplicitTarget.blockedOn != null) {
// We're still blocked.
break;
} else {
attemptExplicitHydrationTarget(nextExplicitTarget);
if (nextExplicitTarget.blockedOn === null) {
if (nextExplicitTarget.blockedOn == null) {
// We're unblocked.
queuedExplicitHydrationTargets.shift();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ function extractEvents(
// the event in the out event of the other target.
const related =
(nativeEvent: any).relatedTarget || (nativeEvent: any).fromElement;
console.log({related});
if (related) {
// If the related node is managed by React, we can assume that we have
// already dispatched the corresponding events during its mouseout.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ describe('EnterLeaveEventPlugin', () => {

expect(enterEvents.length).toBe(1);
expect(enterEvents[0].target).toBe(node);
expect(iframe.contentWindow).toBe(enterEvents[0].relatedTarget);
expect(enterEvents[0].relatedTarget).toBe(iframe.contentWindow);
});

// Regression test for https://github.com/facebook/react/issues/10906.
Expand Down

0 comments on commit 6bf50cb

Please sign in to comment.