返回 DeepSeek-Reasonix
sidebar-active-remote-identity.test.tsx
根目录 / desktop / frontend / src / __tests__ / sidebar-active-remote-identity.test.tsx
1 // Run: node --import ./scripts/svg-stub-register.mjs --import tsx src/__tests__/sidebar-active-remote-identity.test.tsx
2 // The project tree keys ancestor expansion, read marking and its row
3 // projection on the active remote reference, so a new object per render would
4 // re-run every tree walk on each transcript delta. The reference is owned by
5 // useActiveRemoteRef and the chrome assembly must pass it through untouched.
6 import assert from "node:assert/strict";
7 import React, { act } from "react";
8 import { createRoot } from "react-dom/client";
9 import { JSDOM } from "jsdom";
10 import { useActiveRemoteRef } from "../app-runtime/useActiveRemoteRef";
11 import { buildSidebarRegionProps } from "../app-shell/chromeRegionBuilders";
12 import type { RemoteTabRefView, TabMeta } from "../lib/types";
13
14 const dom = new JSDOM("<div id='root'></div>");
15 Object.assign(globalThis, { window: dom.window, document: dom.window.document, IS_REACT_ACT_ENVIRONMENT: true });
16
17 const tab = (overrides: Partial<TabMeta>): TabMeta => ({
18 id: "tab-remote", scope: "project", workspaceRoot: "~/app", workspaceName: "app", topicId: "", topicTitle: "app",
19 label: "box", ready: true, running: false, mode: "normal", active: true, cwd: "~/app", ...overrides,
20 } as TabMeta);
21
22 const seen: Array<RemoteTabRefView | undefined> = [];
23 function Probe({ meta }: { meta: TabMeta | undefined }) {
24 seen.push(useActiveRemoteRef(meta));
25 return null;
26 }
27 const root = createRoot(document.getElementById("root")!);
28 const show = (meta: TabMeta | undefined) => act(async () => { root.render(React.createElement(Probe, { meta })); });
29
30 try {
31 const remoteTab = tab({ remote: { hostId: "box", workspace: "~/app" }, sessionId: "session-a" });
32 await show(remoteTab);
33 const first = seen[seen.length - 1];
34 assert.deepEqual(first, { hostId: "box", workspace: "~/app", sessionId: "session-a" }, "the reference carries the host, workspace and bound session");
35
36 // A transcript delta re-renders the shell with the same tab meta.
37 await show(remoteTab);
38 assert.equal(seen[seen.length - 1], first, "re-rendering with the same tab meta keeps the reference identity");
39
40 // A tab-meta refresh (running flag, turn counters) replaces the meta and its
41 // nested remote object while the session binding is unchanged.
42 await show(tab({ remote: { hostId: "box", workspace: "~/app" }, sessionId: "session-a", running: true, turnId: "t1" }));
43 assert.equal(seen[seen.length - 1], first, "an unrelated tab-meta refresh does not churn the reference");
44
45 await show(tab({ remote: { hostId: "box", workspace: "~/app" }, sessionId: "session-b" }));
46 const rotated = seen[seen.length - 1];
47 assert.notEqual(rotated, first, "rotating to another session publishes a new reference");
48 assert.equal(rotated?.sessionId, "session-b");
49
50 await show(tab({ remote: { hostId: "other-box", workspace: "~/app" }, sessionId: "session-b" }));
51 assert.notEqual(seen[seen.length - 1], rotated, "moving to another host publishes a new reference");
52
53 await show(tab({}));
54 assert.equal(seen[seen.length - 1], undefined, "a local tab has no active remote reference");
55 await show(undefined);
56 assert.equal(seen[seen.length - 1], undefined, "a draft surface (no active tab) has no active remote reference");
57
58 // The chrome assembly is a pure pass-through: rebuilding the object there
59 // would defeat the memo regardless of how stable its input is.
60 type Input = Parameters<typeof buildSidebarRegionProps>[0];
61 const props = buildSidebarRegionProps({
62 className: "", t: ((key: string) => key) as Input["t"], paletteShortcut: "",
63 shell: { sidebarCollapsed: false } as Input["shell"],
64 geometry: { sidebarResizeMinWidth: 264, sidebarRenderWidth: 264 } as Input["geometry"],
65 topics: {} as Input["topics"], commands: {} as Input["commands"],
66 projectTree: {
67 activeTab: remoteTab, activeRemote: first,
68 imTopicSources: {}, refreshSignal: 0, searchExpanded: false, searchFocusSignal: 0,
69 showShortcutBadges: false, shortcutPlatform: undefined, onVisibleTopicsChange: () => {},
70 draftSummaries: [], onOpenDraft: undefined,
71 },
72 });
73 assert.equal(props.projectTree.activeRemote, first, "the sidebar assembly forwards the owned reference by identity");
74 console.log("sidebar active remote identity: stable across deltas and meta refreshes, rotates on host/session change, forwarded by identity");
75 } finally {
76 await act(async () => root.unmount());
77 }
78
78 lines Plain Text