@@ -128,6 +128,13 @@ try {
128128 await signIn . goto ( `${ origin } /sign-in` )
129129 await signIn . close ( )
130130 const panel : Page = await context . newPage ( )
131+ // Observe dispatch before the bridge so background metadata queries stay outside each action.
132+ await panel . addInitScript ( ( ) => {
133+ window . addEventListener ( 'message' , ( ) => {
134+ document . documentElement . dataset . queryCountAtDispatch =
135+ document . documentElement . dataset . browserQueryCount ?? '0'
136+ } )
137+ } )
131138 await panel . goto ( `chrome-extension://${ extensionId } /sidepanel.html` )
132139 await panel . getByTitle ( 'Sim chat' ) . waitFor ( )
133140 const frame : Frame = panel . frames ( ) [ 1 ] ?? ( await panel . waitForEvent ( 'frameattached' ) )
@@ -588,6 +595,7 @@ try {
588595 callback ?: ( tabs : chrome . tabs . Tab [ ] ) => void
589596 ) => {
590597 queries ++
598+ document . documentElement . dataset . browserQueryCount = String ( queries )
591599 return callback ? originalQuery ( query , callback ) : originalQuery ( query )
592600 } ) as typeof chrome . tabs . query
593601 window . addEventListener ( 'message' , ( event : MessageEvent < unknown > ) => {
@@ -599,24 +607,31 @@ try {
599607 event . data . type !== 'action'
600608 )
601609 return
602- document . body . dataset . actionQueries = String ( queries )
610+ document . body . dataset . actionQueries = String (
611+ queries - Number ( document . documentElement . dataset . queryCountAtDispatch ?? '0' )
612+ )
603613 document . body . dataset . observedActions = String ( ++ actions )
604614 } )
605615 } )
616+ await panel . evaluate ( ( ) => chrome . tabs . query ( { active : true , currentWindow : true } ) )
606617 const parentCdp = await panel . context ( ) . newCDPSession ( panel )
607618 const cdp = await panel . context ( ) . newCDPSession ( app )
608619 await expect
609620 . poll (
610621 async ( ) => {
611- const result = await parentCdp . send ( 'Runtime.evaluate' , {
612- expression : 'navigator.userActivation.isActive' ,
613- userGesture : false ,
614- } )
615- return result . result . value
622+ const results = await Promise . all (
623+ [ parentCdp , cdp ] . map ( ( session ) =>
624+ session . send ( 'Runtime.evaluate' , {
625+ expression : 'navigator.userActivation.isActive' ,
626+ userGesture : false ,
627+ } )
628+ )
629+ )
630+ return results . map ( ( result ) => result . result . value )
616631 } ,
617632 { timeout : 10_000 }
618633 )
619- . toBe ( false )
634+ . toEqual ( [ false , false ] )
620635 const action = {
621636 channel : 'sim-extension' ,
622637 version : 1 ,
@@ -653,6 +668,55 @@ try {
653668 await parentCdp . detach ( )
654669 }
655670 )
671+ await check (
672+ 'Site access stays modal without violating the packaged security policy' ,
673+ async ( ) => {
674+ const policyErrors : string [ ] = [ ]
675+ panel . on ( 'console' , ( message ) => {
676+ if ( message . type ( ) === 'error' && message . text ( ) . includes ( 'Content Security Policy' ) )
677+ policyErrors . push ( message . text ( ) )
678+ } )
679+ const openAccess = ( ) =>
680+ app . evaluate (
681+ ( { targetOrigin, sessionId } ) => {
682+ parent . postMessage (
683+ {
684+ channel : 'sim-extension' ,
685+ version : 1 ,
686+ type : 'action' ,
687+ sessionId,
688+ action : 'site-access' ,
689+ } ,
690+ targetOrigin
691+ )
692+ } ,
693+ { targetOrigin : `chrome-extension://${ extensionId } ` , sessionId : receiverSessionId }
694+ )
695+ await panel . bringToFront ( )
696+ await openAccess ( )
697+ const dialog = panel . getByRole ( 'dialog' , { name : 'Site access' } )
698+ await expect ( dialog ) . toBeVisible ( )
699+ await settleMessages ( )
700+ assert ( policyErrors . length === 0 , `Site access violated CSP: ${ policyErrors . join ( '\n' ) } ` )
701+ await panel . evaluate ( ( ) => document . querySelector ( 'iframe' ) ?. focus ( ) )
702+ assert (
703+ await dialog . evaluate ( ( element ) => element . contains ( document . activeElement ) ) ,
704+ 'The hosted frame accepted focus behind the modal'
705+ )
706+ await dialog . getByRole ( 'button' , { name : 'Close' } ) . press ( 'Escape' )
707+ await expect ( dialog , 'Escape closes the modal' ) . toBeHidden ( )
708+ await openAccess ( )
709+ await expect ( dialog ) . toBeVisible ( )
710+ await dialog . getByRole ( 'button' , { name : 'Close' } ) . click ( )
711+ await expect ( dialog , 'The Close button dismisses the modal' ) . toBeHidden ( )
712+ await openAccess ( )
713+ await expect ( dialog ) . toBeVisible ( )
714+ await dialog . click ( { trial : true } )
715+ await panel . mouse . click ( 1 , 1 )
716+ await expect ( dialog , 'The backdrop dismisses the modal' ) . toBeHidden ( )
717+ assert ( policyErrors . length === 0 , `Site access violated CSP: ${ policyErrors . join ( '\n' ) } ` )
718+ }
719+ )
656720 await mkdir ( dirname ( output ) , { recursive : true } )
657721 await panel . screenshot ( { path : output . replace ( / \. j s o n $ / , '.png' ) , fullPage : true } )
658722} catch ( error ) {
0 commit comments