@ -225,6 +225,85 @@ test('select merges scoped css hash with static class', () => {
) ;
} ) ;
describe ( 'boundary hydration comment escaping' , ( ) = > {
const failed_snippet = ( renderer : Renderer , error : unknown ) = > {
renderer . push ( ` <p> ${ ( error as { message : string } ).message}</p> ` ) ;
} ;
const transform = ( error : unknown ) = > ( { message : ( error as Error ) . message } ) ;
const payloads = [
{ name : 'escapes -->' , input : '--><img src=x onerror=alert(1)><!--' , expected : '{"message":"--\\u003e\\u003cimg src=x onerror=alert(1)\\u003e\\u003c!--"}' } ,
{ name : 'escapes <!--' , input : '<!--<script>alert(1)</script>' , expected : '{"message":"\\u003c!--\\u003cscript\\u003ealert(1)\\u003c/script\\u003e"}' } ,
{ name : 'escapes <!-->' , input : '<!-->' , expected : '{"message":"\\u003c!--\\u003e"}' } ,
{ name : 'escapes <!--->' , input : '<!--->' , expected : '{"message":"\\u003c!---\\u003e"}' } ,
{ name : 'escapes multiple -->' , input : '-->one-->two-->' , expected : '{"message":"--\\u003eone--\\u003etwo--\\u003e"}' } ,
{ name : 'escapes --->' , input : '--->' , expected : '{"message":"---\\u003e"}' } ,
{ name : 'no double-encoding' , input : '--\\u003e' , expected : '{"message":"--\\\\u003e"}' } ,
{ name : 'the terrifying special pointy boy' , input : '--!>ooh, what an exotic closing comment tag' , expected : '{"message":"--!\\u003eooh, what an exotic closing comment tag"}' }
] ;
type RenderFn = ( input : string ) = > Promise < string > | string ;
const paths : Array < { path : string ; async : boolean ; render : RenderFn } > = [
{
path : 'sync children, sync transformError' ,
async : false ,
render : ( input ) = > {
const component = ( renderer : Renderer ) = > {
renderer . boundary ( { failed : failed_snippet } , ( ) = > { throw new Error ( input ) ; } ) ;
} ;
return Renderer . render ( component as unknown as Component , { transformError : transform } as any ) . body ;
}
} ,
{
path : 'sync children, async transformError' ,
async : true ,
render : async ( input ) = > {
const component = ( renderer : Renderer ) = > {
renderer . boundary ( { failed : failed_snippet } , ( ) = > { throw new Error ( input ) ; } ) ;
} ;
return ( await Renderer . render ( component as unknown as Component , {
transformError : ( error : unknown ) = > Promise . resolve ( transform ( error ) )
} as any ) ) . body ;
}
} ,
{
path : 'async children throw' ,
async : true ,
render : async ( input ) = > {
const component = ( renderer : Renderer ) = > {
renderer . boundary ( { failed : failed_snippet } , async ( ) = > {
await Promise . resolve ( ) ;
throw new Error ( input ) ;
} ) ;
} ;
return ( await Renderer . render ( component as unknown as Component , {
transformError : transform
} as any ) ) . body ;
}
}
] ;
describe . each ( paths ) ( '$path' , ( { async : needs_async , render } ) = > {
if ( needs_async ) {
beforeAll ( ( ) = > enable_async_mode_flag ( ) ) ;
afterAll ( ( ) = > disable_async_mode_flag ( ) ) ;
}
test . each ( payloads ) ( '$name' , async ( { input , expected } ) = > {
const body = await render ( input ) ;
// Extract the content between <!--[? and the first -->
// If escaping is broken, an unescaped --> in the JSON will truncate
// the match and the content won't equal the expected escaped JSON.
const match = body . match ( /<!--\[\?(.+?)-->/ ) ;
expect ( match , 'expected a hydration comment in output' ) . toBeTruthy ( ) ;
expect ( match ! [ 1 ] ) . toBe ( expected ) ;
} ) ;
} ) ;
} ) ;
describe ( 'async' , ( ) = > {
beforeAll ( ( ) = > {
enable_async_mode_flag ( ) ;