// @vitest-environment node import { beforeEach, describe, expect, it, vi } from 'vitest'; const mocks = vi.hoisted(() => { const state = { currentUrl: 'about:blank', }; const page = { url: vi.fn(() => state.currentUrl), goto: vi.fn(async (url: string) => { state.currentUrl = url; }), bringToFront: vi.fn(async () => {}), title: vi.fn(async () => '百度一下,你就知道'), }; const context = { pages: vi.fn(() => [page]), newPage: vi.fn(async () => page), }; const browser = { contexts: vi.fn(() => [context]), disconnect: vi.fn(async () => {}), close: vi.fn(async () => {}), }; return { state, page, context, browser, launchLocalChrome: vi.fn(async () => {}), isChromeRunning: vi.fn(async () => true), connectOverCDP: vi.fn(async () => browser), logger: { info: vi.fn(), warn: vi.fn(), }, }; }); vi.mock('@electron/utils/chrome/launchLocalChrome', () => ({ launchLocalChrome: mocks.launchLocalChrome, })); vi.mock('@electron/utils/chrome/isChromeRunning', () => ({ isChromeRunning: mocks.isChromeRunning, })); vi.mock('@electron/service/logger', () => ({ default: mocks.logger, })); vi.mock('playwright', () => ({ chromium: { connectOverCDP: mocks.connectOverCDP, }, })); describe('browser open service', () => { beforeEach(() => { vi.resetModules(); vi.clearAllMocks(); mocks.state.currentUrl = 'about:blank'; mocks.page.url.mockImplementation(() => mocks.state.currentUrl); mocks.page.goto.mockImplementation(async (url: string) => { mocks.state.currentUrl = url; }); }); it('extracts an explicit open-url intent and normalizes the URL', async () => { const { extractBrowserOpenIntent } = await import('../electron/service/browser-open-service'); expect(extractBrowserOpenIntent('打开 http://www.baidu.com。')).toEqual({ url: 'http://www.baidu.com/', }); expect(extractBrowserOpenIntent('请帮我分析一下 http://www.baidu.com')).toBeNull(); expect(extractBrowserOpenIntent('打开 javascript:alert(1)')).toBeNull(); }); it('launches chrome via cdp and opens the requested page', async () => { const { openUrlInBrowser } = await import('../electron/service/browser-open-service'); const result = await openUrlInBrowser('http://www.baidu.com'); expect(mocks.launchLocalChrome).toHaveBeenCalledTimes(1); expect(mocks.connectOverCDP).toHaveBeenCalledWith('http://127.0.0.1:9222'); expect(mocks.page.bringToFront).toHaveBeenCalledTimes(1); expect(mocks.page.goto).toHaveBeenCalledWith('http://www.baidu.com/', expect.objectContaining({ waitUntil: 'domcontentloaded', timeout: 15000, })); expect(mocks.browser.disconnect).toHaveBeenCalledTimes(1); expect(result).toEqual({ url: 'http://www.baidu.com/', pageUrl: 'http://www.baidu.com/', title: '百度一下,你就知道', }); }); it('wraps cdp connection errors with a clearer message', async () => { mocks.connectOverCDP.mockRejectedValueOnce(new Error('connect ECONNREFUSED 127.0.0.1:9222')); const { openUrlInBrowser } = await import('../electron/service/browser-open-service'); await expect(openUrlInBrowser('http://www.baidu.com')).rejects.toThrow( 'Chrome 已启动但远程调试端口 9222 不可用', ); }); });