@@ -200,6 +200,59 @@ const starIcons: OwnedIcon[] = [
200200] ;
201201
202202test . describe ( "Next.js compat: streamed metadata icons" , ( ) => {
203+ for ( const pathname of [
204+ "/metadata-icons-stream/heart" ,
205+ "/nextjs-compat/metadata-generate/my-post:pk5ufy:0" ,
206+ ] ) {
207+ test ( `keeps streamed icon keys opaque for crawlers at ${ pathname } ` , async ( { request } ) => {
208+ const headers = { "User-Agent" : "Googlebot" } ;
209+ const response = await request . get ( pathname , { headers } ) ;
210+ expect ( response . status ( ) ) . toBe ( 200 ) ;
211+ const html = await response . text ( ) ;
212+ const markers = [ ...html . matchAll ( / < l i n k d a t a - v i n e x t - s t r e a m e d - i c o n = " ( [ ^ " ] + ) " / g) ] . map (
213+ ( [ , marker ] ) => marker ,
214+ ) ;
215+ expect ( markers . length ) . toBeGreaterThan ( 0 ) ;
216+ for ( const marker of markers ) {
217+ expect ( marker ) . toMatch ( / ^ v i [ a - z 0 - 9 ] + : \d + $ / ) ;
218+ }
219+
220+ const flightResponse = await request . get ( `${ pathname } ?_rsc=icons` , {
221+ headers : { ...headers , RSC : "1" } ,
222+ } ) ;
223+ expect ( flightResponse . headers ( ) [ "content-type" ] ) . toContain ( "text/x-component" ) ;
224+ const flight = await flightResponse . text ( ) ;
225+ const keys = [ ...flight . matchAll ( / " m e t a d a t a K e y " : " ( [ ^ " ] + ) " / g) ] . map ( ( [ , key ] ) => key ) ;
226+ expect ( keys . length ) . toBeGreaterThan ( 0 ) ;
227+ for ( const key of keys ) {
228+ expect ( key ) . toMatch ( / ^ v i [ a - z 0 - 9 ] + $ / ) ;
229+ expect ( markers . every ( ( marker ) => marker . startsWith ( `${ key } :` ) ) ) . toBe ( true ) ;
230+ }
231+ } ) ;
232+ }
233+
234+ test ( "keeps distinct icon ownership across paths that collide under a 32-bit hash" , async ( {
235+ page,
236+ consoleErrors,
237+ } ) => {
238+ // Both full pathnames have the same 32-bit FNV state before the metadata suffix.
239+ await page . goto ( "/metadata-icons-stream/ae0qv10f" ) ;
240+ await expect . poll ( ( ) => ownedIcons ( page ) ) . toEqual ( heartIcons ) ;
241+ const firstMarker = await page
242+ . locator ( "head link[data-vinext-streamed-icon]" )
243+ . first ( )
244+ . getAttribute ( "data-vinext-streamed-icon" ) ;
245+
246+ await page . locator ( "#metadata-icons-collision" ) . click ( ) ;
247+ await expect ( page ) . toHaveURL ( / \/ m e t a d a t a - i c o n s - s t r e a m \/ n 8 e g 5 6 3 h $ / ) ;
248+ await expect ( page . locator ( "head link[data-vinext-streamed-icon]" ) . first ( ) ) . not . toHaveAttribute (
249+ "data-vinext-streamed-icon" ,
250+ firstMarker ! ,
251+ ) ;
252+ await expect . poll ( ( ) => ownedIcons ( page ) ) . toEqual ( heartIcons ) ;
253+ expect ( consoleErrors ) . toEqual ( [ ] ) ;
254+ } ) ;
255+
203256 test ( "relocates every metadata icon relation with the request CSP nonce" , async ( {
204257 page,
205258 consoleErrors,
0 commit comments