@@ -7178,6 +7178,141 @@ describe('ReactDOMFizzServer', () => {
71787178 ) ;
71797179 } ) ;
71807180
7181+ // @gate enablePostpone
7182+ it ( 'can client render a boundary after having already postponed' , async ( ) => {
7183+ let prerendering = true ;
7184+ let ssr = true ;
7185+
7186+ function Postpone ( ) {
7187+ if ( prerendering ) {
7188+ React . unstable_postpone ( ) ;
7189+ }
7190+ return 'Hello' ;
7191+ }
7192+
7193+ function ServerError ( ) {
7194+ if ( ssr ) {
7195+ throw new Error ( 'server error' ) ;
7196+ }
7197+ return 'World' ;
7198+ }
7199+
7200+ function App ( ) {
7201+ return (
7202+ < div >
7203+ < Suspense fallback = "Loading1" >
7204+ < Postpone />
7205+ < ServerError />
7206+ </ Suspense >
7207+ < Suspense fallback = "Loading2" >
7208+ < Postpone />
7209+ </ Suspense >
7210+ </ div >
7211+ ) ;
7212+ }
7213+
7214+ const prerenderErrors = [ ] ;
7215+ const prerendered = await ReactDOMFizzStatic . prerenderToNodeStream (
7216+ < App /> ,
7217+ {
7218+ onError ( x ) {
7219+ prerenderErrors . push ( x . message ) ;
7220+ } ,
7221+ } ,
7222+ ) ;
7223+ expect ( prerendered . postponed ) . not . toBe ( null ) ;
7224+
7225+ prerendering = false ;
7226+
7227+ const ssrErrors = [ ] ;
7228+
7229+ const resumed = ReactDOMFizzServer . resumeToPipeableStream (
7230+ < App /> ,
7231+ JSON . parse ( JSON . stringify ( prerendered . postponed ) ) ,
7232+ {
7233+ onError ( x ) {
7234+ ssrErrors . push ( x . message ) ;
7235+ } ,
7236+ } ,
7237+ ) ;
7238+
7239+ const windowErrors = [ ] ;
7240+ function globalError ( e ) {
7241+ windowErrors . push ( e . message ) ;
7242+ }
7243+ window . addEventListener ( 'error' , globalError ) ;
7244+
7245+ // Create a separate stream so it doesn't close the writable. I.e. simple concat.
7246+ const preludeWritable = new Stream . PassThrough ( ) ;
7247+ preludeWritable . setEncoding ( 'utf8' ) ;
7248+ preludeWritable . on ( 'data' , chunk => {
7249+ writable . write ( chunk ) ;
7250+ } ) ;
7251+
7252+ await act ( ( ) => {
7253+ prerendered . prelude . pipe ( preludeWritable ) ;
7254+ } ) ;
7255+
7256+ expect ( windowErrors ) . toEqual ( [ ] ) ;
7257+
7258+ expect ( getVisibleChildren ( container ) ) . toEqual (
7259+ < div >
7260+ { 'Loading1' }
7261+ { 'Loading2' }
7262+ </ div > ,
7263+ ) ;
7264+
7265+ await act ( ( ) => {
7266+ resumed . pipe ( writable ) ;
7267+ } ) ;
7268+
7269+ expect ( prerenderErrors ) . toEqual ( [ 'server error' ] ) ;
7270+
7271+ // Since this errored, we shouldn't have to replay it.
7272+ expect ( ssrErrors ) . toEqual ( [ ] ) ;
7273+
7274+ expect ( windowErrors ) . toEqual ( [ ] ) ;
7275+
7276+ // Still loading...
7277+ expect ( getVisibleChildren ( container ) ) . toEqual (
7278+ < div >
7279+ { 'Loading1' }
7280+ { 'Hello' }
7281+ </ div > ,
7282+ ) ;
7283+
7284+ const recoverableErrors = [ ] ;
7285+
7286+ ssr = false ;
7287+
7288+ await clientAct ( ( ) => {
7289+ ReactDOMClient . hydrateRoot ( container , < App /> , {
7290+ onRecoverableError ( x ) {
7291+ recoverableErrors . push ( x . message ) ;
7292+ } ,
7293+ } ) ;
7294+ } ) ;
7295+
7296+ expect ( recoverableErrors ) . toEqual (
7297+ __DEV__
7298+ ? [ 'server error' ]
7299+ : [
7300+ 'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.' ,
7301+ ] ,
7302+ ) ;
7303+ expect ( getVisibleChildren ( container ) ) . toEqual (
7304+ < div >
7305+ { 'Hello' }
7306+ { 'World' }
7307+ { 'Hello' }
7308+ </ div > ,
7309+ ) ;
7310+
7311+ expect ( windowErrors ) . toEqual ( [ ] ) ;
7312+
7313+ window . removeEventListener ( 'error' , globalError ) ;
7314+ } ) ;
7315+
71817316 // @gate enablePostpone
71827317 it ( 'can postpone in fallback' , async ( ) => {
71837318 let prerendering = true ;
0 commit comments