From f8c8030bf162150dfb809e4f4fbd22f988d8caa3 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 7 Sep 2018 12:55:00 -0700 Subject: [PATCH] Added failing tests for two remaining suspense cases --- .../__tests__/ReactProfiler-test.internal.js | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/packages/react/src/__tests__/ReactProfiler-test.internal.js b/packages/react/src/__tests__/ReactProfiler-test.internal.js index df188258ad1be..4a15e9ccb33b5 100644 --- a/packages/react/src/__tests__/ReactProfiler-test.internal.js +++ b/packages/react/src/__tests__/ReactProfiler-test.internal.js @@ -1240,6 +1240,7 @@ describe('Profiler', () => { loadModules({ enableSchedulerTracking: true, + enableSuspense: true, }); throwInOnInteractionScheduledWorkCompleted = false; @@ -2301,5 +2302,158 @@ describe('Profiler', () => { onInteractionScheduledWorkCompleted, ).toHaveBeenLastNotifiedOfInteraction(interaction); }); + + it('does not prematurely complete for suspended sync renders', async () => { + const SimpleCacheProvider = require('simple-cache-provider'); + let cache; + function invalidateCache() { + cache = SimpleCacheProvider.createCache(invalidateCache); + } + invalidateCache(); + + let resourcePromise; + const TextResource = SimpleCacheProvider.createResource( + ([text, ms = 0]) => { + resourcePromise = new Promise((resolve, reject) => + setTimeout(() => resolve(text), ms), + ); + return resourcePromise; + }, + ([text, ms]) => text, + ); + + function AsyncText({ms, text}) { + TextResource.read(cache, [text, ms]); + return ; + } + + function Text({text}) { + return text; + } + + const interaction = { + id: 0, + name: 'initial render', + timestamp: mockNow(), + }; + + const onRender = jest.fn(); + SchedulerTracking.unstable_track( + interaction.name, + interaction.timestamp, + () => { + ReactTestRenderer.create( + + }> + + + , + ); + }, + ); + + expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled(); + + jest.runAllTimers(); + await resourcePromise; + + expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1); + expect( + onInteractionScheduledWorkCompleted, + ).toHaveBeenLastNotifiedOfInteraction(interaction); + }); + + it('does not prematurely complete for suspended renders that have exceeded their deadline', async () => { + function awaitableAdvanceTimers(ms) { + jest.advanceTimersByTime(ms); + // Wait until the end of the current tick + return new Promise(resolve => { + setImmediate(resolve); + }); + } + + const SimpleCacheProvider = require('simple-cache-provider'); + let cache; + function invalidateCache() { + cache = SimpleCacheProvider.createCache(invalidateCache); + } + invalidateCache(); + + const TextResource = SimpleCacheProvider.createResource( + ([text, ms = 0]) => { + return new Promise((resolve, reject) => { + setTimeout(() => { + ReactTestRenderer.unstable_yield(`Promise resolved [${text}]`); + resolve(text); + }, ms); + }); + }, + ([text, ms]) => text, + ); + + function AsyncText({ms, text}) { + try { + TextResource.read(cache, [text, ms]); + ReactTestRenderer.unstable_yield(`AsyncText [${text}]`); + return ; + } catch (promise) { + if (typeof promise.then === 'function') { + ReactTestRenderer.unstable_yield(`Suspend! [${text}]`); + } else { + ReactTestRenderer.unstable_yield(`Error! [${text}]`); + } + throw promise; + } + } + + function Text({text}) { + ReactTestRenderer.unstable_yield(`Text [${text}]`); + return text; + } + + const interaction = { + id: 0, + name: 'initial render', + timestamp: mockNow(), + }; + + const onRender = jest.fn(); + let renderer; + SchedulerTracking.unstable_track( + interaction.name, + interaction.timestamp, + () => { + renderer = ReactTestRenderer.create( + + }> + + + , + { + unstable_isAsync: true, + }, + ); + }, + ); + + advanceTimeBy(1500); + await awaitableAdvanceTimers(1500); + + expect(renderer).toFlushAll(['Suspend! [loaded]', 'Text [loading]']); + expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled(); + + advanceTimeBy(2500); + await awaitableAdvanceTimers(2500); + + expect(renderer).toFlushAll(['AsyncText [loaded]']); + expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1); + expect( + onInteractionScheduledWorkCompleted, + ).toHaveBeenLastNotifiedOfInteraction(interaction); + }); }); });