Download integration-tests/browser-agent.test.ts from SaylorTwift/gemini-cli: direct link, hf CLI and curl.
- Browser
- Download file 10.4 kB
-
https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/integration-tests/browser-agent.test.ts
- Command line
-
hf download hf://SaylorTwift/gemini-cli/integration-tests/browser-agent.test.ts
-
curl -L -o browser-agent.test.ts https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/integration-tests/browser-agent.test.ts
10.4 kB
| /** | |
| * @license | |
| * Copyright 2026 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| /** | |
| * Integration tests for the browser agent. | |
| * | |
| * These tests verify the complete end-to-end flow from CLI prompt through | |
| * browser_agent delegation to MCP/Chrome DevTools and back. Unlike the unit | |
| * tests in packages/core/src/agents/browser/ which mock all MCP components, | |
| * these tests launch real Chrome instances in headless mode. | |
| * | |
| * Tests are skipped on systems without Chrome/Chromium installed. | |
| */ | |
| import { describe, it, expect, beforeEach, afterEach } from 'vitest'; | |
| import { TestRig, assertModelHasOutput } from './test-helper.js'; | |
| import { dirname, join } from 'node:path'; | |
| import { fileURLToPath } from 'node:url'; | |
| import { execSync } from 'node:child_process'; | |
| import { existsSync } from 'node:fs'; | |
| const __filename = fileURLToPath(import.meta.url); | |
| const __dirname = dirname(__filename); | |
| const chromeAvailable = (() => { | |
| try { | |
| if (process.platform === 'darwin') { | |
| execSync( | |
| 'test -d "/Applications/Google Chrome.app" || test -d "/Applications/Chromium.app"', | |
| { | |
| stdio: 'ignore', | |
| }, | |
| ); | |
| } else if (process.platform === 'linux') { | |
| execSync( | |
| 'which google-chrome || which chromium-browser || which chromium', | |
| { stdio: 'ignore' }, | |
| ); | |
| } else if (process.platform === 'win32') { | |
| // Check standard Windows installation paths using Node.js fs | |
| const chromePaths = [ | |
| 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe', | |
| 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe', | |
| `${process.env['LOCALAPPDATA'] ?? ''}\\Google\\Chrome\\Application\\chrome.exe`, | |
| ]; | |
| const found = chromePaths.some((p) => existsSync(p)); | |
| if (!found) { | |
| // Fall back to PATH check | |
| execSync('where chrome || where chromium', { stdio: 'ignore' }); | |
| } | |
| } else { | |
| return false; | |
| } | |
| return true; | |
| } catch { | |
| return false; | |
| } | |
| })(); | |
| describe.skipIf(!chromeAvailable)('browser-agent', () => { | |
| let rig: TestRig; | |
| beforeEach(() => { | |
| rig = new TestRig(); | |
| }); | |
| afterEach(async () => await rig.cleanup()); | |
| it('should navigate to a page and capture accessibility tree', async () => { | |
| rig.setup('browser-navigate-and-snapshot', { | |
| fakeResponsesPath: join( | |
| __dirname, | |
| 'browser-agent.navigate-snapshot.responses', | |
| ), | |
| settings: { | |
| agents: { | |
| overrides: { | |
| browser_agent: { | |
| enabled: true, | |
| }, | |
| }, | |
| browser: { | |
| headless: true, | |
| sessionMode: 'isolated', | |
| }, | |
| }, | |
| }, | |
| }); | |
| const result = await rig.run({ | |
| args: 'Open https://example.com in the browser and tell me the page title and main content.', | |
| }); | |
| assertModelHasOutput(result); | |
| const toolLogs = rig.readToolLogs(); | |
| const browserAgentCall = toolLogs.find( | |
| (t) => | |
| t.toolRequest.name === 'invoke_agent' && | |
| JSON.parse(t.toolRequest.args).agent_name === 'browser_agent', | |
| ); | |
| expect( | |
| browserAgentCall, | |
| 'Expected browser_agent to be called', | |
| ).toBeDefined(); | |
| }); | |
| it('should take screenshots of web pages', async () => { | |
| rig.setup('browser-screenshot', { | |
| fakeResponsesPath: join(__dirname, 'browser-agent.screenshot.responses'), | |
| settings: { | |
| agents: { | |
| overrides: { | |
| browser_agent: { | |
| enabled: true, | |
| }, | |
| }, | |
| browser: { | |
| headless: true, | |
| sessionMode: 'isolated', | |
| }, | |
| }, | |
| }, | |
| }); | |
| const result = await rig.run({ | |
| args: 'Navigate to https://example.com and take a screenshot.', | |
| }); | |
| const toolLogs = rig.readToolLogs(); | |
| const browserCalls = toolLogs.filter( | |
| (t) => | |
| t.toolRequest.name === 'invoke_agent' && | |
| JSON.parse(t.toolRequest.args).agent_name === 'browser_agent', | |
| ); | |
| expect(browserCalls.length).toBeGreaterThan(0); | |
| assertModelHasOutput(result); | |
| }); | |
| it('should interact with page elements', async () => { | |
| rig.setup('browser-interaction', { | |
| fakeResponsesPath: join(__dirname, 'browser-agent.interaction.responses'), | |
| settings: { | |
| agents: { | |
| overrides: { | |
| browser_agent: { | |
| enabled: true, | |
| }, | |
| }, | |
| browser: { | |
| headless: true, | |
| sessionMode: 'isolated', | |
| }, | |
| }, | |
| }, | |
| }); | |
| const result = await rig.run({ | |
| args: 'Go to https://example.com, find any links on the page, and describe them.', | |
| }); | |
| const toolLogs = rig.readToolLogs(); | |
| const browserAgentCall = toolLogs.find( | |
| (t) => | |
| t.toolRequest.name === 'invoke_agent' && | |
| JSON.parse(t.toolRequest.args).agent_name === 'browser_agent', | |
| ); | |
| expect( | |
| browserAgentCall, | |
| 'Expected browser_agent to be called', | |
| ).toBeDefined(); | |
| assertModelHasOutput(result); | |
| }); | |
| it('should clean up browser processes after completion', async () => { | |
| rig.setup('browser-cleanup', { | |
| fakeResponsesPath: join(__dirname, 'browser-agent.cleanup.responses'), | |
| settings: { | |
| agents: { | |
| overrides: { | |
| browser_agent: { | |
| enabled: true, | |
| }, | |
| }, | |
| browser: { | |
| headless: true, | |
| sessionMode: 'isolated', | |
| }, | |
| }, | |
| }, | |
| }); | |
| await rig.run({ | |
| args: 'Open https://example.com in the browser and check the page title.', | |
| }); | |
| // Test passes if we reach here, relying on Vitest's timeout mechanism | |
| // to detect hanging browser processes. | |
| }); | |
| it('should handle multiple browser operations in sequence', async () => { | |
| rig.setup('browser-sequential', { | |
| fakeResponsesPath: join(__dirname, 'browser-agent.sequential.responses'), | |
| settings: { | |
| agents: { | |
| overrides: { | |
| browser_agent: { | |
| enabled: true, | |
| }, | |
| }, | |
| browser: { | |
| headless: true, | |
| sessionMode: 'isolated', | |
| }, | |
| }, | |
| }, | |
| }); | |
| const result = await rig.run({ | |
| args: 'Navigate to https://example.com, take a snapshot of the accessibility tree, then take a screenshot.', | |
| }); | |
| const toolLogs = rig.readToolLogs(); | |
| const browserCalls = toolLogs.filter( | |
| (t) => | |
| t.toolRequest.name === 'invoke_agent' && | |
| JSON.parse(t.toolRequest.args).agent_name === 'browser_agent', | |
| ); | |
| expect(browserCalls.length).toBeGreaterThan(0); | |
| // Should successfully complete all operations | |
| assertModelHasOutput(result); | |
| }); | |
| it('should keep browser open across multiple browser_agent invocations', async () => { | |
| rig.setup('browser-persistent-session', { | |
| fakeResponsesPath: join( | |
| __dirname, | |
| 'browser-agent.persistent-session.responses', | |
| ), | |
| settings: { | |
| agents: { | |
| overrides: { | |
| browser_agent: { | |
| enabled: true, | |
| }, | |
| }, | |
| browser: { | |
| headless: true, | |
| sessionMode: 'isolated', | |
| allowedDomains: ['example.com'], | |
| }, | |
| }, | |
| }, | |
| }); | |
| const result = await rig.run({ | |
| args: 'First, ask the browser agent to get the page title of example.com. After you receive that response, you MUST invoke the browser agent a second time to check for links on the page.', | |
| }); | |
| const toolLogs = rig.readToolLogs(); | |
| const browserCalls = toolLogs.filter( | |
| (t) => | |
| t.toolRequest.name === 'invoke_agent' && | |
| JSON.parse(t.toolRequest.args).agent_name === 'browser_agent', | |
| ); | |
| // Both browser_agent invocations must succeed — if the browser was | |
| // incorrectly closed after the first call (regression #24210), | |
| // the second call would fail. | |
| expect( | |
| browserCalls.length, | |
| 'Expected browser_agent to be called twice', | |
| ).toBe(2); | |
| expect( | |
| browserCalls.every((c) => c.toolRequest.success), | |
| 'Both browser_agent calls should succeed', | |
| ).toBe(true); | |
| assertModelHasOutput(result); | |
| }); | |
| it('should handle tool confirmation for write_file without crashing', async () => { | |
| rig.setup('tool-confirmation', { | |
| fakeResponsesPath: join( | |
| __dirname, | |
| 'browser-agent.confirmation.responses', | |
| ), | |
| settings: { | |
| agents: { | |
| overrides: { | |
| browser_agent: { | |
| enabled: true, | |
| }, | |
| }, | |
| browser: { | |
| headless: true, | |
| sessionMode: 'isolated', | |
| }, | |
| }, | |
| }, | |
| }); | |
| const run = await rig.runInteractive({ approvalMode: 'default' }); | |
| await run.type('Write hello to test.txt'); | |
| await run.type('\r'); | |
| await run.expectText('Allow', 15000); | |
| await run.type('y'); | |
| await run.type('\r'); | |
| await run.expectText('successfully written', 15000); | |
| }); | |
| it('should handle concurrent browser agents with isolated session mode', async () => { | |
| rig.setup('browser-concurrent', { | |
| fakeResponsesPath: join(__dirname, 'browser-agent.concurrent.responses'), | |
| settings: { | |
| agents: { | |
| overrides: { | |
| browser_agent: { | |
| enabled: true, | |
| }, | |
| }, | |
| browser: { | |
| headless: true, | |
| // Isolated mode supports concurrent browser agents. | |
| // Persistent/existing modes reject concurrent calls to prevent | |
| // Chrome profile lock conflicts. | |
| sessionMode: 'isolated', | |
| }, | |
| }, | |
| }, | |
| }); | |
| const result = await rig.run({ | |
| args: 'Launch two browser agents concurrently to check example.com', | |
| }); | |
| assertModelHasOutput(result); | |
| const toolLogs = rig.readToolLogs(); | |
| const browserCalls = toolLogs.filter( | |
| (t) => | |
| t.toolRequest.name === 'invoke_agent' && | |
| JSON.parse(t.toolRequest.args).agent_name === 'browser_agent', | |
| ); | |
| // Both browser_agent invocations should have been called | |
| expect(browserCalls.length).toBe(2); | |
| // Both should complete successfully (no errors) | |
| for (const call of browserCalls) { | |
| expect( | |
| call.toolRequest.success, | |
| `browser_agent call failed: ${JSON.stringify(call.toolRequest)}`, | |
| ).toBe(true); | |
| } | |
| }); | |
| }); | |