mirror of
https://github.com/Tencent/BrowserSkill.git
synced 2026-10-02 07:34:35 +08:00
fix(extension): decouple help overlay and notification cleanup
Start tab and notification cleanup concurrently so a stalled notification cannot prevent cancellation messages from reaching the page overlays. Cover content completion, timeout, and cancellation across multiple tabs.
This commit is contained in:
@@ -445,6 +445,81 @@ describe("handleRequestHelp", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it.each([
|
||||
"content",
|
||||
"timeout",
|
||||
"abort",
|
||||
] as const)("cancels all help overlays when notification cleanup hangs (%s)", async (finishPath) => {
|
||||
vi.useFakeTimers();
|
||||
const chromeEvents = installHelpLifecycleChrome();
|
||||
const abort = new AbortController();
|
||||
const sendToTab = vi.fn(async (_tabId: number, _message: { requestId: string }) => ({
|
||||
type: "bsk-help-ack",
|
||||
ok: true,
|
||||
}));
|
||||
const notifications = {
|
||||
create: vi.fn(async (id: string) => id),
|
||||
clear: vi.fn(() => new Promise<boolean>(() => {})),
|
||||
};
|
||||
const deps = baseDeps({
|
||||
autoAttachLifecycle: undefined,
|
||||
sendToTab,
|
||||
notifications,
|
||||
signal: abort.signal,
|
||||
tabsApi: {
|
||||
get: vi.fn(async (id: number) => ({ id, windowId: 99, active: id === 5 }) as never),
|
||||
query: vi.fn(async () => [{ id: 5, windowId: 99, active: true }] as never),
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const pending = handleRequestHelp(
|
||||
fakeManager("abcd", 99, 5),
|
||||
baseParams({ tab_id: 5, timeout_ms: 60_000 }),
|
||||
deps,
|
||||
);
|
||||
await vi.waitFor(() =>
|
||||
expect(sendToTab).toHaveBeenCalledWith(
|
||||
5,
|
||||
expect.objectContaining({ type: "bsk-help-request" }),
|
||||
),
|
||||
);
|
||||
const { requestId } = sendToTab.mock.calls[0][1];
|
||||
chromeEvents.tabsOnCreated.emit({ id: 6, windowId: 99 } as chrome.tabs.Tab);
|
||||
await vi.advanceTimersByTimeAsync(200);
|
||||
expect(sendToTab).toHaveBeenCalledWith(
|
||||
6,
|
||||
expect.objectContaining({ type: "bsk-help-request", requestId }),
|
||||
);
|
||||
|
||||
if (finishPath === "content") {
|
||||
chromeEvents.runtimeOnMessage.emit(
|
||||
{ type: "bsk-help-finish", requestId, outcome: "continued" },
|
||||
{ tab: { id: 5 } as chrome.tabs.Tab } as chrome.runtime.MessageSender,
|
||||
vi.fn(),
|
||||
);
|
||||
} else if (finishPath === "abort") {
|
||||
abort.abort();
|
||||
} else {
|
||||
await vi.advanceTimersByTimeAsync(60_000);
|
||||
}
|
||||
|
||||
await vi.advanceTimersByTimeAsync(1_100);
|
||||
const expected = {
|
||||
content: { outcome: "continued" },
|
||||
timeout: { outcome: "timed_out" },
|
||||
abort: { code: "cancelled" },
|
||||
}[finishPath];
|
||||
await expect(pending).resolves.toMatchObject(expected);
|
||||
expect(notifications.clear).toHaveBeenCalledWith(`bsk-help:${requestId}`);
|
||||
for (const tabId of [5, 6]) {
|
||||
expect(sendToTab).toHaveBeenCalledWith(tabId, { type: "bsk-help-cancel", requestId });
|
||||
}
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
it("returns completed when explicit completion criteria match", async () => {
|
||||
const deps = baseDeps({
|
||||
sendToTab: vi.fn(async () => ({ type: "bsk-help-ack", ok: true })),
|
||||
|
||||
@@ -289,15 +289,14 @@ async function refreshHelpTargets(
|
||||
}
|
||||
|
||||
async function cleanupHelp(help: ActiveHelpRequest): Promise<void> {
|
||||
if (help.deps.notifications) {
|
||||
await help.deps.notifications.clear(help.notificationId).catch(() => {});
|
||||
}
|
||||
const tabsToCancel = new Set([help.primaryTabId, ...help.overlayTabIds]);
|
||||
await Promise.all(
|
||||
[...tabsToCancel].map((tabId) =>
|
||||
// A stalled notification must not prevent cancellation of the page overlays.
|
||||
await Promise.all([
|
||||
...[...tabsToCancel].map((tabId) =>
|
||||
help.deps.sendToTab(tabId, { type: HELP_CANCEL, requestId: help.requestId }).catch(() => {}),
|
||||
),
|
||||
);
|
||||
help.deps.notifications?.clear(help.notificationId).catch(() => {}),
|
||||
]);
|
||||
}
|
||||
|
||||
/** Never let best-effort UI cleanup hold the daemon RPC open indefinitely. */
|
||||
|
||||
Reference in New Issue
Block a user