Files
BrowserSkill/apps/extension/src/content/__tests__/overlay-controller.test.ts
T
BB-fat 37fbaacc94 feat(extension): add popup toggle to hide Agent control hints (#81)
Add a per-browser "Control hints" switch to the popup that hides the in-page control overlay (status pill + orange glow, along with its input blocker) while the Agent controls a tab. Preference lives in chrome.storage.local (bsk_control_hints_hidden, default: shown) and is applied live in content scripts via storage.onChanged.

Both popup settings rows share a single Switch component; the hint copy lives in an info-icon tooltip.

Refs #62
2026-08-13 10:24:17 +08:00

161 lines
5.6 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import { OverlayController, shouldShowAgentControlOverlay } from "../overlay-controller";
describe("OverlayController", () => {
it("resets agent overlays without clearing user-tab borrow requests", () => {
const controller = new OverlayController();
controller.addBorrowRequest({
id: "borrow-1",
isActiveTab: true,
tabTitle: "User page",
timeoutMs: 5000,
onAllow: vi.fn(),
onDeny: vi.fn(),
});
controller.activateAgentSession("sess-1");
controller.setAgentHelpRequest({
id: "help-1",
prompt: "Finish login",
selectors: ["#login"],
onContinue: vi.fn(),
onCancel: vi.fn(),
});
controller.setAutomationBypass(true);
controller.resetAgentOverlays("sess-1");
const state = controller.snapshot();
expect(state.borrowRequests).toHaveLength(1);
expect(state.borrowRequests[0]?.id).toBe("borrow-1");
expect(state.controlVisible).toBe(false);
expect(state.activeSessionId).toBeNull();
expect(state.activeHelp).toBeNull();
expect(state.activeRecord).toBeNull();
expect(state.automationBypassCount).toBe(0);
});
it("ignores a reset for a different session", () => {
const controller = new OverlayController();
controller.activateAgentSession("sess-1");
controller.setAutomationBypass(true);
controller.resetAgentOverlays("sess-2");
const state = controller.snapshot();
expect(state.activeSessionId).toBe("sess-1");
expect(state.controlVisible).toBe(true);
expect(state.automationBypassCount).toBe(1);
});
it("returns the previous help request when replacing agent help", () => {
const controller = new OverlayController();
controller.setAgentHelpRequest({
id: "help-1",
prompt: "First request",
selectors: [],
onContinue: vi.fn(),
onCancel: vi.fn(),
});
const previous = controller.setAgentHelpRequest({
id: "help-2",
prompt: "Second request",
selectors: [],
onContinue: vi.fn(),
onCancel: vi.fn(),
});
expect(previous?.id).toBe("help-1");
expect(controller.snapshot().activeHelp?.id).toBe("help-2");
});
it("tracks active record request state", () => {
const controller = new OverlayController();
controller.activateAgentSession("sess-1");
controller.setAgentRecordRequest({
id: "rec-1",
onFinish: vi.fn(),
});
expect(controller.snapshot().activeRecord?.id).toBe("rec-1");
});
it("hides agent control overlay while recording is active", () => {
const controller = new OverlayController();
controller.activateAgentSession("sess-1");
expect(shouldShowAgentControlOverlay(controller.snapshot())).toBe(true);
controller.setAgentRecordRequest({
id: "rec-1",
onFinish: vi.fn(),
});
expect(shouldShowAgentControlOverlay(controller.snapshot())).toBe(false);
expect(controller.snapshot().controlVisible).toBe(true);
});
it("keeps control hidden after record ends until session overlays reset", () => {
const controller = new OverlayController();
controller.activateAgentSession("sess-1");
controller.setAgentRecordRequest({
id: "rec-1",
onFinish: vi.fn(),
});
controller.setAutomationBypass(true);
controller.setAutomationBypass(true);
controller.clearAgentRecordRequest("rec-1");
expect(controller.snapshot().activeRecord).toBeNull();
expect(controller.snapshot().suppressControlAfterRecord).toBe(true);
expect(controller.snapshot().automationBypassCount).toBe(0);
expect(shouldShowAgentControlOverlay(controller.snapshot())).toBe(false);
controller.resetAgentOverlays("sess-1");
expect(controller.snapshot().suppressControlAfterRecord).toBe(false);
expect(controller.snapshot().controlVisible).toBe(false);
});
it("hides control while paused and shows it again only after authoritative control mode", () => {
const controller = new OverlayController();
controller.applyAgentControlMode("sess-1", "paused");
expect(controller.snapshot().activeSessionId).toBe("sess-1");
expect(controller.snapshot().controlVisible).toBe(false);
expect(shouldShowAgentControlOverlay(controller.snapshot())).toBe(false);
controller.applyAgentControlMode("sess-1", "control");
expect(controller.snapshot().activeSessionId).toBe("sess-1");
expect(controller.snapshot().controlVisible).toBe(true);
expect(shouldShowAgentControlOverlay(controller.snapshot())).toBe(true);
});
it("hides the control overlay when the user hides control hints", () => {
const controller = new OverlayController();
controller.activateAgentSession("sess-1");
expect(shouldShowAgentControlOverlay(controller.snapshot())).toBe(true);
controller.setControlHintsHidden(true);
expect(controller.snapshot().controlHintsHidden).toBe(true);
// The session still owns the tab — only the chrome is hidden.
expect(controller.snapshot().controlVisible).toBe(true);
expect(shouldShowAgentControlOverlay(controller.snapshot())).toBe(false);
controller.setControlHintsHidden(false);
expect(shouldShowAgentControlOverlay(controller.snapshot())).toBe(true);
});
it("keeps the control-hints preference across session overlay resets", () => {
const controller = new OverlayController();
controller.activateAgentSession("sess-1");
controller.setControlHintsHidden(true);
controller.resetAgentOverlays("sess-1");
expect(controller.snapshot().controlHintsHidden).toBe(true);
controller.activateAgentSession("sess-2");
expect(shouldShowAgentControlOverlay(controller.snapshot())).toBe(false);
});
});