From d7a9ef36afcb80ff56b994a810f9852c50be5d52 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 25 Apr 2022 16:05:11 +0100 Subject: [PATCH] Use @gate FIXME --- .../src/__tests__/ReactDOMFizzServer-test.js | 564 +++++++++--------- 1 file changed, 285 insertions(+), 279 deletions(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index c9c50bf57b492..72ed9f8775dab 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -2190,286 +2190,292 @@ describe('ReactDOMFizzServer', () => { // Disabled because of a WWW late mutations regression. // We may want to re-enable this if we figure out why. + // @gate experimental + // @gate FIXME + it('does not recreate the fallback if server errors and hydration suspends', async () => { + let isClient = false; - // // @gate experimental - // it('does not recreate the fallback if server errors and hydration suspends', async () => { - // let isClient = false; - - // function Child() { - // if (isClient) { - // readText('Yay!'); - // } else { - // throw Error('Oops.'); - // } - // Scheduler.unstable_yieldValue('Yay!'); - // return 'Yay!'; - // } - - // const fallbackRef = React.createRef(); - // function App() { - // return ( - //
- // Loading...

}> - // - // - // - //
- //
- // ); - // } - // await act(async () => { - // const {pipe} = ReactDOMFizzServer.renderToPipeableStream(, { - // onError(error) { - // Scheduler.unstable_yieldValue('[s!] ' + error.message); - // }, - // }); - // pipe(writable); - // }); - // expect(Scheduler).toHaveYielded(['[s!] Oops.']); - - // // The server could not complete this boundary, so we'll retry on the client. - // const serverFallback = container.getElementsByTagName('p')[0]; - // expect(serverFallback.innerHTML).toBe('Loading...'); - - // // Hydrate the tree. This will suspend. - // isClient = true; - // ReactDOMClient.hydrateRoot(container, , { - // onRecoverableError(error) { - // Scheduler.unstable_yieldValue('[c!] ' + error.message); - // }, - // }); - // // This should not report any errors yet. - // expect(Scheduler).toFlushAndYield([]); - // expect(getVisibleChildren(container)).toEqual( - //
- //

Loading...

- //
, - // ); - - // // Normally, hydrating after server error would force a clean client render. - // // However, it suspended so at best we'd only get the same fallback anyway. - // // We don't want to recreate the same fallback in the DOM again because - // // that's extra work and would restart animations etc. Check we don't do that. - // const clientFallback = container.getElementsByTagName('p')[0]; - // expect(serverFallback).toBe(clientFallback); - - // // When we're able to fully hydrate, we expect a clean client render. - // await act(async () => { - // resolveText('Yay!'); - // }); - // expect(Scheduler).toFlushAndYield([ - // 'Yay!', - // '[c!] The server could not finish this Suspense boundary, ' + - // 'likely due to an error during server rendering. ' + - // 'Switched to client rendering.', - // ]); - // expect(getVisibleChildren(container)).toEqual( - //
- // Yay! - //
, - // ); - // }); - - // // @gate experimental - // it( - // 'does not recreate the fallback if server errors and hydration suspends ' + - // 'and root receives a transition', - // async () => { - // let isClient = false; - - // function Child({color}) { - // if (isClient) { - // readText('Yay!'); - // } else { - // throw Error('Oops.'); - // } - // Scheduler.unstable_yieldValue('Yay! (' + color + ')'); - // return 'Yay! (' + color + ')'; - // } - - // const fallbackRef = React.createRef(); - // function App({color}) { - // return ( - //
- // Loading...

}> - // - // - // - //
- //
- // ); - // } - // await act(async () => { - // const {pipe} = ReactDOMFizzServer.renderToPipeableStream( - // , - // { - // onError(error) { - // Scheduler.unstable_yieldValue('[s!] ' + error.message); - // }, - // }, - // ); - // pipe(writable); - // }); - // expect(Scheduler).toHaveYielded(['[s!] Oops.']); - - // // The server could not complete this boundary, so we'll retry on the client. - // const serverFallback = container.getElementsByTagName('p')[0]; - // expect(serverFallback.innerHTML).toBe('Loading...'); - - // // Hydrate the tree. This will suspend. - // isClient = true; - // const root = ReactDOMClient.hydrateRoot(container, , { - // onRecoverableError(error) { - // Scheduler.unstable_yieldValue('[c!] ' + error.message); - // }, - // }); - // // This should not report any errors yet. - // expect(Scheduler).toFlushAndYield([]); - // expect(getVisibleChildren(container)).toEqual( - //
- //

Loading...

- //
, - // ); - - // // Normally, hydrating after server error would force a clean client render. - // // However, it suspended so at best we'd only get the same fallback anyway. - // // We don't want to recreate the same fallback in the DOM again because - // // that's extra work and would restart animations etc. Check we don't do that. - // const clientFallback = container.getElementsByTagName('p')[0]; - // expect(serverFallback).toBe(clientFallback); - - // // Transition updates shouldn't recreate the fallback either. - // React.startTransition(() => { - // root.render(); - // }); - // Scheduler.unstable_flushAll(); - // jest.runAllTimers(); - // const clientFallback2 = container.getElementsByTagName('p')[0]; - // expect(clientFallback2).toBe(serverFallback); - - // // When we're able to fully hydrate, we expect a clean client render. - // await act(async () => { - // resolveText('Yay!'); - // }); - // expect(Scheduler).toFlushAndYield([ - // 'Yay! (red)', - // '[c!] The server could not finish this Suspense boundary, ' + - // 'likely due to an error during server rendering. ' + - // 'Switched to client rendering.', - // 'Yay! (blue)', - // ]); - // expect(getVisibleChildren(container)).toEqual( - //
- // Yay! (blue) - //
, - // ); - // }, - // ); - - // // @gate experimental - // it( - // 'recreates the fallback if server errors and hydration suspends but ' + - // 'client receives new props', - // async () => { - // let isClient = false; - - // function Child() { - // const value = 'Yay!'; - // if (isClient) { - // readText(value); - // } else { - // throw Error('Oops.'); - // } - // Scheduler.unstable_yieldValue(value); - // return value; - // } - - // const fallbackRef = React.createRef(); - // function App({fallbackText}) { - // return ( - //
- // {fallbackText}

}> - // - // - // - //
- //
- // ); - // } - - // await act(async () => { - // const {pipe} = ReactDOMFizzServer.renderToPipeableStream( - // , - // { - // onError(error) { - // Scheduler.unstable_yieldValue('[s!] ' + error.message); - // }, - // }, - // ); - // pipe(writable); - // }); - // expect(Scheduler).toHaveYielded(['[s!] Oops.']); - - // const serverFallback = container.getElementsByTagName('p')[0]; - // expect(serverFallback.innerHTML).toBe('Loading...'); - - // // Hydrate the tree. This will suspend. - // isClient = true; - // const root = ReactDOMClient.hydrateRoot( - // container, - // , - // { - // onRecoverableError(error) { - // Scheduler.unstable_yieldValue('[c!] ' + error.message); - // }, - // }, - // ); - // // This should not report any errors yet. - // expect(Scheduler).toFlushAndYield([]); - // expect(getVisibleChildren(container)).toEqual( - //
- //

Loading...

- //
, - // ); - - // // Normally, hydration after server error would force a clean client render. - // // However, that suspended so at best we'd only get a fallback anyway. - // // We don't want to replace a fallback with the same fallback because - // // that's extra work and would restart animations etc. Verify we don't do that. - // const clientFallback1 = container.getElementsByTagName('p')[0]; - // expect(serverFallback).toBe(clientFallback1); - - // // However, an update may have changed the fallback props. In that case we have to - // // actually force it to re-render on the client and throw away the server one. - // root.render(); - // Scheduler.unstable_flushAll(); - // jest.runAllTimers(); - // expect(Scheduler).toHaveYielded([ - // '[c!] The server could not finish this Suspense boundary, ' + - // 'likely due to an error during server rendering. ' + - // 'Switched to client rendering.', - // ]); - // expect(getVisibleChildren(container)).toEqual( - //
- //

More loading...

- //
, - // ); - // // This should be a clean render without reusing DOM. - // const clientFallback2 = container.getElementsByTagName('p')[0]; - // expect(clientFallback2).not.toBe(clientFallback1); - - // // Verify we can still do a clean content render after. - // await act(async () => { - // resolveText('Yay!'); - // }); - // expect(Scheduler).toFlushAndYield(['Yay!']); - // expect(getVisibleChildren(container)).toEqual( - //
- // Yay! - //
, - // ); - // }, - // ); + function Child() { + if (isClient) { + readText('Yay!'); + } else { + throw Error('Oops.'); + } + Scheduler.unstable_yieldValue('Yay!'); + return 'Yay!'; + } + + const fallbackRef = React.createRef(); + function App() { + return ( +
+ Loading...

}> + + + +
+
+ ); + } + await act(async () => { + const {pipe} = ReactDOMFizzServer.renderToPipeableStream(, { + onError(error) { + Scheduler.unstable_yieldValue('[s!] ' + error.message); + }, + }); + pipe(writable); + }); + expect(Scheduler).toHaveYielded(['[s!] Oops.']); + + // The server could not complete this boundary, so we'll retry on the client. + const serverFallback = container.getElementsByTagName('p')[0]; + expect(serverFallback.innerHTML).toBe('Loading...'); + + // Hydrate the tree. This will suspend. + isClient = true; + ReactDOMClient.hydrateRoot(container, , { + onRecoverableError(error) { + Scheduler.unstable_yieldValue('[c!] ' + error.message); + }, + }); + // This should not report any errors yet. + expect(Scheduler).toFlushAndYield([]); + expect(getVisibleChildren(container)).toEqual( +
+

Loading...

+
, + ); + + // Normally, hydrating after server error would force a clean client render. + // However, it suspended so at best we'd only get the same fallback anyway. + // We don't want to recreate the same fallback in the DOM again because + // that's extra work and would restart animations etc. Check we don't do that. + const clientFallback = container.getElementsByTagName('p')[0]; + expect(serverFallback).toBe(clientFallback); + + // When we're able to fully hydrate, we expect a clean client render. + await act(async () => { + resolveText('Yay!'); + }); + expect(Scheduler).toFlushAndYield([ + 'Yay!', + '[c!] The server could not finish this Suspense boundary, ' + + 'likely due to an error during server rendering. ' + + 'Switched to client rendering.', + ]); + expect(getVisibleChildren(container)).toEqual( +
+ Yay! +
, + ); + }); + + // Disabled because of a WWW late mutations regression. + // We may want to re-enable this if we figure out why. + // @gate experimental + // @gate FIXME + it( + 'does not recreate the fallback if server errors and hydration suspends ' + + 'and root receives a transition', + async () => { + let isClient = false; + + function Child({color}) { + if (isClient) { + readText('Yay!'); + } else { + throw Error('Oops.'); + } + Scheduler.unstable_yieldValue('Yay! (' + color + ')'); + return 'Yay! (' + color + ')'; + } + + const fallbackRef = React.createRef(); + function App({color}) { + return ( +
+ Loading...

}> + + + +
+
+ ); + } + await act(async () => { + const {pipe} = ReactDOMFizzServer.renderToPipeableStream( + , + { + onError(error) { + Scheduler.unstable_yieldValue('[s!] ' + error.message); + }, + }, + ); + pipe(writable); + }); + expect(Scheduler).toHaveYielded(['[s!] Oops.']); + + // The server could not complete this boundary, so we'll retry on the client. + const serverFallback = container.getElementsByTagName('p')[0]; + expect(serverFallback.innerHTML).toBe('Loading...'); + + // Hydrate the tree. This will suspend. + isClient = true; + const root = ReactDOMClient.hydrateRoot(container, , { + onRecoverableError(error) { + Scheduler.unstable_yieldValue('[c!] ' + error.message); + }, + }); + // This should not report any errors yet. + expect(Scheduler).toFlushAndYield([]); + expect(getVisibleChildren(container)).toEqual( +
+

Loading...

+
, + ); + + // Normally, hydrating after server error would force a clean client render. + // However, it suspended so at best we'd only get the same fallback anyway. + // We don't want to recreate the same fallback in the DOM again because + // that's extra work and would restart animations etc. Check we don't do that. + const clientFallback = container.getElementsByTagName('p')[0]; + expect(serverFallback).toBe(clientFallback); + + // Transition updates shouldn't recreate the fallback either. + React.startTransition(() => { + root.render(); + }); + Scheduler.unstable_flushAll(); + jest.runAllTimers(); + const clientFallback2 = container.getElementsByTagName('p')[0]; + expect(clientFallback2).toBe(serverFallback); + + // When we're able to fully hydrate, we expect a clean client render. + await act(async () => { + resolveText('Yay!'); + }); + expect(Scheduler).toFlushAndYield([ + 'Yay! (red)', + '[c!] The server could not finish this Suspense boundary, ' + + 'likely due to an error during server rendering. ' + + 'Switched to client rendering.', + 'Yay! (blue)', + ]); + expect(getVisibleChildren(container)).toEqual( +
+ Yay! (blue) +
, + ); + }, + ); + + // Disabled because of a WWW late mutations regression. + // We may want to re-enable this if we figure out why. + // @gate experimental + // @gate FIXME + it( + 'recreates the fallback if server errors and hydration suspends but ' + + 'client receives new props', + async () => { + let isClient = false; + + function Child() { + const value = 'Yay!'; + if (isClient) { + readText(value); + } else { + throw Error('Oops.'); + } + Scheduler.unstable_yieldValue(value); + return value; + } + + const fallbackRef = React.createRef(); + function App({fallbackText}) { + return ( +
+ {fallbackText}

}> + + + +
+
+ ); + } + + await act(async () => { + const {pipe} = ReactDOMFizzServer.renderToPipeableStream( + , + { + onError(error) { + Scheduler.unstable_yieldValue('[s!] ' + error.message); + }, + }, + ); + pipe(writable); + }); + expect(Scheduler).toHaveYielded(['[s!] Oops.']); + + const serverFallback = container.getElementsByTagName('p')[0]; + expect(serverFallback.innerHTML).toBe('Loading...'); + + // Hydrate the tree. This will suspend. + isClient = true; + const root = ReactDOMClient.hydrateRoot( + container, + , + { + onRecoverableError(error) { + Scheduler.unstable_yieldValue('[c!] ' + error.message); + }, + }, + ); + // This should not report any errors yet. + expect(Scheduler).toFlushAndYield([]); + expect(getVisibleChildren(container)).toEqual( +
+

Loading...

+
, + ); + + // Normally, hydration after server error would force a clean client render. + // However, that suspended so at best we'd only get a fallback anyway. + // We don't want to replace a fallback with the same fallback because + // that's extra work and would restart animations etc. Verify we don't do that. + const clientFallback1 = container.getElementsByTagName('p')[0]; + expect(serverFallback).toBe(clientFallback1); + + // However, an update may have changed the fallback props. In that case we have to + // actually force it to re-render on the client and throw away the server one. + root.render(); + Scheduler.unstable_flushAll(); + jest.runAllTimers(); + expect(Scheduler).toHaveYielded([ + '[c!] The server could not finish this Suspense boundary, ' + + 'likely due to an error during server rendering. ' + + 'Switched to client rendering.', + ]); + expect(getVisibleChildren(container)).toEqual( +
+

More loading...

+
, + ); + // This should be a clean render without reusing DOM. + const clientFallback2 = container.getElementsByTagName('p')[0]; + expect(clientFallback2).not.toBe(clientFallback1); + + // Verify we can still do a clean content render after. + await act(async () => { + resolveText('Yay!'); + }); + expect(Scheduler).toFlushAndYield(['Yay!']); + expect(getVisibleChildren(container)).toEqual( +
+ Yay! +
, + ); + }, + ); // @gate experimental it(