Adds a Service URLs setting (ask / in-Paseo / external browser) that controls where URLs from running scripts open on desktop. First click prompts with a "don't ask again" checkbox; in-Paseo opens a workspace browser tab. Closing a browser tab now clears its session partition.
170 lines
5.2 KiB
TypeScript
170 lines
5.2 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import {
|
|
buildBulkCloseConfirmationMessage,
|
|
classifyBulkClosableTabs,
|
|
closeBulkWorkspaceTabs,
|
|
} from "@/screens/workspace/workspace-bulk-close";
|
|
import type { WorkspaceTabDescriptor } from "@/screens/workspace/workspace-tabs-types";
|
|
|
|
function makeAgentTab(id: string): WorkspaceTabDescriptor {
|
|
return {
|
|
key: `agent_${id}`,
|
|
tabId: `agent_${id}`,
|
|
kind: "agent",
|
|
target: { kind: "agent", agentId: id },
|
|
};
|
|
}
|
|
|
|
function makeTerminalTab(id: string): WorkspaceTabDescriptor {
|
|
return {
|
|
key: `terminal_${id}`,
|
|
tabId: `terminal_${id}`,
|
|
kind: "terminal",
|
|
target: { kind: "terminal", terminalId: id },
|
|
};
|
|
}
|
|
|
|
function makeFileTab(path: string): WorkspaceTabDescriptor {
|
|
return {
|
|
key: `file_${path}`,
|
|
tabId: `file_${path}`,
|
|
kind: "file",
|
|
target: { kind: "file", path },
|
|
};
|
|
}
|
|
|
|
describe("workspace bulk close helpers", () => {
|
|
it("classifies agent, terminal, and passive tabs for shared bulk close handling", () => {
|
|
const groups = classifyBulkClosableTabs([
|
|
makeAgentTab("a1"),
|
|
makeTerminalTab("t1"),
|
|
makeFileTab("/repo/README.md"),
|
|
]);
|
|
|
|
expect(groups).toEqual({
|
|
agentTabs: [{ tabId: "agent_a1", agentId: "a1" }],
|
|
terminalTabs: [{ tabId: "terminal_t1", terminalId: "t1" }],
|
|
otherTabs: [
|
|
{
|
|
tabId: "file_/repo/README.md",
|
|
target: { kind: "file", path: "/repo/README.md" },
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
it("describes mixed destructive bulk close operations in the confirmation copy", () => {
|
|
const message = buildBulkCloseConfirmationMessage(
|
|
classifyBulkClosableTabs([
|
|
makeAgentTab("a1"),
|
|
makeAgentTab("a2"),
|
|
makeTerminalTab("t1"),
|
|
makeFileTab("/repo/README.md"),
|
|
]),
|
|
);
|
|
|
|
expect(message).toBe(
|
|
"This will archive 2 agent(s), close 1 terminal(s), and close 1 tab(s). Any running process in a closed terminal will be stopped immediately.",
|
|
);
|
|
});
|
|
|
|
it("keeps terminal-only confirmations explicit about stopping running processes", () => {
|
|
const message = buildBulkCloseConfirmationMessage(
|
|
classifyBulkClosableTabs([makeTerminalTab("t1")]),
|
|
);
|
|
|
|
expect(message).toBe(
|
|
"This will close 1 terminal(s). Any running process in a closed terminal will be stopped immediately.",
|
|
);
|
|
});
|
|
|
|
it("closes all tabs immediately and fires one mixed closeItems RPC in the background", async () => {
|
|
const groups = classifyBulkClosableTabs([
|
|
makeAgentTab("a1"),
|
|
makeTerminalTab("t1"),
|
|
makeTerminalTab("t2"),
|
|
makeFileTab("/repo/README.md"),
|
|
]);
|
|
const closedTabIds: string[] = [];
|
|
const cleanupCalls: Array<{ tabId: string; target?: WorkspaceTabDescriptor["target"] }> = [];
|
|
const closeItems = vi.fn(async () => ({
|
|
agents: [{ agentId: "a1", archivedAt: "2026-04-01T04:00:00.000Z" }],
|
|
terminals: [
|
|
{ terminalId: "t1", success: true },
|
|
{ terminalId: "t2", success: false },
|
|
],
|
|
requestId: "req-1",
|
|
}));
|
|
|
|
await closeBulkWorkspaceTabs({
|
|
groups,
|
|
client: { closeItems },
|
|
closeTab: async (tabId, action) => {
|
|
closedTabIds.push(tabId);
|
|
await action();
|
|
},
|
|
closeWorkspaceTabWithCleanup: (input) => {
|
|
cleanupCalls.push(input);
|
|
},
|
|
logLabel: "all tabs",
|
|
});
|
|
|
|
expect(closeItems).toHaveBeenCalledTimes(1);
|
|
expect(closeItems).toHaveBeenCalledWith({
|
|
agentIds: ["a1"],
|
|
terminalIds: ["t1", "t2"],
|
|
});
|
|
expect(closedTabIds).toEqual([
|
|
"agent_a1",
|
|
"terminal_t1",
|
|
"terminal_t2",
|
|
"file_/repo/README.md",
|
|
]);
|
|
expect(cleanupCalls).toEqual([
|
|
{ tabId: "agent_a1", target: { kind: "agent", agentId: "a1" } },
|
|
{ tabId: "terminal_t1", target: { kind: "terminal", terminalId: "t1" } },
|
|
{ tabId: "terminal_t2", target: { kind: "terminal", terminalId: "t2" } },
|
|
{ tabId: "file_/repo/README.md", target: { kind: "file", path: "/repo/README.md" } },
|
|
]);
|
|
});
|
|
|
|
it("still closes all tabs when the mixed closeItems RPC fails", async () => {
|
|
const groups = classifyBulkClosableTabs([
|
|
makeAgentTab("a1"),
|
|
makeTerminalTab("t1"),
|
|
makeFileTab("/repo/README.md"),
|
|
]);
|
|
const closedTabIds: string[] = [];
|
|
const cleanupCalls: Array<{ tabId: string; target?: WorkspaceTabDescriptor["target"] }> = [];
|
|
const warn = vi.fn();
|
|
|
|
await closeBulkWorkspaceTabs({
|
|
groups,
|
|
client: {
|
|
closeItems: async () => {
|
|
throw new Error("rpc failed");
|
|
},
|
|
},
|
|
closeTab: async (tabId, action) => {
|
|
closedTabIds.push(tabId);
|
|
await action();
|
|
},
|
|
closeWorkspaceTabWithCleanup: (input) => {
|
|
cleanupCalls.push(input);
|
|
},
|
|
warn,
|
|
logLabel: "others",
|
|
});
|
|
|
|
await Promise.resolve();
|
|
|
|
expect(warn).toHaveBeenCalledTimes(1);
|
|
expect(closedTabIds).toEqual(["agent_a1", "terminal_t1", "file_/repo/README.md"]);
|
|
expect(cleanupCalls).toEqual([
|
|
{ tabId: "agent_a1", target: { kind: "agent", agentId: "a1" } },
|
|
{ tabId: "terminal_t1", target: { kind: "terminal", terminalId: "t1" } },
|
|
{ tabId: "file_/repo/README.md", target: { kind: "file", path: "/repo/README.md" } },
|
|
]);
|
|
});
|
|
});
|