import { describe, it, expect, beforeEach, jest as _jest } from '@jest/globals'; import { createBrowserTools, hasBrowserCapability, EBrowserTools, type BrowserToolResponse, } from '../BrowserTools'; describe('BrowserTools', () => { describe('EBrowserTools enum', () => { it('should have all expected browser tool names', () => { expect(EBrowserTools.CLICK).toBe('browser_click'); expect(EBrowserTools.TYPE).toBe('browser_type'); expect(EBrowserTools.NAVIGATE).toBe('browser_navigate'); expect(EBrowserTools.SCROLL).toBe('browser_scroll'); expect(EBrowserTools.EXTRACT).toBe('browser_extract'); expect(EBrowserTools.HOVER).toBe('browser_hover'); expect(EBrowserTools.WAIT).toBe('browser_wait'); expect(EBrowserTools.BACK).toBe('browser_back'); expect(EBrowserTools.SCREENSHOT).toBe('browser_screenshot'); expect(EBrowserTools.GET_PAGE_STATE).toBe('browser_get_page_state'); expect(EBrowserTools.KEYPRESS).toBe('browser_keypress'); expect(EBrowserTools.SWITCH_TAB).toBe('browser_switch_tab'); }); it('should have exactly 12 browser tools', () => { expect(Object.keys(EBrowserTools).length).toBe(12); }); }); describe('hasBrowserCapability', () => { it('should return false when no request is provided', () => { expect(hasBrowserCapability()).toBe(false); expect(hasBrowserCapability(undefined)).toBe(false); }); it('should return false when request has no headers', () => { expect(hasBrowserCapability({})).toBe(false); expect(hasBrowserCapability({ headers: undefined })).toBe(false); }); it('should return true when X-Illuma-Browser-Extension header is true', () => { const req = { headers: { 'x-illuma-browser-extension': 'true', }, }; expect(hasBrowserCapability(req)).toBe(true); }); it('should return true when X-Illuma-Browser-Capable header is true', () => { const req = { headers: { 'x-illuma-browser-capable': 'true', }, }; expect(hasBrowserCapability(req)).toBe(true); }); it('should return false when browser headers are not true', () => { const req = { headers: { 'x-illuma-browser-extension': 'false', 'x-illuma-browser-capable': 'false', }, }; expect(hasBrowserCapability(req)).toBe(false); }); it('should return false when browser headers are missing', () => { const req = { headers: { 'content-type': 'application/json', }, }; expect(hasBrowserCapability(req)).toBe(false); }); }); describe('createBrowserTools', () => { let tools: ReturnType; beforeEach(() => { tools = createBrowserTools(); }); it('should create exactly 12 browser tools', () => { expect(tools.length).toBe(12); }); it('should create tools with correct names', () => { const toolNames = tools.map((t) => t.name); expect(toolNames).toContain('browser_click'); expect(toolNames).toContain('browser_type'); expect(toolNames).toContain('browser_navigate'); expect(toolNames).toContain('browser_scroll'); expect(toolNames).toContain('browser_extract'); expect(toolNames).toContain('browser_hover'); expect(toolNames).toContain('browser_wait'); expect(toolNames).toContain('browser_back'); expect(toolNames).toContain('browser_screenshot'); expect(toolNames).toContain('browser_get_page_state'); }); it('should have descriptions for all tools', () => { for (const tool of tools) { expect(tool.description).toBeDefined(); expect(tool.description.length).toBeGreaterThan(0); } }); describe('browser_click tool', () => { it('should return requiresBrowserExecution marker', async () => { const clickTool = tools.find((t) => t.name === 'browser_click'); expect(clickTool).toBeDefined(); const result = await clickTool!.invoke({ index: 5 }); const parsed: BrowserToolResponse = JSON.parse(result); expect(parsed.requiresBrowserExecution).toBe(true); expect(parsed.action).toBe('click'); expect(parsed.args).toEqual({ index: 5 }); }); }); describe('browser_type tool', () => { it('should return requiresBrowserExecution marker with all args', async () => { const typeTool = tools.find((t) => t.name === 'browser_type'); expect(typeTool).toBeDefined(); const result = await typeTool!.invoke({ index: 3, text: 'Hello World', pressEnter: true, }); const parsed: BrowserToolResponse = JSON.parse(result); expect(parsed.requiresBrowserExecution).toBe(true); expect(parsed.action).toBe('type'); expect(parsed.args.index).toBe(3); expect(parsed.args.text).toBe('Hello World'); expect(parsed.args.pressEnter).toBe(true); }); }); describe('browser_navigate tool', () => { it('should return requiresBrowserExecution marker with URL', async () => { const navigateTool = tools.find((t) => t.name === 'browser_navigate'); expect(navigateTool).toBeDefined(); const result = await navigateTool!.invoke({ url: 'https://example.com', }); const parsed: BrowserToolResponse = JSON.parse(result); expect(parsed.requiresBrowserExecution).toBe(true); expect(parsed.action).toBe('navigate'); expect(parsed.args.url).toBe('https://example.com'); }); }); describe('browser_scroll tool', () => { it('should return requiresBrowserExecution marker with direction', async () => { const scrollTool = tools.find((t) => t.name === 'browser_scroll'); expect(scrollTool).toBeDefined(); const result = await scrollTool!.invoke({ direction: 'down', amount: 500, }); const parsed: BrowserToolResponse = JSON.parse(result); expect(parsed.requiresBrowserExecution).toBe(true); expect(parsed.action).toBe('scroll'); expect(parsed.args.direction).toBe('down'); expect(parsed.args.amount).toBe(500); }); }); describe('browser_hover tool', () => { it('should return requiresBrowserExecution marker with index', async () => { const hoverTool = tools.find((t) => t.name === 'browser_hover'); expect(hoverTool).toBeDefined(); const result = await hoverTool!.invoke({ index: 2 }); const parsed: BrowserToolResponse = JSON.parse(result); expect(parsed.requiresBrowserExecution).toBe(true); expect(parsed.action).toBe('hover'); expect(parsed.args.index).toBe(2); }); }); describe('browser_wait tool', () => { it('should return requiresBrowserExecution marker with duration', async () => { const waitTool = tools.find((t) => t.name === 'browser_wait'); expect(waitTool).toBeDefined(); const result = await waitTool!.invoke({ duration: 2000 }); const parsed: BrowserToolResponse = JSON.parse(result); expect(parsed.requiresBrowserExecution).toBe(true); expect(parsed.action).toBe('wait'); expect(parsed.args.duration).toBe(2000); }); }); describe('browser_back tool', () => { it('should return requiresBrowserExecution marker', async () => { const backTool = tools.find((t) => t.name === 'browser_back'); expect(backTool).toBeDefined(); const result = await backTool!.invoke({}); const parsed: BrowserToolResponse = JSON.parse(result); expect(parsed.requiresBrowserExecution).toBe(true); expect(parsed.action).toBe('back'); }); }); describe('browser_screenshot tool', () => { it('should return requiresBrowserExecution marker', async () => { const screenshotTool = tools.find( (t) => t.name === 'browser_screenshot' ); expect(screenshotTool).toBeDefined(); const result = await screenshotTool!.invoke({}); const parsed: BrowserToolResponse = JSON.parse(result); expect(parsed.requiresBrowserExecution).toBe(true); expect(parsed.action).toBe('screenshot'); }); }); describe('browser_get_page_state tool', () => { it('should return requiresBrowserExecution marker', async () => { const pageStateTool = tools.find( (t) => t.name === 'browser_get_page_state' ); expect(pageStateTool).toBeDefined(); const result = await pageStateTool!.invoke({}); const parsed: BrowserToolResponse = JSON.parse(result); expect(parsed.requiresBrowserExecution).toBe(true); expect(parsed.action).toBe('get_page_state'); }); }); describe('browser_extract tool', () => { it('should return requiresBrowserExecution marker', async () => { const extractTool = tools.find((t) => t.name === 'browser_extract'); expect(extractTool).toBeDefined(); const result = await extractTool!.invoke({ query: 'main content' }); const parsed: BrowserToolResponse = JSON.parse(result); expect(parsed.requiresBrowserExecution).toBe(true); expect(parsed.action).toBe('extract'); expect(parsed.args.query).toBe('main content'); }); }); /* eslint-disable @typescript-eslint/explicit-function-return-type */ describe('with waitForResult callback (formatResultForLLM)', () => { it('should format successful result with full page state', async () => { const mockCallback = async () => ({ success: true, url: 'https://example.com/page', title: 'Example Page', elementList: '[0]