import { afterEach, describe, expect, it, vi } from 'vitest'; import type { BrowserContext, Page } from 'playwright'; import { BrowserClient, type BrowserRuntime, type ConnectedBrowser, } from '../src/browser/client.js'; import type { Logger } from '../src/utils/logger.js'; import { getTestConfig } from './testUtils.js'; function createLogger(): Logger { return { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), } as unknown as Logger; } afterEach(() => { delete process.env.CHROME_CDP_URL; }); describe('BrowserClient external Chrome', () => { it('attaches to independently launched Chrome before considering Playwright launch', async () => { process.env.CHROME_CDP_URL = 'http://127.0.0.1:9222'; const context = { pages: () => [] } as unknown as BrowserContext; const connectedBrowser = { contexts: () => [context], close: vi.fn(), } as unknown as ConnectedBrowser; const runtime: BrowserRuntime = { connectOverCDP: vi.fn().mockResolvedValue(connectedBrowser), launchPersistentContext: vi.fn(), }; const client = new BrowserClient(getTestConfig(), createLogger(), runtime); const result = await client.initialize(); expect(result).toBe(context); expect(runtime.connectOverCDP).toHaveBeenCalledWith('http://127.0.0.1:9222'); expect(runtime.launchPersistentContext).not.toHaveBeenCalled(); }); it('falls back to Playwright only when external Chrome cannot be reached', async () => { process.env.CHROME_CDP_URL = 'http://127.0.0.1:9222'; const fallbackContext = { pages: () => [] } as unknown as BrowserContext; const runtime: BrowserRuntime = { connectOverCDP: vi.fn().mockRejectedValue(new Error('connection refused')), launchPersistentContext: vi.fn().mockResolvedValue(fallbackContext), }; const client = new BrowserClient(getTestConfig(), createLogger(), runtime); const result = await client.initialize(); expect(result).toBe(fallbackContext); expect(runtime.launchPersistentContext).toHaveBeenCalledOnce(); }); it('opens the login page for a human without submitting credentials', async () => { process.env.CHROME_CDP_URL = 'http://127.0.0.1:9222'; const page = { goto: vi.fn().mockResolvedValue(undefined), bringToFront: vi.fn().mockResolvedValue(undefined), fill: vi.fn(), } as unknown as Page; const context = { pages: () => [page] } as unknown as BrowserContext; const runtime: BrowserRuntime = { connectOverCDP: vi.fn().mockResolvedValue({ contexts: () => [context], close: vi.fn(), }), launchPersistentContext: vi.fn(), }; const client = new BrowserClient(getTestConfig({ reddit: { username: 'human', password: 'secret' }, }), createLogger(), runtime); await client.initialize(); await expect(client.login()).resolves.toBe(false); expect(page.bringToFront).toHaveBeenCalledOnce(); expect(page.goto).toHaveBeenCalledWith( 'https://www.reddit.com/login', { waitUntil: 'domcontentloaded' }, ); expect(page.fill).not.toHaveBeenCalled(); expect(client.getSessionState()).toBe('login-required'); }); });