diff --git a/packages/react/src/__tests__/ReactProfiler-test.internal.js b/packages/react/src/__tests__/ReactProfiler-test.internal.js index 073c5053ada78..3d08f3ae33e24 100644 --- a/packages/react/src/__tests__/ReactProfiler-test.internal.js +++ b/packages/react/src/__tests__/ReactProfiler-test.internal.js @@ -2198,9 +2198,9 @@ describe('Profiler', () => { return text; } catch (promise) { if (typeof promise.then === 'function') { - yieldForRenderer(`Suspend! [${text}]`); + yieldForRenderer(`Suspend [${text}]`); } else { - yieldForRenderer(`Error! [${text}]`); + yieldForRenderer(`Error [${text}]`); } throw promise; } @@ -2242,7 +2242,7 @@ describe('Profiler', () => { expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0); expect(ReactNoop.flush()).toEqual([ - 'Suspend! [Async]', + 'Suspend [Async]', 'Text [Loading...]', 'Text [Sync]', ]); @@ -2422,7 +2422,7 @@ describe('Profiler', () => { advanceTimeBy(1500); await awaitableAdvanceTimers(1500); - expect(renderer).toFlushAll(['Suspend! [loaded]', 'Text [loading]']); + expect(renderer).toFlushAll(['Suspend [loaded]', 'Text [loading]']); expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled(); advanceTimeBy(2500); @@ -2473,6 +2473,192 @@ describe('Profiler', () => { onInteractionScheduledWorkCompleted, ).toHaveBeenLastNotifiedOfInteraction(interaction); }); + + it('handles high-pri renderers between suspended and resolved (sync) trees', async () => { + const initialRenderInteraction = { + id: 0, + name: 'initial render', + timestamp: mockNow(), + }; + + const onRender = jest.fn(); + let renderer; + SchedulerTracing.unstable_trace( + initialRenderInteraction.name, + initialRenderInteraction.timestamp, + () => { + renderer = ReactTestRenderer.create( + + }> + + + + , + ); + }, + ); + expect(renderer.toJSON()).toEqual(['loading', 'initial']); + + expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled(); + expect(onRender).toHaveBeenCalledTimes(2); // Sync null commit, placeholder commit + expect(onRender.mock.calls[0][6]).toMatchInteractions([ + initialRenderInteraction, + ]); + onRender.mockClear(); + + const highPriUpdateInteraction = { + id: 1, + name: 'hiPriUpdate', + timestamp: mockNow(), + }; + + const originalPromise = resourcePromise; + + renderer.unstable_flushSync(() => { + SchedulerTracing.unstable_trace( + highPriUpdateInteraction.name, + highPriUpdateInteraction.timestamp, + () => { + renderer.update( + + }> + + + + , + ); + }, + ); + }); + expect(renderer.toJSON()).toEqual(['loading', 'updated']); + + expect(onRender).toHaveBeenCalledTimes(2); // Sync null commit, placeholder commit + expect(onRender.mock.calls[0][6]).toMatchInteractions([ + initialRenderInteraction, + highPriUpdateInteraction, + ]); + onRender.mockClear(); + + expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled(); + + advanceTimeBy(1000); + jest.advanceTimersByTime(1000); + await originalPromise; + expect(renderer.toJSON()).toEqual(['loaded', 'updated']); + + expect(onRender).toHaveBeenCalledTimes(2); + expect(onRender.mock.calls[0][6]).toMatchInteractions([ + initialRenderInteraction, + highPriUpdateInteraction, + ]); + + expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2); + expect( + onInteractionScheduledWorkCompleted.mock.calls[0][0], + ).toMatchInteraction(initialRenderInteraction); + expect( + onInteractionScheduledWorkCompleted.mock.calls[1][0], + ).toMatchInteraction(highPriUpdateInteraction); + }); + + it('handles high-pri renderers between suspended and resolved (async) trees', async () => { + const initialRenderInteraction = { + id: 0, + name: 'initial render', + timestamp: mockNow(), + }; + + const onRender = jest.fn(); + let renderer; + SchedulerTracing.unstable_trace( + initialRenderInteraction.name, + initialRenderInteraction.timestamp, + () => { + renderer = ReactTestRenderer.create( + + }> + + + + , + {unstable_isAsync: true}, + ); + }, + ); + expect(renderer).toFlushAll([ + 'Suspend [loaded]', + 'Text [loading]', + 'Text [initial]', + ]); + + expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled(); + expect(onRender).not.toHaveBeenCalled(); + + advanceTimeBy(500); + jest.advanceTimersByTime(500); + + const highPriUpdateInteraction = { + id: 1, + name: 'hiPriUpdate', + timestamp: mockNow(), + }; + + const originalPromise = resourcePromise; + + renderer.unstable_flushSync(() => { + SchedulerTracing.unstable_trace( + highPriUpdateInteraction.name, + highPriUpdateInteraction.timestamp, + () => { + renderer.update( + + }> + + + + , + ); + }, + ); + }); + expect(renderer.toJSON()).toEqual(['loading', 'updated']); + + expect(onRender).toHaveBeenCalledTimes(1); + expect(onRender.mock.calls[0][6]).toMatchInteractions([ + highPriUpdateInteraction, + ]); + onRender.mockClear(); + + expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1); + expect( + onInteractionScheduledWorkCompleted, + ).toHaveBeenLastNotifiedOfInteraction(highPriUpdateInteraction); + + advanceTimeBy(500); + jest.advanceTimersByTime(500); + await originalPromise; + expect(renderer).toFlushAll(['AsyncText [loaded]']); + expect(renderer.toJSON()).toEqual(['loaded', 'updated']); + + expect(onRender).toHaveBeenCalledTimes(1); + expect(onRender.mock.calls[0][6]).toMatchInteractions([ + initialRenderInteraction, + highPriUpdateInteraction, + ]); + + expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2); + expect( + onInteractionScheduledWorkCompleted, + ).toHaveBeenLastNotifiedOfInteraction(initialRenderInteraction); + }); }); }); });