fix(browser): keep input blocker active across navigations (#22562)

Co-authored-by: cynthialong0-0 <82900738+cynthialong0-0@users.noreply.github.com>
This commit is contained in:
Aditya Bijalwan
2026-03-26 22:24:49 +05:30
committed by GitHub
parent a3c1c659fd
commit 5755ec2dcf
6 changed files with 163 additions and 31 deletions

View File

@@ -9,6 +9,7 @@ import { BrowserManager } from './browserManager.js';
import { makeFakeConfig } from '../../test-utils/config.js';
import type { Config } from '../../config/config.js';
import { injectAutomationOverlay } from './automationOverlay.js';
import { injectInputBlocker } from './inputBlocker.js';
import { coreEvents } from '../../utils/events.js';
// Mock the MCP SDK
@@ -54,6 +55,13 @@ vi.mock('./automationOverlay.js', () => ({
injectAutomationOverlay: vi.fn().mockResolvedValue(undefined),
}));
vi.mock('./inputBlocker.js', () => ({
injectInputBlocker: vi.fn().mockResolvedValue(undefined),
removeInputBlocker: vi.fn().mockResolvedValue(undefined),
suspendInputBlocker: vi.fn().mockResolvedValue(undefined),
resumeInputBlocker: vi.fn().mockResolvedValue(undefined),
}));
vi.mock('node:fs', async (importOriginal) => {
const actual = await importOriginal<typeof import('node:fs')>();
return {
@@ -78,6 +86,7 @@ describe('BrowserManager', () => {
beforeEach(() => {
vi.resetAllMocks();
vi.mocked(injectAutomationOverlay).mockClear();
vi.mocked(injectInputBlocker).mockClear();
vi.spyOn(coreEvents, 'emitFeedback').mockImplementation(() => {});
// Re-establish consent mock after resetAllMocks
@@ -692,21 +701,66 @@ describe('BrowserManager', () => {
});
describe('overlay re-injection in callTool', () => {
it('should re-inject overlay after click in non-headless mode', async () => {
it('should re-inject overlay and input blocker after click in non-headless mode when input disabling is enabled', async () => {
// Enable input disabling in config
mockConfig = makeFakeConfig({
agents: {
overrides: {
browser_agent: {
enabled: true,
},
},
browser: {
headless: false,
disableUserInput: true,
},
},
});
const manager = new BrowserManager(mockConfig);
await manager.callTool('click', { uid: '1_2' });
expect(injectAutomationOverlay).toHaveBeenCalledWith(manager, undefined);
expect(injectInputBlocker).toHaveBeenCalledWith(manager, undefined);
});
it('should re-inject overlay after navigate_page in non-headless mode', async () => {
it('should re-inject overlay and input blocker after navigate_page in non-headless mode when input disabling is enabled', async () => {
mockConfig = makeFakeConfig({
agents: {
overrides: {
browser_agent: {
enabled: true,
},
},
browser: {
headless: false,
disableUserInput: true,
},
},
});
const manager = new BrowserManager(mockConfig);
await manager.callTool('navigate_page', { url: 'https://example.com' });
expect(injectAutomationOverlay).toHaveBeenCalledWith(manager, undefined);
expect(injectInputBlocker).toHaveBeenCalledWith(manager, undefined);
});
it('should re-inject overlay after click_at, new_page, press_key, handle_dialog', async () => {
it('should re-inject overlay and input blocker after click_at, new_page, press_key, handle_dialog when input disabling is enabled', async () => {
mockConfig = makeFakeConfig({
agents: {
overrides: {
browser_agent: {
enabled: true,
},
},
browser: {
headless: false,
disableUserInput: true,
},
},
});
const manager = new BrowserManager(mockConfig);
for (const tool of [
'click_at',
@@ -715,12 +769,15 @@ describe('BrowserManager', () => {
'handle_dialog',
]) {
vi.mocked(injectAutomationOverlay).mockClear();
vi.mocked(injectInputBlocker).mockClear();
await manager.callTool(tool, {});
expect(injectAutomationOverlay).toHaveBeenCalledTimes(1);
expect(injectInputBlocker).toHaveBeenCalledTimes(1);
expect(injectInputBlocker).toHaveBeenCalledWith(manager, undefined);
}
});
it('should NOT re-inject overlay after read-only tools', async () => {
it('should NOT re-inject overlay or input blocker after read-only tools', async () => {
const manager = new BrowserManager(mockConfig);
for (const tool of [
'take_snapshot',
@@ -729,8 +786,10 @@ describe('BrowserManager', () => {
'fill',
]) {
vi.mocked(injectAutomationOverlay).mockClear();
vi.mocked(injectInputBlocker).mockClear();
await manager.callTool(tool, {});
expect(injectAutomationOverlay).not.toHaveBeenCalled();
expect(injectInputBlocker).not.toHaveBeenCalled();
}
});