Skip to content

Commit 845fccd

Browse files
author
Brian Vaughn
committed
Added some additional interaction tests for interleaved suspense and high-pri renders
1 parent be18adc commit 845fccd

File tree

1 file changed

+208
-4
lines changed

1 file changed

+208
-4
lines changed

packages/react/src/__tests__/ReactProfiler-test.internal.js

Lines changed: 208 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2198,9 +2198,9 @@ describe('Profiler', () => {
21982198
return text;
21992199
} catch (promise) {
22002200
if (typeof promise.then === 'function') {
2201-
yieldForRenderer(`Suspend! [${text}]`);
2201+
yieldForRenderer(`Suspend [${text}]`);
22022202
} else {
2203-
yieldForRenderer(`Error! [${text}]`);
2203+
yieldForRenderer(`Error [${text}]`);
22042204
}
22052205
throw promise;
22062206
}
@@ -2242,7 +2242,7 @@ describe('Profiler', () => {
22422242
expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
22432243

22442244
expect(ReactNoop.flush()).toEqual([
2245-
'Suspend! [Async]',
2245+
'Suspend [Async]',
22462246
'Text [Loading...]',
22472247
'Text [Sync]',
22482248
]);
@@ -2422,7 +2422,7 @@ describe('Profiler', () => {
24222422
advanceTimeBy(1500);
24232423
await awaitableAdvanceTimers(1500);
24242424

2425-
expect(renderer).toFlushAll(['Suspend! [loaded]', 'Text [loading]']);
2425+
expect(renderer).toFlushAll(['Suspend [loaded]', 'Text [loading]']);
24262426
expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
24272427

24282428
advanceTimeBy(2500);
@@ -2473,6 +2473,210 @@ describe('Profiler', () => {
24732473
onInteractionScheduledWorkCompleted,
24742474
).toHaveBeenLastNotifiedOfInteraction(interaction);
24752475
});
2476+
2477+
it('handles high-pri renderers between suspended and resolved (sync) trees', async () => {
2478+
let instance;
2479+
class ClassComponent extends React.Component {
2480+
render() {
2481+
instance = this;
2482+
yieldForRenderer(`ClassComponent [${this.props.value}]`);
2483+
return this.props.value;
2484+
}
2485+
}
2486+
2487+
const initialRenderInteraction = {
2488+
id: 0,
2489+
name: 'initial render',
2490+
timestamp: mockNow(),
2491+
};
2492+
2493+
const onRender = jest.fn();
2494+
let renderer;
2495+
SchedulerTracing.unstable_trace(
2496+
initialRenderInteraction.name,
2497+
initialRenderInteraction.timestamp,
2498+
() => {
2499+
renderer = ReactTestRenderer.create(
2500+
<React.unstable_Profiler id="app" onRender={onRender}>
2501+
<React.Placeholder
2502+
delayMs={2000}
2503+
fallback={<Text text="loading" />}>
2504+
<AsyncText text="loaded" ms={1000} />
2505+
</React.Placeholder>
2506+
<ClassComponent value="initial" />
2507+
</React.unstable_Profiler>,
2508+
);
2509+
},
2510+
);
2511+
expect(renderer.toJSON()).toEqual(['loading', 'initial']);
2512+
2513+
expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
2514+
expect(onRender).toHaveBeenCalledTimes(2); // Sync null commit, placeholder commit
2515+
expect(onRender.mock.calls[0][6]).toMatchInteractions([
2516+
initialRenderInteraction,
2517+
]);
2518+
onRender.mockClear();
2519+
2520+
const highPriUpdateInteraction = {
2521+
id: 1,
2522+
name: 'hiPriUpdate',
2523+
timestamp: mockNow(),
2524+
};
2525+
2526+
const originalPromise = resourcePromise;
2527+
2528+
renderer.unstable_flushSync(() => {
2529+
SchedulerTracing.unstable_trace(
2530+
highPriUpdateInteraction.name,
2531+
highPriUpdateInteraction.timestamp,
2532+
() => {
2533+
renderer.update(
2534+
<React.unstable_Profiler id="app" onRender={onRender}>
2535+
<React.Placeholder
2536+
delayMs={2000}
2537+
fallback={<Text text="loading" />}>
2538+
<AsyncText text="loaded" ms={1000} />
2539+
</React.Placeholder>
2540+
<ClassComponent value="updated" />
2541+
</React.unstable_Profiler>,
2542+
);
2543+
},
2544+
);
2545+
});
2546+
expect(renderer.toJSON()).toEqual(['loading', 'updated']);
2547+
2548+
expect(onRender).toHaveBeenCalledTimes(2); // Sync null commit, placeholder commit
2549+
expect(onRender.mock.calls[0][6]).toMatchInteractions([
2550+
initialRenderInteraction,
2551+
highPriUpdateInteraction,
2552+
]);
2553+
onRender.mockClear();
2554+
2555+
expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
2556+
2557+
advanceTimeBy(1000);
2558+
jest.advanceTimersByTime(1000);
2559+
await originalPromise;
2560+
expect(renderer.toJSON()).toEqual(['loaded', 'updated']);
2561+
2562+
expect(onRender).toHaveBeenCalledTimes(2);
2563+
expect(onRender.mock.calls[0][6]).toMatchInteractions([
2564+
initialRenderInteraction,
2565+
highPriUpdateInteraction,
2566+
]);
2567+
2568+
expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2);
2569+
expect(
2570+
onInteractionScheduledWorkCompleted.mock.calls[0][0],
2571+
).toMatchInteraction(initialRenderInteraction);
2572+
expect(
2573+
onInteractionScheduledWorkCompleted.mock.calls[1][0],
2574+
).toMatchInteraction(highPriUpdateInteraction);
2575+
});
2576+
2577+
it('handles high-pri renderers between suspended and resolved (async) trees', async () => {
2578+
let instance;
2579+
class ClassComponent extends React.Component {
2580+
render() {
2581+
instance = this;
2582+
yieldForRenderer(`ClassComponent [${this.props.value}]`);
2583+
return this.props.value;
2584+
}
2585+
}
2586+
2587+
const initialRenderInteraction = {
2588+
id: 0,
2589+
name: 'initial render',
2590+
timestamp: mockNow(),
2591+
};
2592+
2593+
const onRender = jest.fn();
2594+
let renderer;
2595+
SchedulerTracing.unstable_trace(
2596+
initialRenderInteraction.name,
2597+
initialRenderInteraction.timestamp,
2598+
() => {
2599+
renderer = ReactTestRenderer.create(
2600+
<React.unstable_Profiler id="app" onRender={onRender}>
2601+
<React.Placeholder
2602+
delayMs={2000}
2603+
fallback={<Text text="loading" />}>
2604+
<AsyncText text="loaded" ms={1000} />
2605+
</React.Placeholder>
2606+
<ClassComponent value="initial" />
2607+
</React.unstable_Profiler>,
2608+
{unstable_isAsync: true},
2609+
);
2610+
},
2611+
);
2612+
expect(renderer).toFlushAll([
2613+
'Suspend [loaded]',
2614+
'Text [loading]',
2615+
'ClassComponent [initial]',
2616+
]);
2617+
2618+
expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
2619+
expect(onRender).not.toHaveBeenCalled();
2620+
2621+
advanceTimeBy(500);
2622+
jest.advanceTimersByTime(500);
2623+
2624+
const highPriUpdateInteraction = {
2625+
id: 1,
2626+
name: 'hiPriUpdate',
2627+
timestamp: mockNow(),
2628+
};
2629+
2630+
const originalPromise = resourcePromise;
2631+
2632+
renderer.unstable_flushSync(() => {
2633+
SchedulerTracing.unstable_trace(
2634+
highPriUpdateInteraction.name,
2635+
highPriUpdateInteraction.timestamp,
2636+
() => {
2637+
renderer.update(
2638+
<React.unstable_Profiler id="app" onRender={onRender}>
2639+
<React.Placeholder
2640+
delayMs={2000}
2641+
fallback={<Text text="loading" />}>
2642+
<AsyncText text="loaded" ms={1000} />
2643+
</React.Placeholder>
2644+
<ClassComponent value="updated" />
2645+
</React.unstable_Profiler>,
2646+
);
2647+
},
2648+
);
2649+
});
2650+
expect(renderer.toJSON()).toEqual(['loading', 'updated']);
2651+
2652+
expect(onRender).toHaveBeenCalledTimes(1);
2653+
expect(onRender.mock.calls[0][6]).toMatchInteractions([
2654+
highPriUpdateInteraction,
2655+
]);
2656+
onRender.mockClear();
2657+
2658+
expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
2659+
expect(
2660+
onInteractionScheduledWorkCompleted,
2661+
).toHaveBeenLastNotifiedOfInteraction(highPriUpdateInteraction);
2662+
2663+
advanceTimeBy(500);
2664+
jest.advanceTimersByTime(500);
2665+
await originalPromise;
2666+
expect(renderer).toFlushAll(['AsyncText [loaded]']);
2667+
expect(renderer.toJSON()).toEqual(['loaded', 'updated']);
2668+
2669+
expect(onRender).toHaveBeenCalledTimes(1);
2670+
expect(onRender.mock.calls[0][6]).toMatchInteractions([
2671+
initialRenderInteraction,
2672+
highPriUpdateInteraction,
2673+
]);
2674+
2675+
expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2);
2676+
expect(
2677+
onInteractionScheduledWorkCompleted,
2678+
).toHaveBeenLastNotifiedOfInteraction(initialRenderInteraction);
2679+
});
24762680
});
24772681
});
24782682
});

0 commit comments

Comments
 (0)