| 1 | import assert from "node:assert/strict"; |
| 2 | import { managementDom } from "../test-support/managementDom"; |
| 3 | import { installDesktopHostStub } from "./desktopHostStub"; |
| 4 | |
| 5 | const dom = managementDom(); |
| 6 | const { default: React, act } = await import("react"); |
| 7 | const { createRoot } = await import("react-dom/client"); |
| 8 | const { LocaleProvider } = await import("../lib/i18n"); |
| 9 | const { ArchivedSessionsList } = await import("../components/ArchivedSessionsList"); |
| 10 | const row = (id: string) => ({ id, ref: { hostId: "local", sessionId: id }, title: id, workspaceId: "global", workspaceTitle: "Global", archivedAt: 0, health: "ready", canPreview: true, canRestore: true, canPurge: true }); |
| 11 | let rows = [row("alpha"), row("beta")]; |
| 12 | let generation = 1; |
| 13 | const requests: Array<{ action: string; targets: Array<{ ref: { sessionId: string } }> }> = []; |
| 14 | const host = installDesktopHostStub({ |
| 15 | ListTrashEntries: async () => ({ items: rows, generation }), |
| 16 | ApplySessionLifecycle: async (request: { action: string; targets: Array<{ ref: { sessionId: string } }> }) => { |
| 17 | requests.push(request); |
| 18 | rows = rows.filter(item => !request.targets.some(target => target.ref.sessionId === item.id)); |
| 19 | generation++; |
| 20 | return { committed: true, generation, items: request.targets.map(target => ({ target, committed: true, retryable: false })) }; |
| 21 | }, |
| 22 | }); |
| 23 | const root = createRoot(document.getElementById("root")!); |
| 24 | const button = (name: string) => Array.from(document.querySelectorAll<HTMLButtonElement>("button")).find(node => node.textContent?.trim() === name)!; |
| 25 | const openClear = async () => { |
| 26 | await act(async () => (document.querySelector(".archived-sessions__management") as HTMLButtonElement).click()); |
| 27 | await act(async () => (document.querySelector('[role="menuitem"]:last-child') as HTMLButtonElement).click()); |
| 28 | }; |
| 29 | await act(async () => root.render(<LocaleProvider><ArchivedSessionsList active onOpenSession={async () => {}} /></LocaleProvider>)); |
| 30 | |
| 31 | // Filtering changes what is visible, never the frozen bulk target set. |
| 32 | const search = document.querySelector<HTMLInputElement>(".archived-sessions__search input")!; |
| 33 | await act(async () => { |
| 34 | Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")!.set!.call(search, "alpha"); |
| 35 | search.dispatchEvent(new Event("input", { bubbles: true })); |
| 36 | }); |
| 37 | assert.equal(document.querySelectorAll(".archived-sessions__row").length, 1); |
| 38 | await openClear(); |
| 39 | assert.ok(document.querySelector('[role="dialog"]')?.textContent?.includes("all 2 archived conversations")); |
| 40 | await act(async () => button("Cancel").click()); |
| 41 | assert.equal(requests.length, 0); |
| 42 | await openClear(); |
| 43 | await act(async () => button("Delete all 2 conversations").click()); |
| 44 | assert.deepEqual(requests[0].targets.map(target => target.ref.sessionId), ["alpha", "beta"]); |
| 45 | |
| 46 | // The clear-all route stays a bulk confirmation even with only one archive. |
| 47 | rows = [row("solo")]; |
| 48 | await act(async () => host.emit("project-tree:changed")); |
| 49 | await openClear(); |
| 50 | assert.ok(document.querySelector('[role="dialog"]')?.textContent?.includes("all 1 archived conversations")); |
| 51 | assert.equal(button("Delete all 1 conversations")?.textContent, "Delete all 1 conversations"); |
| 52 | await act(async () => button("Cancel").click()); |
| 53 | |
| 54 | await act(async () => root.unmount()); |
| 55 | host.uninstall(); |
| 56 | dom.window.close(); |
| 57 | console.log("PASS filtered bulk scope, cancellation, captured targets and single-entry clear-all wording"); |
| 58 |