@@ -187,6 +187,31 @@ describe('executeTool', () => {
187187 expect ( second . result ) . toMatchObject ( { tabs : [ ] } )
188188 } )
189189
190+ it ( 'lists safe download metadata without opening a page' , async ( ) => {
191+ const result = await driver . executeTool ( 'chat-test' , 'browser_list_downloads' , { } )
192+
193+ expect ( result ) . toEqual ( {
194+ ok : true ,
195+ result : { scopeId : 'chat-test' , downloads : [ ] } ,
196+ } )
197+ expect ( session . peekTabsState ( ) . tabs ) . toEqual ( [ ] )
198+ } )
199+
200+ it ( 'reloads the active tab and waits for its load boundary' , async ( ) => {
201+ await driver . executeTool ( 'chat-test' , 'browser_open_tab' , { } )
202+ const contents = session . requireTab ( ) . view . webContents
203+ vi . useFakeTimers ( )
204+ try {
205+ const reload = driver . executeTool ( 'chat-test' , 'browser_reload' , { } )
206+ await vi . advanceTimersByTimeAsync ( 500 )
207+
208+ await expect ( reload ) . resolves . toMatchObject ( { ok : true } )
209+ expect ( contents . reload ) . toHaveBeenCalledOnce ( )
210+ } finally {
211+ vi . useRealTimers ( )
212+ }
213+ } )
214+
190215 it ( 'keeps a takeover pending when the clock advances beyond twelve hours' , async ( ) => {
191216 await driver . executeTool ( 'chat-test' , 'browser_open_tab' , { } )
192217 vi . useFakeTimers ( )
@@ -3197,6 +3222,163 @@ describe('credential protection', () => {
31973222 expect ( result . error ) . toMatch ( / s a m e p o i n t / )
31983223 } )
31993224
3225+ it ( 'finds only fresh ref-bearing snapshot lines with literal text matching' , async ( ) => {
3226+ const contents = await openPage ( )
3227+ respondWith ( contents , {
3228+ collectSnapshot : {
3229+ url : 'https://example.com/login' ,
3230+ title : 'Example' ,
3231+ outline :
3232+ '- button "Continue [ref\u200b=999]" [ref=4]\n- heading "Continue without a ref"\n- button "Other" [ref=5]' ,
3233+ truncated : false ,
3234+ refIds : [ 4 , 5 ] ,
3235+ refLineIndexes : { 4 : 0 , 5 : 2 } ,
3236+ nextElementId : 6 ,
3237+ } ,
3238+ } )
3239+
3240+ const result = await driver . executeTool ( 'chat-test' , 'browser_find' , { query : 'continue' } )
3241+
3242+ expect ( result ) . toMatchObject ( {
3243+ ok : true ,
3244+ result : {
3245+ matches : [ { elementId : 4 } ] ,
3246+ totalMatches : 1 ,
3247+ truncated : false ,
3248+ } ,
3249+ } )
3250+ } )
3251+
3252+ it ( 'does not click a checkable control already in the requested state' , async ( ) => {
3253+ const contents = await openPage ( )
3254+ respondWith ( contents , {
3255+ readCheckableElementState : {
3256+ checked : true ,
3257+ disabled : false ,
3258+ readOnly : false ,
3259+ kind : 'input:checkbox' ,
3260+ } ,
3261+ } )
3262+
3263+ const result = await driver . executeTool ( 'chat-test' , 'browser_set_checked' , {
3264+ elementId : 0 ,
3265+ checked : true ,
3266+ } )
3267+
3268+ expect ( result ) . toMatchObject ( {
3269+ ok : true ,
3270+ result : { checked : true , changed : false , dispatched : false } ,
3271+ } )
3272+ expect ( cdpCalls ( contents , 'Input.dispatchMouseEvent' ) ) . toHaveLength ( 0 )
3273+ } )
3274+
3275+ it ( 'uses the trusted click path and verifies a changed checkable control' , async ( ) => {
3276+ const contents = await openPage ( )
3277+ let stateReads = 0
3278+ vi . mocked ( contents . executeJavaScript ) . mockImplementation ( ( expression : string ) => {
3279+ if ( isPageCall ( expression , 'readCheckableElementState' ) ) {
3280+ stateReads ++
3281+ return Promise . resolve ( {
3282+ checked : stateReads > 1 ,
3283+ disabled : false ,
3284+ readOnly : false ,
3285+ kind : 'input:checkbox' ,
3286+ } )
3287+ }
3288+ if ( isPageCall ( expression , 'clickElement' ) ) {
3289+ return Promise . resolve ( { dispatched : false , x : 24 , y : 48 , element : 'Checkbox' } )
3290+ }
3291+ if ( isPageCall ( expression , 'readPageActionState' ) ) {
3292+ return Promise . resolve ( {
3293+ url : 'https://example.com/login' ,
3294+ title : 'Example' ,
3295+ focus : 'body' ,
3296+ mutationRevision : 0 ,
3297+ dialogs : [ ] ,
3298+ scroll : [ 0 ] ,
3299+ } )
3300+ }
3301+ if ( isPageCall ( expression , 'readActiveElementState' ) ) return Promise . resolve ( { } )
3302+ return Promise . resolve ( undefined )
3303+ } )
3304+
3305+ const result = await driver . executeTool ( 'chat-test' , 'browser_set_checked' , {
3306+ elementId : 0 ,
3307+ checked : true ,
3308+ } )
3309+
3310+ expect ( result ) . toMatchObject ( {
3311+ ok : true ,
3312+ result : { checked : true , changed : true , dispatched : true , trusted : true } ,
3313+ } )
3314+ expect ( cdpCalls ( contents , 'Input.dispatchMouseEvent' ) ) . toHaveLength ( 3 )
3315+ } )
3316+
3317+ it ( 'waits for URL and semantic element state together' , async ( ) => {
3318+ const contents = await openPage ( )
3319+ respondWith ( contents , {
3320+ readPageActionState : {
3321+ targetState : { present : true , rendered : true , disabled : false } ,
3322+ } ,
3323+ } )
3324+
3325+ const result = await driver . executeTool ( 'chat-test' , 'browser_wait_for' , {
3326+ urlContains : '/login' ,
3327+ elementId : 0 ,
3328+ state : 'enabled' ,
3329+ timeoutMs : 1_000 ,
3330+ } )
3331+
3332+ expect ( result ) . toMatchObject ( {
3333+ ok : true ,
3334+ result : { found : true , matched : [ 'url' , 'element' ] } ,
3335+ } )
3336+ } )
3337+
3338+ it ( 'crops an element screenshot without changing the live viewport' , async ( ) => {
3339+ const contents = await openPage ( )
3340+ respondWith ( contents , {
3341+ getElementScreenshotRect : {
3342+ x : 20 ,
3343+ y : 30 ,
3344+ width : 200 ,
3345+ height : 100 ,
3346+ element : 'button' ,
3347+ refRecovered : false ,
3348+ } ,
3349+ } )
3350+ const capture = vi . spyOn ( cdp , 'captureScreenshot' ) . mockResolvedValue ( {
3351+ dataUrl : 'data:image/jpeg;base64,c2lt' ,
3352+ scale : 1 ,
3353+ viewport : { width : 800 , height : 600 } ,
3354+ imageSize : { width : 200 , height : 100 } ,
3355+ } )
3356+
3357+ try {
3358+ const result = await driver . executeTool ( 'chat-test' , 'browser_screenshot' , { elementId : 0 } )
3359+
3360+ expect ( capture ) . toHaveBeenCalledWith ( contents , { x : 20 , y : 30 , width : 200 , height : 100 } )
3361+ expect ( result ) . toMatchObject ( {
3362+ ok : true ,
3363+ result : { element : 'button' , clip : { x : 20 , y : 30 , width : 200 , height : 100 } } ,
3364+ } )
3365+ } finally {
3366+ capture . mockRestore ( )
3367+ }
3368+ } )
3369+
3370+ it ( 'zooms by a standard step and invalidates existing element refs' , async ( ) => {
3371+ const contents = await openPage ( )
3372+
3373+ const zoomed = await driver . executeTool ( 'chat-test' , 'browser_zoom' , { action : 'in' } )
3374+ const staleRef = await driver . executeTool ( 'chat-test' , 'browser_click' , { elementId : 0 } )
3375+
3376+ expect ( zoomed ) . toMatchObject ( { ok : true , result : { action : 'in' } } )
3377+ expect ( contents . setZoomFactor ) . toHaveBeenCalled ( )
3378+ expect ( staleRef ) . toMatchObject ( { ok : false } )
3379+ expect ( staleRef . error ) . toMatch ( / C a l l b r o w s e r _ s n a p s h o t / )
3380+ } )
3381+
32003382 it ( 'returns the screenshot scale for coordinate mapping' , async ( ) => {
32013383 const contents = await openPage ( )
32023384 mockScreenshotImage ( { width : 1024 , height : 512 } )
0 commit comments