@@ -19,6 +19,7 @@ let Suspense;
1919let  SuspenseList ; 
2020let  useSyncExternalStore ; 
2121let  useSyncExternalStoreWithSelector ; 
22+ let  use ; 
2223let  PropTypes ; 
2324let  textCache ; 
2425let  window ; 
@@ -42,6 +43,7 @@ describe('ReactDOMFizzServer', () => {
4243    Suspense  =  React . Suspense ; 
4344    if  ( gate ( flags  =>  flags . enableSuspenseList ) )  { 
4445      SuspenseList  =  React . SuspenseList ; 
46+       use  =  React . experimental_use ; 
4547    } 
4648
4749    PropTypes  =  require ( 'prop-types' ) ; 
@@ -5243,5 +5245,215 @@ describe('ReactDOMFizzServer', () => {
52435245        console . error  =  originalConsoleError ; 
52445246      } 
52455247    } ) ; 
5248+ 
5249+     // @gate  enableUseHook 
5250+     it ( 'basic use(promise)' ,  async  ( )  =>  { 
5251+       const  promiseA  =  Promise . resolve ( 'A' ) ; 
5252+       const  promiseB  =  Promise . resolve ( 'B' ) ; 
5253+       const  promiseC  =  Promise . resolve ( 'C' ) ; 
5254+ 
5255+       function  Async ( )  { 
5256+         return  use ( promiseA )  +  use ( promiseB )  +  use ( promiseC ) ; 
5257+       } 
5258+ 
5259+       function  App ( )  { 
5260+         return  ( 
5261+           < Suspense  fallback = "Loading..." > 
5262+             < Async  /> 
5263+           </ Suspense > 
5264+         ) ; 
5265+       } 
5266+ 
5267+       await  act ( async  ( )  =>  { 
5268+         const  { pipe}  =  ReactDOMFizzServer . renderToPipeableStream ( < App  /> ) ; 
5269+         pipe ( writable ) ; 
5270+       } ) ; 
5271+ 
5272+       // TODO: The `act` implementation in this file doesn't unwrap microtasks 
5273+       // automatically. We can't use the same `act` we use for Fiber tests 
5274+       // because that relies on the mock Scheduler. Doesn't affect any public 
5275+       // API but we might want to fix this for our own internal tests. 
5276+       // 
5277+       // For now, wait for each promise in sequence. 
5278+       await  act ( async  ( )  =>  { 
5279+         await  promiseA ; 
5280+       } ) ; 
5281+       await  act ( async  ( )  =>  { 
5282+         await  promiseB ; 
5283+       } ) ; 
5284+       await  act ( async  ( )  =>  { 
5285+         await  promiseC ; 
5286+       } ) ; 
5287+ 
5288+       expect ( getVisibleChildren ( container ) ) . toEqual ( 'ABC' ) ; 
5289+ 
5290+       ReactDOMClient . hydrateRoot ( container ,  < App  /> ) ; 
5291+       expect ( Scheduler ) . toFlushAndYield ( [ ] ) ; 
5292+       expect ( getVisibleChildren ( container ) ) . toEqual ( 'ABC' ) ; 
5293+     } ) ; 
5294+ 
5295+     // @gate  enableUseHook 
5296+     it ( 'use(promise) in multiple components' ,  async  ( )  =>  { 
5297+       const  promiseA  =  Promise . resolve ( 'A' ) ; 
5298+       const  promiseB  =  Promise . resolve ( 'B' ) ; 
5299+       const  promiseC  =  Promise . resolve ( 'C' ) ; 
5300+       const  promiseD  =  Promise . resolve ( 'D' ) ; 
5301+ 
5302+       function  Child ( { prefix} )  { 
5303+         return  prefix  +  use ( promiseC )  +  use ( promiseD ) ; 
5304+       } 
5305+ 
5306+       function  Parent ( )  { 
5307+         return  < Child  prefix = { use ( promiseA )  +  use ( promiseB ) }  /> ; 
5308+       } 
5309+ 
5310+       function  App ( )  { 
5311+         return  ( 
5312+           < Suspense  fallback = "Loading..." > 
5313+             < Parent  /> 
5314+           </ Suspense > 
5315+         ) ; 
5316+       } 
5317+ 
5318+       await  act ( async  ( )  =>  { 
5319+         const  { pipe}  =  ReactDOMFizzServer . renderToPipeableStream ( < App  /> ) ; 
5320+         pipe ( writable ) ; 
5321+       } ) ; 
5322+ 
5323+       // TODO: The `act` implementation in this file doesn't unwrap microtasks 
5324+       // automatically. We can't use the same `act` we use for Fiber tests 
5325+       // because that relies on the mock Scheduler. Doesn't affect any public 
5326+       // API but we might want to fix this for our own internal tests. 
5327+       // 
5328+       // For now, wait for each promise in sequence. 
5329+       await  act ( async  ( )  =>  { 
5330+         await  promiseA ; 
5331+       } ) ; 
5332+       await  act ( async  ( )  =>  { 
5333+         await  promiseB ; 
5334+       } ) ; 
5335+       await  act ( async  ( )  =>  { 
5336+         await  promiseC ; 
5337+       } ) ; 
5338+       await  act ( async  ( )  =>  { 
5339+         await  promiseD ; 
5340+       } ) ; 
5341+ 
5342+       expect ( getVisibleChildren ( container ) ) . toEqual ( 'ABCD' ) ; 
5343+ 
5344+       ReactDOMClient . hydrateRoot ( container ,  < App  /> ) ; 
5345+       expect ( Scheduler ) . toFlushAndYield ( [ ] ) ; 
5346+       expect ( getVisibleChildren ( container ) ) . toEqual ( 'ABCD' ) ; 
5347+     } ) ; 
5348+ 
5349+     // @gate  enableUseHook 
5350+     it ( 'using a rejected promise will throw' ,  async  ( )  =>  { 
5351+       const  promiseA  =  Promise . resolve ( 'A' ) ; 
5352+       const  promiseB  =  Promise . reject ( new  Error ( 'Oops!' ) ) ; 
5353+       const  promiseC  =  Promise . resolve ( 'C' ) ; 
5354+ 
5355+       // Jest/Node will raise an unhandled rejected error unless we await this. It 
5356+       // works fine in the browser, though. 
5357+       await  expect ( promiseB ) . rejects . toThrow ( 'Oops!' ) ; 
5358+ 
5359+       function  Async ( )  { 
5360+         return  use ( promiseA )  +  use ( promiseB )  +  use ( promiseC ) ; 
5361+       } 
5362+ 
5363+       class  ErrorBoundary  extends  React . Component  { 
5364+         state  =  { error : null } ; 
5365+         static  getDerivedStateFromError ( error )  { 
5366+           return  { error} ; 
5367+         } 
5368+         render ( )  { 
5369+           if  ( this . state . error )  { 
5370+             return  this . state . error . message ; 
5371+           } 
5372+           return  this . props . children ; 
5373+         } 
5374+       } 
5375+ 
5376+       function  App ( )  { 
5377+         return  ( 
5378+           < Suspense  fallback = "Loading..." > 
5379+             < ErrorBoundary > 
5380+               < Async  /> 
5381+             </ ErrorBoundary > 
5382+           </ Suspense > 
5383+         ) ; 
5384+       } 
5385+ 
5386+       const  reportedServerErrors  =  [ ] ; 
5387+       await  act ( async  ( )  =>  { 
5388+         const  { pipe}  =  ReactDOMFizzServer . renderToPipeableStream ( < App  /> ,  { 
5389+           onError ( error )  { 
5390+             reportedServerErrors . push ( error ) ; 
5391+           } , 
5392+         } ) ; 
5393+         pipe ( writable ) ; 
5394+       } ) ; 
5395+ 
5396+       // TODO: The `act` implementation in this file doesn't unwrap microtasks 
5397+       // automatically. We can't use the same `act` we use for Fiber tests 
5398+       // because that relies on the mock Scheduler. Doesn't affect any public 
5399+       // API but we might want to fix this for our own internal tests. 
5400+       // 
5401+       // For now, wait for each promise in sequence. 
5402+       await  act ( async  ( )  =>  { 
5403+         await  promiseA ; 
5404+       } ) ; 
5405+       await  act ( async  ( )  =>  { 
5406+         await  expect ( promiseB ) . rejects . toThrow ( 'Oops!' ) ; 
5407+       } ) ; 
5408+       await  act ( async  ( )  =>  { 
5409+         await  promiseC ; 
5410+       } ) ; 
5411+ 
5412+       expect ( getVisibleChildren ( container ) ) . toEqual ( 'Loading...' ) ; 
5413+       expect ( reportedServerErrors . length ) . toBe ( 1 ) ; 
5414+       expect ( reportedServerErrors [ 0 ] . message ) . toBe ( 'Oops!' ) ; 
5415+ 
5416+       const  reportedClientErrors  =  [ ] ; 
5417+       ReactDOMClient . hydrateRoot ( container ,  < App  /> ,  { 
5418+         onRecoverableError ( error )  { 
5419+           reportedClientErrors . push ( error ) ; 
5420+         } , 
5421+       } ) ; 
5422+       expect ( Scheduler ) . toFlushAndYield ( [ ] ) ; 
5423+       expect ( getVisibleChildren ( container ) ) . toEqual ( 'Oops!' ) ; 
5424+       expect ( reportedClientErrors . length ) . toBe ( 1 ) ; 
5425+       if  ( __DEV__ )  { 
5426+         expect ( reportedClientErrors [ 0 ] . message ) . toBe ( 'Oops!' ) ; 
5427+       }  else  { 
5428+         expect ( reportedClientErrors [ 0 ] . message ) . toBe ( 
5429+           'The server could not finish this Suspense boundary, likely due to '  + 
5430+             'an error during server rendering. Switched to client rendering.' , 
5431+         ) ; 
5432+       } 
5433+     } ) ; 
5434+ 
5435+     // @gate  enableUseHook 
5436+     it ( "use a promise that's already been instrumented and resolved" ,  async  ( )  =>  { 
5437+       const  thenable  =  { 
5438+         status : 'fulfilled' , 
5439+         value : 'Hi' , 
5440+         then ( )  { } , 
5441+       } ; 
5442+ 
5443+       // This will never suspend because the thenable already resolved 
5444+       function  App ( )  { 
5445+         return  use ( thenable ) ; 
5446+       } 
5447+ 
5448+       await  act ( async  ( )  =>  { 
5449+         const  { pipe}  =  ReactDOMFizzServer . renderToPipeableStream ( < App  /> ) ; 
5450+         pipe ( writable ) ; 
5451+       } ) ; 
5452+       expect ( getVisibleChildren ( container ) ) . toEqual ( 'Hi' ) ; 
5453+ 
5454+       ReactDOMClient . hydrateRoot ( container ,  < App  /> ) ; 
5455+       expect ( Scheduler ) . toFlushAndYield ( [ ] ) ; 
5456+       expect ( getVisibleChildren ( container ) ) . toEqual ( 'Hi' ) ; 
5457+     } ) ; 
52465458  } ) ; 
52475459} ) ; 
0 commit comments