| 1 | import assert from "node:assert/strict"; |
| 2 | import { managementDom } from "../test-support/managementDom"; |
| 3 | import { installDesktopHostStub } from "./desktopHostStub"; |
| 4 | const dom = managementDom(); |
| 5 | const { default: React, act } = await import("react"); |
| 6 | const { createRoot } = await import("react-dom/client"); |
| 7 | const { LocaleProvider } = await import("../lib/i18n"); |
| 8 | const { ToastProvider } = await import("../lib/toast"); |
| 9 | const { ArchivedSessionsList } = await import("../components/ArchivedSessionsList"); |
| 10 | const row = (id: string) => ({ id, ref: { hostId: "local", sessionId: id }, title: id, workspaceId: "hidden", workspaceTitle: "Hidden project", archivedAt: 0, health: "ready", canRestore: true, canPreview: true, canPurge: true }); |
| 11 | const calls: string[] = []; |
| 12 | let restored = false, late = false; |
| 13 | let resolveLate: ((value: unknown) => void) | undefined; |
| 14 | const host = installDesktopHostStub({ |
| 15 | ListTrashEntries: async (_query: string, cursor: string) => { |
| 16 | calls.push(cursor); |
| 17 | if (late) return new Promise(resolve => { resolveLate = resolve; }); |
| 18 | return cursor ? { items: restored ? [] : [row("archived")], generation: 1 } |
| 19 | : { items: [], generation: 1, nextCursor: "page-2" }; |
| 20 | }, |
| 21 | ReadSessionHistory: async () => ({ messages: [{ messageId: "preview", role: "user", content: "Read-only history" }] }), |
| 22 | ApplySessionLifecycle: async (request: {targets:{ref:{sessionId:string}}[]}) => { |
| 23 | const target = request.targets[0]; assert.equal(target.ref.sessionId, "archived"); restored = true; |
| 24 | return { committed: true, generation: 2, items: [{ target, ref: target.ref, committed: true, workspaceId: "hidden" }] }; |
| 25 | }, |
| 26 | }); |
| 27 | const opened: string[] = []; |
| 28 | const root = createRoot(document.getElementById("root")!); |
| 29 | const render = (active = true) => <LocaleProvider><ToastProvider><ArchivedSessionsList active={active} onOpenSession={async ref => { opened.push(ref.sessionId); }} /></ToastProvider></LocaleProvider>; |
| 30 | await act(async () => root.render(render())); |
| 31 | assert.equal(document.querySelectorAll(".archived-sessions__row").length, 1, "archives beyond the first page stay reachable in hidden projects"); |
| 32 | assert.ok(calls.includes("page-2")); |
| 33 | assert.ok(document.body.textContent?.includes("Hidden project")); |
| 34 | await act(async () => (document.querySelector(".archived-sessions__open") as HTMLButtonElement).click()); |
| 35 | assert.deepEqual(opened, [], "preview does not open a writable runtime"); |
| 36 | assert.ok(document.body.textContent?.includes("Read-only history")); |
| 37 | await act(async () => (document.querySelector('[aria-label="Restore session"]') as HTMLButtonElement).click()); |
| 38 | assert.equal(document.querySelectorAll(".archived-sessions__row").length, 0); |
| 39 | assert.ok(document.body.textContent?.includes("No archived sessions")); |
| 40 | late = true; |
| 41 | await act(async () => Array.from(document.querySelectorAll<HTMLButtonElement>("button")).find(node => node.textContent?.trim() === "Refresh")!.click()); |
| 42 | assert.ok(resolveLate, "refresh starts a pending read"); |
| 43 | await act(async () => root.render(render(false))); |
| 44 | await act(async () => resolveLate!({ items: [row("stale")], generation: 2 })); |
| 45 | assert.equal(document.querySelectorAll(".archived-sessions__row").length, 0, "hidden page rejects stale reads"); |
| 46 | await act(async () => root.unmount()); |
| 47 | host.uninstall(); |
| 48 | dom.window.close(); |
| 49 | console.log("PASS archived pagination, hidden workspace, navigation, restore and stale-load isolation"); |
| 50 |