fix(workspace): hide ephemeral quick runtimes

This commit is contained in:
2026-08-31 14:39:17 +02:00
parent 087c177e8e
commit a4ee3eb7a8
4 changed files with 256 additions and 2 deletions
+161
View File
@@ -0,0 +1,161 @@
import { act, renderHook, waitFor } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { RuntimeSnapshot, Workspace, WorkspaceSummary } from "./types";
const mocks = vi.hoisted(() => ({
invoke: vi.fn(),
listeners: {} as Record<string, (message: { payload: any }) => void>,
listen: vi.fn(),
}));
vi.mock("@tauri-apps/api/core", () => ({ invoke: mocks.invoke }));
vi.mock("@tauri-apps/api/event", () => ({ listen: mocks.listen }));
vi.mock("@tauri-apps/api/window", () => ({
getCurrentWindow: () => ({}),
}));
vi.mock("./persistence", () => ({
legacyPreferences: () => ({}),
loadPresentation: async () => ({
version: 1,
directoryOrder: [],
runtimeOrderByDirectory: {},
drafts: {},
scroll: {},
lastSeen: {},
}),
presentationState: vi.fn(),
removeLegacyPreferences: vi.fn(),
savePresentation: vi.fn().mockResolvedValue(undefined),
}));
import { useWorkspace } from "./useWorkspace";
const normalRuntime = {
runtimeId: "normal",
worktreePath: "/repo",
state: "idle",
label: "Normal",
attention: false,
queueCount: 0,
lastActivity: "now",
openedAt: "now",
};
const quickRuntime = { ...normalRuntime, runtimeId: "quick", label: "Quick" };
function workspace(runtimes: (typeof normalRuntime)[]): Workspace {
return {
bridgeInstanceId: "bridge",
latestSeq: 1,
directories: [
{
worktreePath: "/repo",
isHome: true,
openCount: runtimes.length,
workingCount: 0,
attentionCount: 0,
recoveringCount: 0,
errorCount: 0,
runtimes,
},
],
};
}
const summary: WorkspaceSummary = {
bridgeInstanceId: "bridge",
latestSeq: 1,
openCount: 1,
workingCount: 0,
attentionCount: 0,
recoveringCount: 0,
errorCount: 0,
directoryCount: 1,
resourceWarning: false,
};
function snapshot(runtime: typeof normalRuntime): RuntimeSnapshot {
return {
bridgeInstanceId: "bridge",
latestSeq: 1,
runtime,
extensions: [],
};
}
describe("useWorkspace Quick runtime isolation", () => {
beforeEach(() => {
vi.clearAllMocks();
for (const key of Object.keys(mocks.listeners)) delete mocks.listeners[key];
mocks.listen.mockImplementation((event, listener) => {
mocks.listeners[event] = listener;
return Promise.resolve(() => delete mocks.listeners[event]);
});
});
it("ignores unknown Quick events and exposes promoted runtime once", async () => {
let promoted = false;
mocks.invoke.mockImplementation(
(command: string, payload?: { runtimeId?: string }) => {
if (command === "get_workspace")
return Promise.resolve(
workspace(promoted ? [normalRuntime, quickRuntime] : [normalRuntime]),
);
if (command === "get_workspace_summary") return Promise.resolve(summary);
if (command === "get_session_runtime_snapshot")
return Promise.resolve(
snapshot(payload?.runtimeId === "quick" ? quickRuntime : normalRuntime),
);
if (command === "get_pending_promoted_runtime")
return Promise.resolve(null);
return Promise.resolve(undefined);
},
);
const { result } = renderHook(() => useWorkspace());
await waitFor(() =>
expect(result.current.state.runtimesById.normal).toBeDefined(),
);
await waitFor(() =>
expect(mocks.listeners["workspace-bridge"]).toBeDefined(),
);
const workspaceCalls = () =>
mocks.invoke.mock.calls.filter(([command]) => command === "get_workspace");
await act(async () => {
mocks.listeners["workspace-bridge"]({
payload: {
kind: "event",
event: {
bridgeInstanceId: "bridge",
seq: 2,
type: "runtime_event",
data: {
runtimeId: "quick",
runtime: quickRuntime,
eventType: "transcript",
eventData: { event: { type: "message_end" } },
},
},
},
});
});
expect(result.current.state.runtimesById.quick).toBeUndefined();
expect(workspaceCalls()).toHaveLength(1);
promoted = true;
await act(async () => {
mocks.listeners["workspace-promoted-runtime"]({
payload: { runtimeId: "quick" },
});
});
await waitFor(() =>
expect(result.current.state.runtimesById.quick).toBeDefined(),
);
expect(result.current.state.runtimeOrderByDirectory["/repo"]).toEqual([
"normal",
"quick",
]);
expect(result.current.state.selectedRuntimeId).toBe("quick");
expect(workspaceCalls()).toHaveLength(2);
});
});