0949c6f0d7
Use native browser and Tauri behavior instead of custom selector, dialog, and window-state implementations. Remove obsolete bridge, relay, asset, and dependency paths to keep the supported surface small. BREAKING CHANGE: The Noctalia relay no longer accepts --agent, and the Tauri host no longer exposes the legacy load_agent command.
159 lines
4.2 KiB
TypeScript
159 lines
4.2 KiB
TypeScript
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("./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);
|
|
});
|
|
});
|