Apply #20778 to new fork, too (#20782)

* Apply #20778 to new fork, too

* Fix tests that use runWithPriority

Where possible, I tried to rewrite in terms of an idiomatic API.

For DOM tests, we should be dispatching an event with the desired
priority level.

For Idle updates (very unstable feature), probably need an unstable
API like ReactDOM.unstable_IdleUpdates.

Some of these fixes are not great, but we can replace them once we've
landed the more of our planned changes to the layering between
Scheduler, the reconciler, and the renderer.
This commit is contained in:
Andrew Clark
2021-02-09 23:21:46 -08:00
committed by GitHub
parent 3d10eca241
commit 3499c343ab
17 changed files with 197 additions and 197 deletions
@@ -92,6 +92,21 @@ function dispatchClickEvent(target) {
return target.dispatchEvent(mouseOutEvent);
}
// TODO: There's currently no React DOM API to opt into Idle priority updates,
// and there's no native DOM event that maps to idle priority, so this is a
// temporary workaround. Need something like ReactDOM.unstable_IdleUpdates.
function TODO_scheduleIdleDOMSchedulerTask(fn) {
Scheduler.unstable_runWithPriority(Scheduler.unstable_IdlePriority, () => {
const prevEvent = window.event;
window.event = {type: 'message'};
try {
fn();
} finally {
window.event = prevEvent;
}
});
}
describe('ReactDOMServerSelectiveHydration', () => {
beforeEach(() => {
jest.resetModuleRegistry();
@@ -889,12 +904,10 @@ describe('ReactDOMServerSelectiveHydration', () => {
expect(Scheduler).toFlushAndYieldThrough(['App', 'Commit']);
// Render an update at Idle priority that needs to update A.
Scheduler.unstable_runWithPriority(
Scheduler.unstable_IdlePriority,
() => {
root.render(<App a="AA" />);
},
);
TODO_scheduleIdleDOMSchedulerTask(() => {
root.render(<App a="AA" />);
});
// Start rendering. This will force the first boundary to hydrate
// by scheduling it at one higher pri than Idle.
+1
View File
@@ -41,6 +41,7 @@ export const {
deferredUpdates,
unbatchedUpdates,
discreteUpdates,
idleUpdates,
flushDiscreteUpdates,
flushSync,
flushPassiveEffects,
@@ -41,6 +41,7 @@ export const {
deferredUpdates,
unbatchedUpdates,
discreteUpdates,
idleUpdates,
flushDiscreteUpdates,
flushSync,
flushPassiveEffects,
+21 -1
View File
@@ -27,6 +27,7 @@ import {
LegacyRoot,
} from 'react-reconciler/src/ReactRootTags';
import {enableNativeEventPriorityInference} from 'shared/ReactFeatureFlags';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import enqueueTask from 'shared/enqueueTask';
const {IsSomeRendererActing} = ReactSharedInternals;
@@ -392,7 +393,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
resetAfterCommit(): void {},
getCurrentEventPriority() {
return NoopRenderer.DefaultEventPriority;
return currentEventPriority;
},
now: Scheduler.unstable_now,
@@ -587,6 +588,8 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
const roots = new Map();
const DEFAULT_ROOT_ID = '<default>';
let currentEventPriority = NoopRenderer.DefaultEventPriority;
function childToJSX(child, text) {
if (text !== null) {
return text;
@@ -925,6 +928,23 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
discreteUpdates: NoopRenderer.discreteUpdates,
idleUpdates<T>(fn: () => T): T {
if (enableNativeEventPriorityInference) {
const prevEventPriority = currentEventPriority;
currentEventPriority = NoopRenderer.IdleEventPriority;
try {
fn();
} finally {
currentEventPriority = prevEventPriority;
}
} else {
return Scheduler.unstable_runWithPriority(
Scheduler.unstable_IdlePriority,
fn,
);
}
},
flushDiscreteUpdates: NoopRenderer.flushDiscreteUpdates,
flushSync(fn: () => mixed) {
@@ -70,7 +70,7 @@ const RetryLanePriority: LanePriority = 5;
const SelectiveHydrationLanePriority: LanePriority = 4;
const IdleHydrationLanePriority: LanePriority = 3;
const IdleLanePriority: LanePriority = 2;
export const IdleLanePriority: LanePriority = 2;
const OffscreenLanePriority: LanePriority = 1;
@@ -275,6 +275,7 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
// Check if any work has expired.
if (expiredLanes !== NoLanes) {
// TODO: Should entangle with SyncLane
nextLanes = expiredLanes;
nextLanePriority = return_highestLanePriority = SyncLanePriority;
} else {
@@ -70,7 +70,7 @@ const RetryLanePriority: LanePriority = 5;
const SelectiveHydrationLanePriority: LanePriority = 4;
const IdleHydrationLanePriority: LanePriority = 3;
const IdleLanePriority: LanePriority = 2;
export const IdleLanePriority: LanePriority = 2;
const OffscreenLanePriority: LanePriority = 1;
@@ -275,6 +275,7 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
// Check if any work has expired.
if (expiredLanes !== NoLanes) {
// TODO: Should entangle with SyncLane
nextLanes = expiredLanes;
nextLanePriority = return_highestLanePriority = SyncLanePriority;
} else {
+5
View File
@@ -55,6 +55,7 @@ import {
DefaultEventPriority as DefaultEventPriority_old,
DiscreteEventPriority as DiscreteEventPriority_old,
ContinuousEventPriority as ContinuousEventPriority_old,
IdleEventPriority as IdleEventPriority_old,
} from './ReactFiberReconciler.old';
import {
@@ -98,6 +99,7 @@ import {
DefaultEventPriority as DefaultEventPriority_new,
DiscreteEventPriority as DiscreteEventPriority_new,
ContinuousEventPriority as ContinuousEventPriority_new,
IdleEventPriority as IdleEventPriority_new,
} from './ReactFiberReconciler.new';
export const createContainer = enableNewReconciler
@@ -183,6 +185,9 @@ export const DiscreteEventPriority = enableNewReconciler
export const ContinuousEventPriority = enableNewReconciler
? ContinuousEventPriority_new
: ContinuousEventPriority_old;
export const IdleEventPriority = enableNewReconciler
? IdleEventPriority_new
: IdleEventPriority_old;
//TODO: "psuedo" is spelled "pseudo"
export const createHasPsuedoClassSelector = enableNewReconciler
@@ -101,6 +101,7 @@ export {
InputDiscreteLanePriority as DiscreteEventPriority,
InputContinuousLanePriority as ContinuousEventPriority,
DefaultLanePriority as DefaultEventPriority,
IdleLanePriority as IdleEventPriority,
} from './ReactFiberLane.new';
export {registerMutableSourceForHydration} from './ReactMutableSource.new';
@@ -101,6 +101,7 @@ export {
InputDiscreteLanePriority as DiscreteEventPriority,
InputContinuousLanePriority as ContinuousEventPriority,
DefaultLanePriority as DefaultEventPriority,
IdleLanePriority as IdleEventPriority,
} from './ReactFiberLane.old';
export {registerMutableSourceForHydration} from './ReactMutableSource.new';
@@ -465,15 +465,7 @@ export function requestUpdateLane(fiber: Fiber): Lane {
} else {
if (enableNativeEventPriorityInference) {
const eventLanePriority = getCurrentEventPriority();
if (eventLanePriority === DefaultLanePriority) {
// TODO: move this case into the ReactDOM host config.
const schedulerLanePriority = schedulerPriorityToLanePriority(
schedulerPriority,
);
lane = findUpdateLane(schedulerLanePriority, currentEventWipLanes);
} else {
lane = findUpdateLane(eventLanePriority, currentEventWipLanes);
}
lane = findUpdateLane(eventLanePriority, currentEventWipLanes);
} else {
const schedulerLanePriority = schedulerPriorityToLanePriority(
schedulerPriority,
@@ -403,23 +403,23 @@ describe('ReactExpiration', () => {
expect(ReactNoop).toMatchRenderedOutput('Hi');
});
it('prevents starvation by high priority updates', async () => {
it('prevents starvation by sync updates', async () => {
const {useState} = React;
let updateHighPri;
let updateSyncPri;
let updateNormalPri;
function App() {
const [highPri, setHighPri] = useState(0);
const [normalPri, setNormalPri] = useState(0);
updateHighPri = () =>
Scheduler.unstable_runWithPriority(
Scheduler.unstable_UserBlockingPriority,
() => setHighPri(n => n + 1),
);
updateSyncPri = () => {
ReactNoop.flushSync(() => {
setHighPri(n => n + 1);
});
};
updateNormalPri = () => setNormalPri(n => n + 1);
return (
<>
<Text text={'High pri: ' + highPri} />
<Text text={'Sync pri: ' + highPri} />
{', '}
<Text text={'Normal pri: ' + normalPri} />
</>
@@ -430,29 +430,29 @@ describe('ReactExpiration', () => {
await ReactNoop.act(async () => {
root.render(<App />);
});
expect(Scheduler).toHaveYielded(['High pri: 0', 'Normal pri: 0']);
expect(root).toMatchRenderedOutput('High pri: 0, Normal pri: 0');
expect(Scheduler).toHaveYielded(['Sync pri: 0', 'Normal pri: 0']);
expect(root).toMatchRenderedOutput('Sync pri: 0, Normal pri: 0');
// First demonstrate what happens when there's no starvation
await ReactNoop.act(async () => {
updateNormalPri();
expect(Scheduler).toFlushAndYieldThrough(['High pri: 0']);
updateHighPri();
expect(Scheduler).toFlushAndYieldThrough(['Sync pri: 0']);
updateSyncPri();
});
expect(Scheduler).toHaveYielded([
// Interrupt high pri update to render sync update
'High pri: 1',
'Sync pri: 1',
'Normal pri: 0',
// Now render normal pri
'High pri: 1',
'Sync pri: 1',
'Normal pri: 1',
]);
expect(root).toMatchRenderedOutput('High pri: 1, Normal pri: 1');
expect(root).toMatchRenderedOutput('Sync pri: 1, Normal pri: 1');
// Do the same thing, but starve the first update
await ReactNoop.act(async () => {
updateNormalPri();
expect(Scheduler).toFlushAndYieldThrough(['High pri: 1']);
expect(Scheduler).toFlushAndYieldThrough(['Sync pri: 1']);
// This time, a lot of time has elapsed since the normal pri update
// started rendering. (This should advance time by some number that's
@@ -461,86 +461,16 @@ describe('ReactExpiration', () => {
Scheduler.unstable_advanceTime(10000);
// So when we get a high pri update, we shouldn't interrupt
updateHighPri();
updateSyncPri();
});
expect(Scheduler).toHaveYielded([
// Finish normal pri update
'Normal pri: 2',
// Then do high pri update
'High pri: 2',
'Sync pri: 2',
'Normal pri: 2',
]);
expect(root).toMatchRenderedOutput('High pri: 2, Normal pri: 2');
});
it('prevents starvation by sync updates', async () => {
const {useState} = React;
let updateSyncPri;
let updateHighPri;
function App() {
const [syncPri, setSyncPri] = useState(0);
const [highPri, setHighPri] = useState(0);
updateSyncPri = () => ReactNoop.flushSync(() => setSyncPri(n => n + 1));
updateHighPri = () =>
Scheduler.unstable_runWithPriority(
Scheduler.unstable_UserBlockingPriority,
() => setHighPri(n => n + 1),
);
return (
<>
<Text text={'Sync pri: ' + syncPri} />
{', '}
<Text text={'High pri: ' + highPri} />
</>
);
}
const root = ReactNoop.createRoot();
await ReactNoop.act(async () => {
root.render(<App />);
});
expect(Scheduler).toHaveYielded(['Sync pri: 0', 'High pri: 0']);
expect(root).toMatchRenderedOutput('Sync pri: 0, High pri: 0');
// First demonstrate what happens when there's no starvation
await ReactNoop.act(async () => {
updateHighPri();
expect(Scheduler).toFlushAndYieldThrough(['Sync pri: 0']);
updateSyncPri();
});
expect(Scheduler).toHaveYielded([
// Interrupt high pri update to render sync update
'Sync pri: 1',
'High pri: 0',
// Now render high pri
'Sync pri: 1',
'High pri: 1',
]);
expect(root).toMatchRenderedOutput('Sync pri: 1, High pri: 1');
// Do the same thing, but starve the first update
await ReactNoop.act(async () => {
updateHighPri();
expect(Scheduler).toFlushAndYieldThrough(['Sync pri: 1']);
// This time, a lot of time has elapsed since the high pri update started
// rendering. (This should advance time by some number that's definitely
// bigger than the constant heuristic we use to detect starvation of user
// interactions, but not as high as the onse used for normal pri updates.)
Scheduler.unstable_advanceTime(1500);
// So when we get a sync update, we shouldn't interrupt
updateSyncPri();
});
expect(Scheduler).toHaveYielded([
// Finish high pri update
'High pri: 2',
// Then do sync update
'Sync pri: 2',
'High pri: 2',
]);
expect(root).toMatchRenderedOutput('Sync pri: 2, High pri: 2');
expect(root).toMatchRenderedOutput('Sync pri: 2, Normal pri: 2');
});
it('idle work never expires', async () => {
@@ -553,10 +483,9 @@ describe('ReactExpiration', () => {
const [highPri, setIdlePri] = useState(0);
updateSyncPri = () => ReactNoop.flushSync(() => setSyncPri(n => n + 1));
updateIdlePri = () =>
Scheduler.unstable_runWithPriority(
Scheduler.unstable_IdlePriority,
() => setIdlePri(n => n + 1),
);
ReactNoop.idleUpdates(() => {
setIdlePri(n => n + 1);
});
return (
<>
<Text text={'Sync pri: ' + syncPri} />
@@ -695,11 +624,11 @@ describe('ReactExpiration', () => {
function App() {
const [highPri, setHighPri] = useState(0);
const [normalPri, setNormalPri] = useState(0);
updateHighPri = () =>
Scheduler.unstable_runWithPriority(
Scheduler.unstable_UserBlockingPriority,
() => setHighPri(n => n + 1),
);
updateHighPri = () => {
ReactNoop.flushSync(() => {
setHighPri(n => n + 1);
});
};
updateNormalPri = () => setNormalPri(n => n + 1);
return (
<>
@@ -735,20 +664,34 @@ describe('ReactExpiration', () => {
expect(Scheduler).toFlushAndYieldThrough(['Normal pri: 1']);
// More time goes by. This expires both of the updates just scheduled.
Scheduler.unstable_advanceTime(10000);
expect(Scheduler).toHaveYielded([]);
// Attempt to interrupt with a high pri update.
updateHighPri();
// Both normal pri updates should have expired.
expect(Scheduler).toFlushExpired([
'Sibling',
// Notice that the high pri update didn't flush yet. Expiring one lane
// doesn't affect other lanes. (Unless they are intentionally entangled,
// like we do for overlapping transitions that affect the same state.)
'High pri: 0',
'Normal pri: 2',
'Sibling',
]);
if (gate(flags => flags.FIXME)) {
// The sync update and the expired normal pri updates render in a
// single batch.
expect(Scheduler).toHaveYielded([
'Sibling',
'High pri: 1',
'Normal pri: 2',
'Sibling',
]);
} else {
expect(Scheduler).toHaveYielded([
'Sibling',
'High pri: 0',
'Normal pri: 2',
'Sibling',
// TODO: This is the sync update. We should have rendered it in the same
// batch as the expired update.
'High pri: 1',
'Normal pri: 2',
'Sibling',
]);
}
});
});
@@ -1351,11 +1351,10 @@ describe('ReactHooksWithNoopRenderer', () => {
expect(Scheduler).toFlushAndYieldThrough(['Child one render']);
// Schedule unmount for the parent that unmounts children with pending update.
Scheduler.unstable_runWithPriority(
Scheduler.unstable_UserBlockingPriority,
() => setParentState(false),
);
expect(Scheduler).toFlushAndYieldThrough([
ReactNoop.flushSync(() => {
setParentState(false);
});
expect(Scheduler).toHaveYielded([
'Parent false render',
'Parent false commit',
]);
@@ -79,6 +79,10 @@ describe('ReactSchedulerIntegration', () => {
expect(Scheduler).toHaveYielded(['Priority: Immediate']);
});
// TODO: Figure out what to do with these tests. I don't think most of them
// make sense once we decouple Scheduler from React. Perhaps need similar
// tests for React DOM.
// @gate !enableNativeEventPriorityInference
it('has correct priority during rendering', () => {
function ReadPriority() {
Scheduler.unstable_yieldValue(
@@ -100,6 +104,10 @@ describe('ReactSchedulerIntegration', () => {
expect(Scheduler).toFlushAndYield(['Priority: Idle']);
});
// TODO: Figure out what to do with these tests. I don't think most of them
// make sense once we decouple Scheduler from React. Perhaps need similar
// tests for React DOM.
// @gate !enableNativeEventPriorityInference
it('has correct priority when continuing a render after yielding', () => {
function ReadPriority() {
Scheduler.unstable_yieldValue(
@@ -152,6 +160,10 @@ describe('ReactSchedulerIntegration', () => {
]);
});
// TODO: Figure out what to do with these tests. I don't think most of them
// make sense once we decouple Scheduler from React. Perhaps need similar
// tests for React DOM.
// @gate !enableNativeEventPriorityInference
it('passive effects never have higher than normal priority', async () => {
const {useEffect} = React;
function ReadPriority({step}) {
@@ -205,6 +217,10 @@ describe('ReactSchedulerIntegration', () => {
]);
});
// TODO: Figure out what to do with these tests. I don't think most of them
// make sense once we decouple Scheduler from React. Perhaps need similar
// tests for React DOM.
// @gate !enableNativeEventPriorityInference
it('passive effects have correct priority even if they are flushed early', async () => {
const {useEffect} = React;
function ReadPriority({step}) {
@@ -233,6 +249,10 @@ describe('ReactSchedulerIntegration', () => {
]);
});
// TODO: Figure out what to do with these tests. I don't think most of them
// make sense once we decouple Scheduler from React. Perhaps need similar
// tests for React DOM.
// @gate !enableNativeEventPriorityInference
it('passive effect clean-up functions have correct priority even when component is deleted', async () => {
const {useEffect} = React;
function ReadPriority({step}) {
@@ -322,6 +342,10 @@ describe('ReactSchedulerIntegration', () => {
]);
});
// TODO: Figure out what to do with these tests. I don't think most of them
// make sense once we decouple Scheduler from React. Perhaps need similar
// tests for React DOM.
// @gate !enableNativeEventPriorityInference
it('after completing a level of work, infers priority of the next batch based on its expiration time', () => {
function App({label}) {
Scheduler.unstable_yieldValue(
@@ -2248,9 +2248,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
expect(ReactNoop.getChildren()).toEqual([]);
// Schedule an update at idle pri.
Scheduler.unstable_runWithPriority(Scheduler.unstable_IdlePriority, () =>
ReactNoop.render(<Foo renderContent={2} />),
);
ReactNoop.idleUpdates(() => ReactNoop.render(<Foo renderContent={2} />));
// We won't even work on Idle priority.
expect(Scheduler).toFlushAndYield([]);
@@ -3018,12 +3016,9 @@ describe('ReactSuspenseWithNoopRenderer', () => {
setText('B');
await resolveText('C');
Scheduler.unstable_runWithPriority(
Scheduler.unstable_IdlePriority,
() => {
setText('C');
},
);
ReactNoop.idleUpdates(() => {
setText('C');
});
expect(Scheduler).toFlushAndYield([
// First we attempt the high pri update. It suspends.
@@ -3282,12 +3277,9 @@ describe('ReactSuspenseWithNoopRenderer', () => {
// And another update at lower priority. This will unblock.
await resolveText('E');
Scheduler.unstable_runWithPriority(
Scheduler.unstable_IdlePriority,
() => {
setText('E');
},
);
ReactNoop.idleUpdates(() => {
setText('E');
});
});
// Even though the fragment fiber is not part of the return path, we should
// be able to finish rendering.
@@ -3838,12 +3830,9 @@ describe('ReactSuspenseWithNoopRenderer', () => {
await ReactNoop.act(async () => {
setText('B');
Scheduler.unstable_runWithPriority(
Scheduler.unstable_IdlePriority,
() => {
setText('B');
},
);
ReactNoop.idleUpdates(() => {
setText('B');
});
// Suspend the first update. The second update doesn't run because it has
// Idle priority.
expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
@@ -529,7 +529,7 @@ describe('useMutableSource', () => {
// Changing values should schedule an update with React.
// Start working on this update but don't finish it.
Scheduler.unstable_runWithPriority(Scheduler.unstable_LowPriority, () => {
ReactNoop.idleUpdates(() => {
source.value = 'two';
expect(Scheduler).toFlushAndYieldThrough(['a:two']);
});
@@ -538,29 +538,26 @@ describe('useMutableSource', () => {
// Force a higher priority render with a new config.
// This should signal that the snapshot is not safe and trigger a full re-render.
Scheduler.unstable_runWithPriority(
Scheduler.unstable_UserBlockingPriority,
() => {
ReactNoop.render(
<>
<Component
label="a"
getSnapshot={newGetSnapshot}
mutableSource={mutableSource}
subscribe={defaultSubscribe}
/>
<Component
label="b"
getSnapshot={newGetSnapshot}
mutableSource={mutableSource}
subscribe={defaultSubscribe}
/>
</>,
() => Scheduler.unstable_yieldValue('Sync effect'),
);
},
);
expect(Scheduler).toFlushAndYieldThrough([
ReactNoop.flushSync(() => {
ReactNoop.render(
<>
<Component
label="a"
getSnapshot={newGetSnapshot}
mutableSource={mutableSource}
subscribe={defaultSubscribe}
/>
<Component
label="b"
getSnapshot={newGetSnapshot}
mutableSource={mutableSource}
subscribe={defaultSubscribe}
/>
</>,
() => Scheduler.unstable_yieldValue('Sync effect'),
);
});
expect(Scheduler).toHaveYielded([
'a:new:two',
'b:new:two',
'Sync effect',
@@ -596,7 +593,7 @@ describe('useMutableSource', () => {
// Changing values should schedule an update with React.
// Start working on this update but don't finish it.
Scheduler.unstable_runWithPriority(Scheduler.unstable_LowPriority, () => {
ReactNoop.idleUpdates(() => {
source.value = 'two';
expect(Scheduler).toFlushAndYieldThrough(['a:two']);
});
@@ -793,19 +790,14 @@ describe('useMutableSource', () => {
ReactNoop.flushPassiveEffects();
// Change the source (and schedule an update).
Scheduler.unstable_runWithPriority(Scheduler.unstable_LowPriority, () => {
source.value = 'two';
});
source.value = 'two';
// Schedule a higher priority update that changes getSnapshot.
Scheduler.unstable_runWithPriority(
Scheduler.unstable_UserBlockingPriority,
() => {
updateGetSnapshot(() => newGetSnapshot);
},
);
ReactNoop.flushSync(() => {
updateGetSnapshot(() => newGetSnapshot);
});
expect(Scheduler).toFlushAndYield(['only:new:two']);
expect(Scheduler).toHaveYielded(['only:new:two']);
});
});
@@ -31,6 +31,15 @@ describe('useMutableSourceHydration', () => {
useMutableSource = React.unstable_useMutableSource;
});
function dispatchAndSetCurrentEvent(el, event) {
try {
window.event = event;
el.dispatchEvent(event);
} finally {
window.event = undefined;
}
}
const defaultGetSnapshot = source => source.value;
const defaultSubscribe = (source, callback) => source.subscribe(callback);
@@ -332,6 +341,7 @@ describe('useMutableSourceHydration', () => {
});
// @gate experimental
// @gate enableNativeEventPriorityInference
it('should detect a tear during a higher priority interruption', () => {
const source = createSource('one');
const mutableSource = createMutableSource(source, param => param.version);
@@ -371,16 +381,22 @@ describe('useMutableSourceHydration', () => {
mutableSources: [mutableSource],
},
});
expect(() => {
act(() => {
root.render(<TestComponent flag={1} />);
expect(Scheduler).toFlushAndYieldThrough([1]);
// Render an update which will be higher priority than the hydration.
Scheduler.unstable_runWithPriority(
Scheduler.unstable_UserBlockingPriority,
() => root.render(<TestComponent flag={2} />),
);
// We can do this by scheduling the update inside a mouseover event.
const arbitraryElement = document.createElement('div');
const mouseOverEvent = document.createEvent('MouseEvents');
mouseOverEvent.initEvent('mouseover', true, true);
arbitraryElement.addEventListener('mouseover', () => {
root.render(<TestComponent flag={2} />);
});
dispatchAndSetCurrentEvent(arbitraryElement, mouseOverEvent);
expect(Scheduler).toFlushAndYieldThrough([2]);
source.value = 'two';
@@ -28,6 +28,7 @@ let onWorkStopped;
// This is hard coded directly to avoid needing to import, and
// we'll remove this as we replace runWithPriority with React APIs.
const IdleLanePriority = 2;
const InputContinuousPriority = 10;
function loadModules() {
ReactFeatureFlags = require('shared/ReactFeatureFlags');
@@ -427,6 +428,7 @@ describe('ReactDOMTracing', () => {
});
// @gate experimental
// @gate enableNativeEventPriorityInference
it('should properly trace interactions when there is work of interleaved priorities', () => {
const Child = () => {
Scheduler.unstable_yieldValue('Child');
@@ -502,9 +504,8 @@ describe('ReactDOMTracing', () => {
let interaction = null;
SchedulerTracing.unstable_trace('update', 0, () => {
interaction = Array.from(SchedulerTracing.unstable_getCurrent())[0];
Scheduler.unstable_runWithPriority(
Scheduler.unstable_UserBlockingPriority,
() => scheduleUpdateWithHidden(),
ReactDOM.unstable_runWithPriority(InputContinuousPriority, () =>
scheduleUpdateWithHidden(),
);
});
scheduleUpdate();
@@ -549,6 +550,7 @@ describe('ReactDOMTracing', () => {
});
// @gate experimental
// @gate enableNativeEventPriorityInference
it('should properly trace interactions through a multi-pass SuspenseList render', () => {
const SuspenseList = React.SuspenseList;
const Suspense = React.Suspense;
@@ -610,10 +612,9 @@ describe('ReactDOMTracing', () => {
// Schedule an unrelated low priority update that shouldn't be included
// in the previous interaction. This is meant to ensure that we don't
// rely on the whole tree completing to cover up bugs.
Scheduler.unstable_runWithPriority(
Scheduler.unstable_IdlePriority,
() => root.render(<App />),
);
ReactDOM.unstable_runWithPriority(IdleLanePriority, () => {
root.render(<App />);
});
expect(onInteractionTraced).toHaveBeenCalledTimes(1);
expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(