| 1 | import React, { act } from "react"; |
| 2 | import { createRoot } from "react-dom/client"; |
| 3 | import { LocaleProvider } from "../../lib/i18n"; |
| 4 | import { useRemoteSession, type RemoteSessionApi } from "../../lib/useRemoteSession"; |
| 5 | |
| 6 | /** Cases that assert what a remote snapshot projects into the transcript. */ |
| 7 | interface RemoteProjectionHarness { |
| 8 | ok: (value: boolean, label: string) => void; |
| 9 | flush: () => Promise<void>; |
| 10 | } |
| 11 | |
| 12 | /** History from a serve keeps the tool args and output its transcript shows. */ |
| 13 | export async function runRemoteToolHistoryCase({ ok, flush }: RemoteProjectionHarness) { |
| 14 | let toolProbe: RemoteSessionApi | undefined; |
| 15 | function ToolProbe() { |
| 16 | toolProbe = useRemoteSession("tab-tool-history"); |
| 17 | return null; |
| 18 | } |
| 19 | const toolRoot = createRoot(document.createElement("div")); |
| 20 | await act(async () => { |
| 21 | toolRoot.render(<LocaleProvider><ToolProbe /></LocaleProvider>); |
| 22 | await flush(); |
| 23 | }); |
| 24 | const remoteTool = toolProbe?.transcript.items.find((item) => item.kind === "tool" && item.id === "remote-tool"); |
| 25 | ok(remoteTool?.kind === "tool" && remoteTool.args.includes("go test ./...") |
| 26 | && remoteTool.output === "remote tool output" && remoteTool.dataArchived !== true, |
| 27 | "remote history retains expandable tool args and output without a local rehydrate endpoint"); |
| 28 | await act(async () => toolRoot.unmount()); |
| 29 | } |
| 30 | |
| 31 | // Pending prompt frames retained by Desktop are replayed by the next snapshot, |
| 32 | // which restores decisions missed while the tab had no frontend listener. |
| 33 | export async function runRemotePendingPromptReplayCase({ ok, flush }: RemoteProjectionHarness) { |
| 34 | let replayProbe: RemoteSessionApi | undefined; |
| 35 | function ReplayProbe() { replayProbe = useRemoteSession("tab-replay"); return null; } |
| 36 | const replayRoot = createRoot(document.createElement("div")); |
| 37 | await act(async () => { |
| 38 | replayRoot.render(<LocaleProvider><ReplayProbe /></LocaleProvider>); |
| 39 | await flush(); |
| 40 | }); |
| 41 | ok(replayProbe?.transcript.approval?.id === "replayed-approval", "snapshot replays a prompt emitted while the remote tab was inactive"); |
| 42 | ok(replayProbe?.transcript.extensionForm?.pluginId === "replayed-plugin" && replayProbe.transcript.extensionForm.surfaceId === "replayed-form", "snapshot replays an extension form emitted while the remote tab was inactive"); |
| 43 | await act(async () => { replayProbe?.drainApprovals(["different-approval"]); await flush(); }); |
| 44 | ok(replayProbe?.transcript.approval?.id === "replayed-approval", "a remote mode transaction preserves approvals it did not drain"); |
| 45 | await act(async () => { replayProbe?.drainApprovals(["replayed-approval"]); await flush(); }); |
| 46 | ok(replayProbe?.transcript.approval === undefined, "a remote mode transaction clears the exact approval it auto-allowed"); |
| 47 | await act(async () => replayRoot.unmount()); |
| 48 | } |
| 49 |