@@ -252,12 +252,6 @@ describe('ReactDOMServerPartialHydration', () => {
252
252
} ) ;
253
253
254
254
it ( 'falls back to client rendering boundary on mismatch' , async ( ) => {
255
- // We can't use the toErrorDev helper here because this is async.
256
- const originalConsoleError = console . error ;
257
- const mockError = jest . fn ( ) ;
258
- console . error = ( ...args ) => {
259
- mockError ( ...args . map ( normalizeCodeLocInfo ) ) ;
260
- } ;
261
255
let client = false ;
262
256
let suspend = false ;
263
257
let resolve ;
@@ -294,77 +288,58 @@ describe('ReactDOMServerPartialHydration', () => {
294
288
</ Suspense >
295
289
) ;
296
290
}
297
- try {
298
- const finalHTML = ReactDOMServer . renderToString ( < App /> ) ;
299
- const container = document . createElement ( 'section' ) ;
300
- container . innerHTML = finalHTML ;
301
- assertLog ( [ 'Hello' , 'Component' , 'Component' , 'Component' , 'Component' ] ) ;
302
-
303
- expect ( container . innerHTML ) . toBe (
304
- '<!--$-->Hello<div>Component</div><div>Component</div><div>Component</div><div>Component</div><!--/$-->' ,
305
- ) ;
291
+ const finalHTML = ReactDOMServer . renderToString ( < App /> ) ;
292
+ const container = document . createElement ( 'section' ) ;
293
+ container . innerHTML = finalHTML ;
294
+ assertLog ( [ 'Hello' , 'Component' , 'Component' , 'Component' , 'Component' ] ) ;
306
295
307
- suspend = true ;
308
- client = true ;
296
+ expect ( container . innerHTML ) . toBe (
297
+ '<!--$-->Hello<div>Component</div><div>Component</div><div>Component</div><div>Component</div><!--/$-->' ,
298
+ ) ;
309
299
310
- ReactDOMClient . hydrateRoot ( container , < App /> , {
311
- onRecoverableError ( error ) {
312
- Scheduler . log ( normalizeError ( error . message ) ) ;
313
- } ,
314
- } ) ;
315
- await waitForAll ( [ 'Suspend' ] ) ;
316
- jest . runAllTimers ( ) ;
300
+ suspend = true ;
301
+ client = true ;
317
302
318
- // Unchanged
319
- expect ( container . innerHTML ) . toBe (
320
- '<!--$-->Hello<div>Component</div><div>Component</div><div>Component</div><div>Component</div><!--/$-->' ,
321
- ) ;
303
+ ReactDOMClient . hydrateRoot ( container , < App /> , {
304
+ onRecoverableError ( error ) {
305
+ Scheduler . log ( normalizeError ( error . message ) ) ;
306
+ } ,
307
+ } ) ;
308
+ await waitForAll ( [ 'Suspend' ] ) ;
309
+ jest . runAllTimers ( ) ;
322
310
323
- suspend = false ;
324
- resolve ( ) ;
325
- await promise ;
326
- await waitForAll ( [
327
- // first pass, mismatches at end
328
- 'Hello' ,
329
- 'Component' ,
330
- 'Component' ,
331
- 'Component' ,
332
- 'Component' ,
333
-
334
- // second pass as client render
335
- 'Hello' ,
336
- 'Component' ,
337
- 'Component' ,
338
- 'Component' ,
339
- 'Component' ,
340
-
341
- // Hydration mismatch is logged
342
- "Hydration failed because the server rendered HTML didn't match the client." ,
343
- 'There was an error while hydrating this Suspense boundary.' ,
344
- ] ) ;
311
+ // Unchanged
312
+ expect ( container . innerHTML ) . toBe (
313
+ '<!--$-->Hello<div>Component</div><div>Component</div><div>Component</div><div>Component</div><!--/$-->' ,
314
+ ) ;
345
315
346
- // Client rendered - suspense comment nodes removed
347
- expect ( container . innerHTML ) . toBe (
348
- 'Hello<div>Component</div><div>Component</div><div>Component</div><article>Mismatch</article>' ,
349
- ) ;
316
+ suspend = false ;
317
+ resolve ( ) ;
318
+ await promise ;
319
+ await waitForAll ( [
320
+ // first pass, mismatches at end
321
+ 'Hello' ,
322
+ 'Component' ,
323
+ 'Component' ,
324
+ 'Component' ,
325
+ 'Component' ,
326
+
327
+ // second pass as client render
328
+ 'Hello' ,
329
+ 'Component' ,
330
+ 'Component' ,
331
+ 'Component' ,
332
+ 'Component' ,
333
+
334
+ // Hydration mismatch is logged
335
+ "Hydration failed because the server rendered HTML didn't match the client." ,
336
+ 'There was an error while hydrating this Suspense boundary.' ,
337
+ ] ) ;
350
338
351
- if ( __DEV__ ) {
352
- const secondToLastCall =
353
- mockError . mock . calls [ mockError . mock . calls . length - 2 ] ;
354
- expect ( secondToLastCall ) . toEqual ( [
355
- 'Warning: Expected server HTML to contain a matching <%s> in <%s>.%s' ,
356
- 'article' ,
357
- 'Suspense' ,
358
- '\n' +
359
- ' in article (at **)\n' +
360
- ' in Component (at **)\n' +
361
- ' in Suspense (at **)\n' +
362
- ' in App (at **)' ,
363
- ] ) ;
364
- }
365
- } finally {
366
- console . error = originalConsoleError ;
367
- }
339
+ // Client rendered - suspense comment nodes removed
340
+ expect ( container . innerHTML ) . toBe (
341
+ 'Hello<div>Component</div><div>Component</div><div>Component</div><article>Mismatch</article>' ,
342
+ ) ;
368
343
} ) ;
369
344
370
345
it ( 'calls the hydration callbacks after hydration or deletion' , async ( ) => {
@@ -522,38 +497,27 @@ describe('ReactDOMServerPartialHydration', () => {
522
497
expect ( container . innerHTML ) . toContain ( '<span>B</span>' ) ;
523
498
expect ( ref . current ) . toBe ( null ) ;
524
499
525
- await expect ( async ( ) => {
526
- await act ( ( ) => {
527
- ReactDOMClient . hydrateRoot ( container , < App hasB = { false } /> , {
528
- onRecoverableError ( error ) {
529
- Scheduler . log ( normalizeError ( error . message ) ) ;
530
- } ,
531
- } ) ;
500
+ await act ( ( ) => {
501
+ ReactDOMClient . hydrateRoot ( container , < App hasB = { false } /> , {
502
+ onRecoverableError ( error ) {
503
+ Scheduler . log ( normalizeError ( error . message ) ) ;
504
+ } ,
532
505
} ) ;
533
- } ) . toErrorDev (
534
- 'Did not expect server HTML to contain a <span> in <Suspense>' ,
535
- ) ;
506
+ } ) ;
536
507
537
508
expect ( container . innerHTML ) . toContain ( '<span>A</span>' ) ;
538
509
expect ( container . innerHTML ) . not . toContain ( '<span>B</span>' ) ;
539
510
540
511
assertLog ( [
541
512
'Server rendered' ,
542
513
'Client rendered' ,
543
- ' Hydration failed because the initial UI does not match what was rendered on the server.' ,
514
+ " Hydration failed because the server rendered HTML didn't match the client." ,
544
515
'There was an error while hydrating this Suspense boundary.' ,
545
516
] ) ;
546
517
expect ( ref . current ) . not . toBe ( span ) ;
547
518
} ) ;
548
519
549
520
it ( 'recovers with client render when server rendered additional nodes at suspense root after unsuspending' , async ( ) => {
550
- // We can't use the toErrorDev helper here because this is async.
551
- const originalConsoleError = console . error ;
552
- const mockError = jest . fn ( ) ;
553
- console . error = ( ...args ) => {
554
- mockError ( ...args . map ( normalizeCodeLocInfo ) ) ;
555
- } ;
556
-
557
521
const ref = React . createRef ( ) ;
558
522
let shouldSuspend = false ;
559
523
let resolve ;
@@ -581,44 +545,34 @@ describe('ReactDOMServerPartialHydration', () => {
581
545
</ div >
582
546
) ;
583
547
}
584
- try {
585
- const finalHTML = ReactDOMServer . renderToString ( < App hasB = { true } /> ) ;
548
+ const finalHTML = ReactDOMServer . renderToString ( < App hasB = { true } /> ) ;
586
549
587
- const container = document . createElement ( 'div' ) ;
588
- container . innerHTML = finalHTML ;
550
+ const container = document . createElement ( 'div' ) ;
551
+ container . innerHTML = finalHTML ;
589
552
590
- const span = container . getElementsByTagName ( 'span' ) [ 0 ] ;
553
+ const span = container . getElementsByTagName ( 'span' ) [ 0 ] ;
591
554
592
- expect ( container . innerHTML ) . toContain ( '<span>A</span>' ) ;
593
- expect ( container . innerHTML ) . toContain ( '<span>B</span>' ) ;
594
- expect ( ref . current ) . toBe ( null ) ;
555
+ expect ( container . innerHTML ) . toContain ( '<span>A</span>' ) ;
556
+ expect ( container . innerHTML ) . toContain ( '<span>B</span>' ) ;
557
+ expect ( ref . current ) . toBe ( null ) ;
595
558
596
- shouldSuspend = true ;
597
- await act ( ( ) => {
598
- ReactDOMClient . hydrateRoot ( container , < App hasB = { false } /> ) ;
599
- } ) ;
559
+ shouldSuspend = true ;
560
+ await act ( ( ) => {
561
+ ReactDOMClient . hydrateRoot ( container , < App hasB = { false } /> ) ;
562
+ } ) ;
600
563
564
+ await expect ( async ( ) => {
601
565
await act ( ( ) => {
602
566
resolve ( ) ;
603
567
} ) ;
568
+ } ) . toErrorDev ( [
569
+ "Hydration failed because the server rendered HTML didn't match the client." ,
570
+ 'There was an error while hydrating this Suspense boundary. Switched to client rendering.' ,
571
+ ] ) ;
604
572
605
- expect ( container . innerHTML ) . toContain ( '<span>A</span>' ) ;
606
- expect ( container . innerHTML ) . not . toContain ( '<span>B</span>' ) ;
607
- expect ( ref . current ) . not . toBe ( span ) ;
608
- if ( __DEV__ ) {
609
- expect ( mockError ) . toHaveBeenCalledWith (
610
- 'Warning: Did not expect server HTML to contain a <%s> in <%s>.%s' ,
611
- 'span' ,
612
- 'Suspense' ,
613
- '\n' +
614
- ' in Suspense (at **)\n' +
615
- ' in div (at **)\n' +
616
- ' in App (at **)' ,
617
- ) ;
618
- }
619
- } finally {
620
- console . error = originalConsoleError ;
621
- }
573
+ expect ( container . innerHTML ) . toContain ( '<span>A</span>' ) ;
574
+ expect ( container . innerHTML ) . not . toContain ( '<span>B</span>' ) ;
575
+ expect ( ref . current ) . not . toBe ( span ) ;
622
576
} ) ;
623
577
624
578
it ( 'recovers with client render when server rendered additional nodes deep inside suspense root' , async ( ) => {
0 commit comments