fix(extension): release automation state when returning borrowed tabs

This commit is contained in:
drakezhang
2026-09-07 11:30:01 +08:00
parent 1498cd425c
commit f70978bfcf
10 changed files with 343 additions and 20 deletions
@@ -747,6 +747,78 @@ describe("ToolDispatcher", () => {
);
});
it("releases the returned tab's hover before moving it and leaves other tabs alone", async () => {
const sendMessage = vi.fn(async () => undefined);
const move = vi.fn(async () => {
expect(sendMessage).toHaveBeenCalledWith(7, expect.objectContaining({ enabled: false }));
return { id: 7, windowId: 200, index: 4 };
});
vi.stubGlobal("chrome", {
tabs: { sendMessage, move },
windows: { get: vi.fn(async () => ({ id: 200 })) },
});
const { transport, sent, deliver } = fakeTransport();
const sessions = new SessionManager({
agentWindow: {
create: vi.fn(async () => 100),
remove: vi.fn(async () => {}),
ensureActiveTab: vi.fn(async () => 1),
},
});
const ctx = await sessions.start("aa11");
ctx.borrowedTabs.set(7, { tabId: 7, originalWindowId: 200, originalIndex: 4 });
const cdp = {
send: vi.fn(async <T>() => ({}) as T),
detachSession: vi.fn(async () => {}),
releaseSessionTab: vi.fn(async () => {}),
};
const dispatcher = new ToolDispatcher({
transport,
sessions,
cdp: cdp as unknown as TestDispatcherCdp,
});
const helpers = dispatcher as unknown as {
rememberHover: (sessionId: string, result: unknown) => unknown;
setHoverBypass: (sessionId: string, tabId: number, enabled: boolean) => Promise<void>;
withHoverReassert: <T>(
params: { session_id: string; tab_id?: number },
work: () => Promise<T>,
) => Promise<T>;
};
for (const [sessionId, tabId] of [
["aa11", 7],
["aa11", 8],
["bb22", 9],
] as const) {
helpers.rememberHover(sessionId, { tab_id: tabId, x: 10, y: 20 });
await helpers.setHoverBypass(sessionId, tabId, true);
}
dispatcher.start();
deliver(makeRequest("tool.tab_return", { session_id: "aa11", tab_id: 7 }));
await vi.waitFor(() => expect(sent).toHaveLength(1));
expect(sent[0]).toMatchObject({ result: { tab_id: 7, returned_to_window_id: 200 } });
expect(cdp.releaseSessionTab).toHaveBeenCalledExactlyOnceWith("aa11", 7);
await helpers.withHoverReassert({ session_id: "aa11", tab_id: 7 }, async () => ({}));
expect(cdp.send).not.toHaveBeenCalled();
for (const [sessionId, tabId] of [
["aa11", 8],
["bb22", 9],
] as const) {
expect(sendMessage).not.toHaveBeenCalledWith(
tabId,
expect.objectContaining({ enabled: false }),
);
await helpers.withHoverReassert({ session_id: sessionId, tab_id: tabId }, async () => ({}));
expect(cdp.send).toHaveBeenLastCalledWith(tabId, "Input.dispatchMouseEvent", {
type: "mouseMoved",
x: 10,
y: 20,
});
}
dispatcher.stop();
});
it("transfers hover overlay bypass ownership between sessions", async () => {
vi.stubGlobal("chrome", {
tabs: {