// @vitest-environment node import { beforeEach, describe, expect, it, vi } from 'vitest'; const mocks = vi.hoisted(() => ({ appendMessage: vi.fn(), setActiveRun: vi.fn(), clearActiveRun: vi.fn(), extractBrowserOpenIntent: vi.fn(), openUrlInBrowser: vi.fn(), appendTranscriptLine: vi.fn(), logger: { error: vi.fn(), }, })); vi.mock('../electron/gateway/session-store', () => ({ sessionStore: { appendMessage: mocks.appendMessage, setActiveRun: mocks.setActiveRun, clearActiveRun: mocks.clearActiveRun, }, })); vi.mock('@electron/service/browser-open-service', () => ({ extractBrowserOpenIntent: mocks.extractBrowserOpenIntent, openUrlInBrowser: mocks.openUrlInBrowser, })); vi.mock('@electron/utils/token-usage-writer', () => ({ appendTranscriptLine: mocks.appendTranscriptLine, })); vi.mock('@electron/service/logger', () => ({ default: mocks.logger, })); function flushAsyncTasks(): Promise { return new Promise((resolve) => { setTimeout(resolve, 0); }); } describe('gateway browser shortcut', () => { beforeEach(() => { vi.resetModules(); vi.clearAllMocks(); mocks.extractBrowserOpenIntent.mockImplementation((text: string) => ( text === '打开 http://www.baidu.com' ? { url: 'http://www.baidu.com/' } : null )); }); it('starts a browser-open run and emits a final assistant message on success', async () => { mocks.openUrlInBrowser.mockResolvedValue({ url: 'http://www.baidu.com/', pageUrl: 'http://www.baidu.com/', title: '百度一下,你就知道', }); const { maybeHandleBrowserOpenMessage } = await import('../electron/gateway/browser-shortcut'); const broadcast = vi.fn(); const handled = maybeHandleBrowserOpenMessage( 'agent:test:main', 'run-1', { role: 'user', content: '打开 http://www.baidu.com' }, broadcast, ); expect(handled).toBe(true); expect(mocks.setActiveRun).toHaveBeenCalledWith( 'agent:test:main', 'run-1', expect.any(AbortController), ); await flushAsyncTasks(); expect(mocks.openUrlInBrowser).toHaveBeenCalledWith( 'http://www.baidu.com/', expect.objectContaining({ signal: expect.any(AbortSignal) }), ); expect(mocks.appendMessage).toHaveBeenCalledWith( 'agent:test:main', expect.objectContaining({ role: 'assistant', content: '已为你打开 http://www.baidu.com/(百度一下,你就知道)', }), ); expect(broadcast).toHaveBeenCalledWith(expect.objectContaining({ type: 'chat:final', sessionKey: 'agent:test:main', runId: 'run-1', })); }); it('emits a failure assistant message when browser open fails', async () => { mocks.openUrlInBrowser.mockRejectedValue(new Error('No browser context available')); const { maybeHandleBrowserOpenMessage } = await import('../electron/gateway/browser-shortcut'); const broadcast = vi.fn(); const handled = maybeHandleBrowserOpenMessage( 'agent:test:main', 'run-2', { role: 'user', content: '打开 http://www.baidu.com' }, broadcast, ); expect(handled).toBe(true); await flushAsyncTasks(); expect(mocks.appendMessage).toHaveBeenCalledWith( 'agent:test:main', expect.objectContaining({ role: 'assistant', content: '打开失败:No browser context available', }), ); expect(broadcast).toHaveBeenCalledWith(expect.objectContaining({ type: 'chat:final', runId: 'run-2', })); }); it('returns false when the user message is not an explicit browser-open command', async () => { mocks.extractBrowserOpenIntent.mockReturnValue(null); const { maybeHandleBrowserOpenMessage } = await import('../electron/gateway/browser-shortcut'); expect( maybeHandleBrowserOpenMessage( 'agent:test:main', 'run-3', { role: 'user', content: '帮我总结一下百度首页' }, vi.fn(), ), ).toBe(false); expect(mocks.openUrlInBrowser).not.toHaveBeenCalled(); }); });