diff --git a/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js b/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js index 28909ccb6716..c85d153fb461 100644 --- a/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js @@ -12,10 +12,9 @@ let React; let ReactDOM; let Suspense; -let ReactCache; let Scheduler; -let TextResource; let act; +let textCache; describe('ReactDOMSuspensePlaceholder', () => { let container; @@ -24,48 +23,78 @@ describe('ReactDOMSuspensePlaceholder', () => { jest.resetModules(); React = require('react'); ReactDOM = require('react-dom'); - ReactCache = require('react-cache'); Scheduler = require('scheduler'); act = require('internal-test-utils').act; Suspense = React.Suspense; container = document.createElement('div'); document.body.appendChild(container); - TextResource = ReactCache.unstable_createResource( - ([text, ms = 0]) => { - return new Promise((resolve, reject) => - setTimeout(() => { - resolve(text); - }, ms), - ); - }, - ([text, ms]) => text, - ); + textCache = new Map(); }); afterEach(() => { document.body.removeChild(container); }); - function advanceTimers(ms) { - // Note: This advances Jest's virtual time but not React's. Use - // ReactNoop.expire for that. - if (typeof ms !== 'number') { - throw new Error('Must specify ms'); + function resolveText(text) { + const record = textCache.get(text); + if (record === undefined) { + const newRecord = { + status: 'resolved', + value: text, + }; + textCache.set(text, newRecord); + } else if (record.status === 'pending') { + const thenable = record.value; + record.status = 'resolved'; + record.value = text; + thenable.pings.forEach(t => t()); + } + } + + function readText(text) { + const record = textCache.get(text); + if (record !== undefined) { + switch (record.status) { + case 'pending': + Scheduler.log(`Suspend! [${text}]`); + throw record.value; + case 'rejected': + throw record.value; + case 'resolved': + return record.value; + } + } else { + Scheduler.log(`Suspend! [${text}]`); + const thenable = { + pings: [], + then(resolve) { + if (newRecord.status === 'pending') { + thenable.pings.push(resolve); + } else { + Promise.resolve().then(() => resolve(newRecord.value)); + } + }, + }; + + const newRecord = { + status: 'pending', + value: thenable, + }; + textCache.set(text, newRecord); + + throw thenable; } - jest.advanceTimersByTime(ms); - // Wait until the end of the current tick - // We cannot use a timer since we're faking them - return Promise.resolve().then(() => {}); } - function Text(props) { - return props.text; + function Text({text}) { + Scheduler.log(text); + return text; } - function AsyncText(props) { - const text = props.text; - TextResource.read([props.text, props.ms]); + function AsyncText({text}) { + readText(text); + Scheduler.log(text); return text; } @@ -82,7 +111,7 @@ describe('ReactDOMSuspensePlaceholder', () => {
- +
@@ -95,9 +124,9 @@ describe('ReactDOMSuspensePlaceholder', () => { expect(window.getComputedStyle(divs[1].current).display).toEqual('none'); expect(window.getComputedStyle(divs[2].current).display).toEqual('none'); - await advanceTimers(500); - - Scheduler.unstable_flushAll(); + await act(async () => { + await resolveText('B'); + }); expect(window.getComputedStyle(divs[0].current).display).toEqual('block'); expect(window.getComputedStyle(divs[1].current).display).toEqual('block'); @@ -110,7 +139,7 @@ describe('ReactDOMSuspensePlaceholder', () => { return ( }> - + ); @@ -118,9 +147,9 @@ describe('ReactDOMSuspensePlaceholder', () => { ReactDOM.render(, container); expect(container.textContent).toEqual('Loading...'); - await advanceTimers(500); - - Scheduler.unstable_flushAll(); + await act(async () => { + await resolveText('B'); + }); expect(container.textContent).toEqual('ABC'); }); @@ -147,7 +176,7 @@ describe('ReactDOMSuspensePlaceholder', () => { }> Sibling - + ); @@ -161,8 +190,16 @@ describe('ReactDOMSuspensePlaceholder', () => { '"display: none;">Loading...', ); + // Update the inline display style. It will be overridden because it's + // inside a hidden fallback. await act(async () => setIsVisible(true)); + expect(container.innerHTML).toEqual( + 'SiblingLoading...', + ); + // Unsuspend. The style should now match the inline prop. + await act(async () => resolveText('Async')); expect(container.innerHTML).toEqual( 'SiblingAsync', );