33 */
44import { act } from 'react'
55import { createRoot , type Root } from 'react-dom/client'
6- import { afterEach , beforeEach , describe , expect , it } from 'vitest'
6+ import { afterEach , beforeEach , describe , expect , it , vi } from 'vitest'
77import type { AgentGroupItem } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group-view'
88import {
99 BrowserAgentIcon ,
1010 getBrowserAgentFaviconUrl ,
1111} from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/browser-agent-icon'
1212import type { ToolCallData } from '@/app/workspace/[workspaceId]/home/types'
13+ import { useBrowserSessionStore } from '@/stores/browser-session/store'
14+
15+ const { browserAvailable, chatIdentity } = vi . hoisted ( ( ) => ( {
16+ browserAvailable : vi . fn ( ( ) => true ) ,
17+ chatIdentity : { chatId : 'chat-1' } ,
18+ } ) )
19+ vi . mock ( '@/lib/browser-agent/transport' , ( ) => ( { isBrowserAgentAvailable : browserAvailable } ) )
20+ vi . mock ( '@/app/workspace/[workspaceId]/home/components/chat-surface-context' , ( ) => ( {
21+ useChatSurface : ( ) => chatIdentity ,
22+ } ) )
23+
24+ function openPage ( url : string , scopeId = 'chat-1' , loading = false ) {
25+ act ( ( ) =>
26+ useBrowserSessionStore . getState ( ) . setTabsState ( {
27+ scopeId,
28+ activeTabId : 'tab-1' ,
29+ automationTabId : 'tab-1' ,
30+ tabs : [ { tabId : 'tab-1' , title : '' , url, loading, active : true } ] ,
31+ } )
32+ )
33+ }
1334
1435function tool ( overrides : Partial < ToolCallData > = { } ) : AgentGroupItem {
1536 return {
@@ -189,6 +210,9 @@ describe('BrowserAgentIcon', () => {
189210
190211 beforeEach ( ( ) => {
191212 ; ( globalThis as { IS_REACT_ACT_ENVIRONMENT ?: boolean } ) . IS_REACT_ACT_ENVIRONMENT = true
213+ browserAvailable . mockReturnValue ( true )
214+ chatIdentity . chatId = 'chat-1'
215+ useBrowserSessionStore . setState ( { sessions : { } , activeScopeId : null } )
192216 container = document . createElement ( 'div' )
193217 root = createRoot ( container )
194218 } )
@@ -203,7 +227,45 @@ describe('BrowserAgentIcon', () => {
203227 )
204228 }
205229
230+ it ( 'does not contact sites from history, other chats, or pending navigation' , ( ) => {
231+ render ( 'https://example.com/document' )
232+ expect ( container . querySelector ( 'img' ) ) . toBeNull ( )
233+ openPage ( 'https://example.com' , 'another-chat' )
234+ expect ( container . querySelector ( 'img' ) ) . toBeNull ( )
235+ openPage ( 'https://example.com' , 'chat-1' , true )
236+ expect ( container . querySelector ( 'img' ) ) . toBeNull ( )
237+ openPage ( 'https://example.org' )
238+ expect ( container . querySelector ( 'img' ) ) . toBeNull ( )
239+ openPage ( 'https://example.com' )
240+ expect ( container . querySelector ( 'img' ) ) . not . toBeNull ( )
241+ } )
242+
243+ it ( 'requires the local desktop browser and retains an already loaded favicon after closing it' , ( ) => {
244+ browserAvailable . mockReturnValue ( false )
245+ openPage ( 'https://example.com' )
246+ render ( 'https://example.com' )
247+ expect ( container . querySelector ( 'img' ) ) . toBeNull ( )
248+ browserAvailable . mockReturnValue ( true )
249+ render ( 'https://example.com' )
250+ const img = container . querySelector ( 'img' ) !
251+ act ( ( ) => img . dispatchEvent ( new Event ( 'load' ) ) )
252+ act ( ( ) => useBrowserSessionStore . getState ( ) . discardScope ( 'chat-1' ) )
253+ expect ( container . querySelector ( 'img' ) ) . toBe ( img )
254+ expect ( container . querySelector ( 'svg' ) ) . toBeNull ( )
255+ } )
256+
257+ it ( 'does not carry loaded state into another chat with the same destination' , ( ) => {
258+ openPage ( 'https://example.com' )
259+ render ( 'https://example.com' )
260+ act ( ( ) => container . querySelector ( 'img' ) ! . dispatchEvent ( new Event ( 'load' ) ) )
261+ chatIdentity . chatId = 'chat-2'
262+ render ( 'https://example.com' )
263+ expect ( container . querySelector ( 'img' ) ) . toBeNull ( )
264+ expect ( container . querySelector ( 'svg' ) ) . not . toBeNull ( )
265+ } )
266+
206267 it ( 'keeps the globe until load and resets image state when the page origin changes' , ( ) => {
268+ openPage ( 'https://example.com/document' )
207269 render ( 'https://username:password@example.com/document?token=private#section' )
208270 const firstImage = container . querySelector ( 'img' ) !
209271 expect ( firstImage . src ) . toBe ( 'https://example.com/favicon.ico' )
@@ -216,6 +278,7 @@ describe('BrowserAgentIcon', () => {
216278 expect ( container . querySelector ( 'img' ) ) . toBe ( firstImage )
217279 expect ( container . querySelector ( 'svg' ) ) . toBeNull ( )
218280
281+ openPage ( 'https://example.org/document' )
219282 render ( 'https://example.org/document' )
220283 expect ( container . querySelector ( 'img' ) ) . not . toBe ( firstImage )
221284 expect ( container . querySelector ( 'svg' ) ) . not . toBeNull ( )
@@ -225,6 +288,7 @@ describe('BrowserAgentIcon', () => {
225288 expect ( container . querySelector ( 'img' ) ) . toBeNull ( )
226289 expect ( container . querySelector ( 'svg' ) ) . not . toBeNull ( )
227290
291+ openPage ( 'https://example.com' )
228292 render ( 'https://example.com' )
229293 expect ( container . querySelector ( 'img' ) ) . not . toBeNull ( )
230294 render ( 'about:blank' )
0 commit comments