@@ -3795,7 +3795,7 @@ describe('ReactDOMFizzServer', () => {
37953795 } ) ;
37963796
37973797 expect ( container . firstElementChild . outerHTML ) . toEqual (
3798- '<div>hello<b>world<!-- -->< /b></div>' ,
3798+ '<div>hello<b>world</b></div>' ,
37993799 ) ;
38003800
38013801 const errors = [ ] ;
@@ -3938,7 +3938,7 @@ describe('ReactDOMFizzServer', () => {
39383938 } ) ;
39393939
39403940 // @gate experimental
3941- it ( '(only) includes extraneous text separators in segments that complete before flushing, followed by nothing or a non-Text node' , async ( ) => {
3941+ it ( 'excludes extraneous text separators in segments that complete before flushing, followed by nothing or a non-Text node' , async ( ) => {
39423942 function App ( ) {
39433943 return (
39443944 < div >
@@ -3970,7 +3970,7 @@ describe('ReactDOMFizzServer', () => {
39703970 } ) ;
39713971
39723972 expect ( container . innerHTML ) . toEqual (
3973- '<div><!--$-->hello<!-- -->world<!-- --><!-- /$--><!--$-->world<!-- --><!-- /$--><!--$-->hello<!-- -->world<!-- -->< br><!--/$--><!--$-->world<!-- --> <br><!--/$--></div>' ,
3973+ '<div><!--$-->hello<!-- -->world<!--/$--><!--$-->world<!--/$--><!--$-->hello<!-- -->world<br><!--/$--><!--$-->world<br><!--/$--></div>' ,
39743974 ) ;
39753975
39763976 const errors = [ ] ;
@@ -3998,5 +3998,360 @@ describe('ReactDOMFizzServer', () => {
39983998 </ div > ,
39993999 ) ;
40004000 } ) ;
4001+
4002+ // @gate experimental
4003+ it ( 'handles many serial adjacent segments that resolves in arbitrary order' , async ( ) => {
4004+ function NineText ( ) {
4005+ return (
4006+ < >
4007+ < ThreeText start = { 1 } />
4008+ < ThreeText start = { 4 } />
4009+ < ThreeText start = { 7 } />
4010+ </ >
4011+ ) ;
4012+ }
4013+
4014+ function ThreeText ( { start} ) {
4015+ return (
4016+ < >
4017+ < AsyncText text = { start } />
4018+ < AsyncText text = { start + 1 } />
4019+ < AsyncText text = { start + 2 } />
4020+ </ >
4021+ ) ;
4022+ }
4023+
4024+ function App ( ) {
4025+ return (
4026+ < div >
4027+ < Suspense >
4028+ < NineText />
4029+ </ Suspense >
4030+ </ div >
4031+ ) ;
4032+ }
4033+
4034+ await act ( async ( ) => {
4035+ const { pipe} = ReactDOMFizzServer . renderToPipeableStream ( < App /> ) ;
4036+ await act ( ( ) => resolveText ( 1 ) ) ;
4037+ await act ( ( ) => resolveText ( 6 ) ) ;
4038+ await act ( ( ) => resolveText ( 9 ) ) ;
4039+ await afterImmediate ( ) ;
4040+ await act ( ( ) => resolveText ( 2 ) ) ;
4041+ await act ( ( ) => resolveText ( 5 ) ) ;
4042+ await act ( ( ) => resolveText ( 7 ) ) ;
4043+ pipe ( writable ) ;
4044+ } ) ;
4045+
4046+ expect ( container . innerHTML ) . toEqual (
4047+ '<div><!--$?--><template id="B:0"></template><!--/$--></div><div hidden="" id="S:0">1<!-- -->2<template id="P:1"></template><template id="P:2"></template>5<!-- -->6<!-- -->7<template id="P:3"></template>9</div>' ,
4048+ ) ;
4049+
4050+ await act ( async ( ) => {
4051+ resolveText ( 3 ) ;
4052+ resolveText ( 4 ) ;
4053+ resolveText ( 8 ) ;
4054+ } ) ;
4055+
4056+ expect ( container . firstElementChild . outerHTML ) . toEqual (
4057+ '<div><!--$-->1<!-- -->2345<!-- -->6<!-- -->789<!--/$--></div>' ,
4058+ ) ;
4059+
4060+ const errors = [ ] ;
4061+ ReactDOMClient . hydrateRoot ( container , < App /> , {
4062+ onRecoverableError ( error ) {
4063+ errors . push ( error . message ) ;
4064+ } ,
4065+ } ) ;
4066+ expect ( Scheduler ) . toFlushAndYield ( [ ] ) ;
4067+ expect ( errors ) . toEqual ( [ ] ) ;
4068+ expect ( getVisibleChildren ( container ) ) . toEqual (
4069+ < div >
4070+ { '1' }
4071+ { '2' }
4072+ { '3' }
4073+ { '4' }
4074+ { '5' }
4075+ { '6' }
4076+ { '7' }
4077+ { '8' }
4078+ { '9' }
4079+ </ div > ,
4080+ ) ;
4081+ } ) ;
4082+
4083+ // @gate experimental
4084+ it ( 'handles deeply nested segments that resolves in arbitrary order' , async ( ) => {
4085+ function RecursiveNumber ( { from, steps, reverse} ) {
4086+ if ( steps === 1 ) {
4087+ return readText ( from ) ;
4088+ }
4089+
4090+ const num = readText ( from ) ;
4091+
4092+ return (
4093+ < >
4094+ { num }
4095+ < RecursiveNumber
4096+ from = { reverse ? from - 1 : from + 1 }
4097+ steps = { steps - 1 }
4098+ reverse = { reverse }
4099+ />
4100+ </ >
4101+ ) ;
4102+ }
4103+
4104+ function App ( ) {
4105+ return (
4106+ < div >
4107+ < Suspense >
4108+ < RecursiveNumber from = { 1 } steps = { 3 } />
4109+ < RecursiveNumber from = { 6 } steps = { 3 } reverse = { true } />
4110+ </ Suspense >
4111+ </ div >
4112+ ) ;
4113+ }
4114+
4115+ await act ( async ( ) => {
4116+ const { pipe} = ReactDOMFizzServer . renderToPipeableStream ( < App /> ) ;
4117+ await afterImmediate ( ) ;
4118+ await act ( ( ) => resolveText ( 1 ) ) ;
4119+ await act ( ( ) => resolveText ( 2 ) ) ;
4120+ await act ( ( ) => resolveText ( 4 ) ) ;
4121+
4122+ pipe ( writable ) ;
4123+ } ) ;
4124+
4125+ expect ( container . innerHTML ) . toEqual (
4126+ '<div><!--$?--><template id="B:0"></template><!--/$--></div><div hidden="" id="S:0">1<!-- -->2<template id="P:1"></template><template id="P:2"></template></div>' ,
4127+ ) ;
4128+
4129+ await act ( async ( ) => {
4130+ resolveText ( 3 ) ;
4131+ resolveText ( 5 ) ;
4132+ resolveText ( 6 ) ;
4133+ } ) ;
4134+
4135+ expect ( container . firstElementChild . outerHTML ) . toEqual (
4136+ '<div><!--$-->1<!-- -->236<!-- -->5<!-- -->4<!--/$--></div>' ,
4137+ ) ;
4138+
4139+ const errors = [ ] ;
4140+ ReactDOMClient . hydrateRoot ( container , < App /> , {
4141+ onRecoverableError ( error ) {
4142+ errors . push ( error . message ) ;
4143+ } ,
4144+ } ) ;
4145+ expect ( Scheduler ) . toFlushAndYield ( [ ] ) ;
4146+ expect ( errors ) . toEqual ( [ ] ) ;
4147+ expect ( getVisibleChildren ( container ) ) . toEqual (
4148+ < div >
4149+ { '1' }
4150+ { '2' }
4151+ { '3' }
4152+ { '6' }
4153+ { '5' }
4154+ { '4' }
4155+ </ div > ,
4156+ ) ;
4157+ } ) ;
4158+
4159+ // @gate experimental
4160+ it ( 'handles segments that return null' , async ( ) => {
4161+ function WrappedAsyncText ( { outer, text} ) {
4162+ readText ( outer ) ;
4163+ return < AsyncText text = { text } /> ;
4164+ }
4165+
4166+ function App ( ) {
4167+ return (
4168+ < div >
4169+ < Suspense >
4170+ < div >
4171+ < AsyncText text = { null } />
4172+ < AsyncText text = { 'hello' } />
4173+ < AsyncText text = { 'world' } />
4174+ </ div >
4175+ < div >
4176+ < AsyncText text = { 'hello' } />
4177+ < AsyncText text = { null } />
4178+ < AsyncText text = { 'world' } />
4179+ </ div >
4180+ < div >
4181+ < AsyncText text = { 'hello' } />
4182+ < AsyncText text = { 'world' } />
4183+ < AsyncText text = { null } />
4184+ </ div >
4185+ < div >
4186+ < AsyncText text = { 'hello' } />
4187+ < AsyncText text = { null } />
4188+ < AsyncText text = { null } />
4189+ < AsyncText text = { 'world' } />
4190+ </ div >
4191+ < div >
4192+ < AsyncText text = { 'hello' } />
4193+ < WrappedAsyncText outer = { 'outer1' } text = { null } />
4194+ < AsyncText text = { null } />
4195+ < AsyncText text = { 'world' } />
4196+ </ div >
4197+ < div >
4198+ < WrappedAsyncText outer = { 'outer1' } text = { 'hello' } />
4199+ < WrappedAsyncText outer = { 'outer2' } text = { null } />
4200+ < AsyncText text = { 'world' } />
4201+ </ div >
4202+ </ Suspense >
4203+ </ div >
4204+ ) ;
4205+ }
4206+
4207+ await act ( async ( ) => {
4208+ const { pipe} = ReactDOMFizzServer . renderToPipeableStream ( < App /> ) ;
4209+ await afterImmediate ( ) ;
4210+ await act ( ( ) => resolveText ( 'outer2' ) ) ;
4211+ await act ( ( ) => resolveText ( 'world' ) ) ;
4212+ await act ( ( ) => resolveText ( 'outer1' ) ) ;
4213+ await act ( ( ) => resolveText ( null ) ) ;
4214+ await act ( ( ) => resolveText ( 'hello' ) ) ;
4215+
4216+ pipe ( writable ) ;
4217+ } ) ;
4218+
4219+ let helloWorld = '<div>hello<!-- -->world</div>' ;
4220+ let testcases = 6 ;
4221+
4222+ expect ( container . firstElementChild . outerHTML ) . toEqual (
4223+ '<div><!--$-->' +
4224+ new Array ( testcases ) . fill ( helloWorld ) . join ( '' ) +
4225+ '<!--/$--></div>' ,
4226+ ) ;
4227+
4228+ const errors = [ ] ;
4229+ ReactDOMClient . hydrateRoot ( container , < App /> , {
4230+ onRecoverableError ( error ) {
4231+ errors . push ( error . message ) ;
4232+ } ,
4233+ } ) ;
4234+ expect ( Scheduler ) . toFlushAndYield ( [ ] ) ;
4235+ expect ( errors ) . toEqual ( [ ] ) ;
4236+ const assertion = ( ) => {
4237+ expect ( getVisibleChildren ( container ) ) . toEqual (
4238+ < div >
4239+ { new Array ( testcases ) . fill (
4240+ < div >
4241+ { 'hello' }
4242+ { 'world' }
4243+ </ div > ,
4244+ ) }
4245+ </ div > ,
4246+ ) ;
4247+ } ;
4248+ if ( __DEV__ ) {
4249+ expect ( assertion ) . toErrorDev ( [
4250+ 'Warning: Each child in a list should have a unique "key" prop.' ,
4251+ ] ) ;
4252+ } else {
4253+ assertion ( ) ;
4254+ }
4255+ } ) ;
4256+
4257+ // @gate experimental
4258+ it ( 'does not add separators when otherwise adjacent text is wrapped in Suspense' , async ( ) => {
4259+ function App ( ) {
4260+ return (
4261+ < div >
4262+ hello
4263+ < Suspense >
4264+ < AsyncText text = { 'world' } />
4265+ </ Suspense >
4266+ </ div >
4267+ ) ;
4268+ }
4269+
4270+ await act ( async ( ) => {
4271+ const { pipe} = ReactDOMFizzServer . renderToPipeableStream ( < App /> ) ;
4272+ await afterImmediate ( ) ;
4273+ await act ( ( ) => resolveText ( 'world' ) ) ;
4274+
4275+ pipe ( writable ) ;
4276+ } ) ;
4277+
4278+ expect ( container . firstElementChild . outerHTML ) . toEqual (
4279+ '<div>hello<!--$-->world<!--/$--></div>' ,
4280+ ) ;
4281+
4282+ const errors = [ ] ;
4283+ ReactDOMClient . hydrateRoot ( container , < App /> , {
4284+ onRecoverableError ( error ) {
4285+ errors . push ( error . message ) ;
4286+ } ,
4287+ } ) ;
4288+ expect ( Scheduler ) . toFlushAndYield ( [ ] ) ;
4289+ expect ( errors ) . toEqual ( [ ] ) ;
4290+ expect ( getVisibleChildren ( container ) ) . toEqual (
4291+ < div >
4292+ { 'hello' }
4293+ { 'world' }
4294+ </ div > ,
4295+ ) ;
4296+ } ) ;
4297+
4298+ // @gate experimental
4299+ it ( 'does not prepend separators for Suspense fallback text but will append them if followed by text' , async ( ) => {
4300+ function App ( ) {
4301+ return (
4302+ < div >
4303+ < Suspense fallback = { 'outer' } >
4304+ hello
4305+ < Suspense
4306+ fallback = {
4307+ < >
4308+ < AsyncText text = { 'world' } /> !< AsyncText text = { 'foo' } />
4309+ </ >
4310+ } >
4311+ < AsyncText text = { 'bar' } />
4312+ </ Suspense >
4313+ !
4314+ </ Suspense >
4315+ </ div >
4316+ ) ;
4317+ }
4318+
4319+ await act ( async ( ) => {
4320+ const { pipe} = ReactDOMFizzServer . renderToPipeableStream ( < App /> ) ;
4321+ await afterImmediate ( ) ;
4322+ await act ( ( ) => resolveText ( 'foo' ) ) ;
4323+ pipe ( writable ) ;
4324+ } ) ;
4325+
4326+ expect ( container . innerHTML ) . toEqual (
4327+ '<div><!--$?--><template id="B:0"></template>outer<!--/$--></div><div hidden="" id="S:0">hello<!--$?--><template id="B:1"></template><template id="P:2"></template>!<!-- -->foo<!--/$-->!</div>' ,
4328+ ) ;
4329+
4330+ await act ( ( ) => resolveText ( 'world' ) ) ;
4331+
4332+ expect ( container . children [ 0 ] . outerHTML ) . toEqual (
4333+ '<div><!--$-->hello<!--$?--><template id="B:1"></template>world!<!-- -->foo<!--/$-->!<!--/$--></div>' ,
4334+ ) ;
4335+
4336+ const errors = [ ] ;
4337+ ReactDOMClient . hydrateRoot ( container , < App /> , {
4338+ onRecoverableError ( error ) {
4339+ errors . push ( error . message ) ;
4340+ } ,
4341+ } ) ;
4342+ expect ( Scheduler ) . toFlushAndYield ( [ ] ) ;
4343+ expect ( errors ) . toEqual ( [ ] ) ;
4344+ expect ( getVisibleChildren ( container ) ) . toEqual (
4345+ < div >
4346+ { 'hello' }
4347+ { /* starting the inner Suspense boundary Fallback */ }
4348+ { 'world' }
4349+ { '!' }
4350+ { 'foo' }
4351+ { /* ending the inner Suspense boundary Fallback */ }
4352+ { '!' }
4353+ </ div > ,
4354+ ) ;
4355+ } ) ;
40014356 } ) ;
40024357} ) ;
0 commit comments