| 1 | import React, { act, useLayoutEffect } from "react"; |
| 2 | import { createRoot } from "react-dom/client"; |
| 3 | import { JSDOM } from "jsdom"; |
| 4 | import assert from "node:assert/strict"; |
| 5 | import { useNavigationSurface } from "../lib/useNavigationSurface"; |
| 6 | import { projectNavigationSurfaceTarget } from "../app-runtime/conversationProjection"; |
| 7 | import { initialState } from "../lib/useController"; |
| 8 | import type { RemoteSessionApi } from "../lib/useRemoteSession"; |
| 9 | |
| 10 | const dom = new JSDOM("<div id='root'></div>"); |
| 11 | Object.assign(globalThis, { window: dom.window, document: dom.window.document, IS_REACT_ACT_ENVIRONMENT: true }); |
| 12 | const root = createRoot(document.getElementById("root")!); |
| 13 | let surface!: ReturnType<typeof useNavigationSurface>; |
| 14 | function Probe({ tab, session, remote }: { tab: string; session: string; |
| 15 | remote?: Pick<RemoteSessionApi, "state" | "hydrated" | "surfaceGeneration" | "error"> }) { |
| 16 | const next = useNavigationSurface(projectNavigationSurfaceTarget({ activeTabId: tab, sessionKey: session, |
| 17 | local: { ...initialState, meta: { ...initialState.meta, ready: !remote } as typeof initialState.meta, |
| 18 | backendActivationPending: Boolean(remote), hydrating: Boolean(remote) }, remote })); |
| 19 | useLayoutEffect(() => { surface = next; }); |
| 20 | return null; |
| 21 | } |
| 22 | |
| 23 | try { |
| 24 | await act(async () => root.render(<Probe tab="a" session="a:1" />)); |
| 25 | act(() => { surface.begin(1); }); |
| 26 | act(() => { surface.maskTarget(1); }); |
| 27 | const firstToken = surface.surfaceCommitToken!; |
| 28 | assert.ok(firstToken); |
| 29 | await act(async () => root.render(<Probe tab="a" session="a:2" />)); |
| 30 | const secondToken = surface.surfaceCommitToken!; |
| 31 | assert.notEqual(secondToken, firstToken, "replacement within the same intent receives a distinct paint receipt"); |
| 32 | act(() => { assert.equal(surface.commitPaint(firstToken, "ready"), null); }); |
| 33 | let receipt: unknown; |
| 34 | act(() => { receipt = surface.commitPaint(secondToken, "ready"); }); |
| 35 | assert.deepEqual(receipt, { token: secondToken, intent: 1, targetTabId: "a", targetSessionKey: "a:2" }); |
| 36 | act(() => { assert.equal(surface.commitPaint(secondToken, "ready"), null, "a receipt commits once"); }); |
| 37 | const remote = { state: "ready", hydrated: true, surfaceGeneration: 1, error: "" } as const; |
| 38 | await act(async () => root.render(<Probe tab="remote" session="workspace" remote={remote} />)); |
| 39 | act(() => { surface.begin(2); surface.maskTarget(2); }); |
| 40 | const remoteToken = surface.surfaceCommitToken!; |
| 41 | assert.ok(remoteToken, "remote readiness is independent of the inactive local controller's pending hydration"); |
| 42 | await act(async () => root.render(<Probe tab="remote" session="workspace" remote={{ ...remote, surfaceGeneration: 2 }} />)); |
| 43 | act(() => { assert.equal(surface.commitPaint(remoteToken, "ready"), null, "a former Serve generation cannot acknowledge the new surface"); }); |
| 44 | act(() => { assert.ok(surface.commitPaint(surface.surfaceCommitToken!, "ready")); }); |
| 45 | assert.equal(surface.transitioning, false, "the same paint transaction releases remote Composer readiness"); |
| 46 | await act(async () => root.render(<Probe tab="remote" session="workspace" remote={{ ...remote, state: "error", hydrated: false, error: "offline" }} />)); |
| 47 | act(() => { surface.begin(3); surface.maskTarget(3); }); |
| 48 | assert.equal(surface.transitioning, false, "remote hydration failure terminates the masked navigation, leaving recovery reachable"); |
| 49 | const retained = surface.begin; |
| 50 | act(() => { root.unmount(); retained(2); }); |
| 51 | console.log("PASS navigation receipts are unique, source-bound, and consumed exactly once"); |
| 52 | } finally { |
| 53 | dom.window.close(); |
| 54 | } |
| 55 |