返回 DeepSeek-Reasonix
file-navigation-session.test.tsx
根目录 / desktop / frontend / src / __tests__ / file-navigation-session.test.tsx
1 // Run: node --import ./scripts/svg-stub-register.mjs --import tsx src/__tests__/file-navigation-session.test.tsx
2 //
3 // What a session change does to a dock that keeps its identity: the previews
4 // stay where the user left them, their read credentials do not cross over, and
5 // the new session's first command is still delivered as a navigation.
6
7 import assert from "node:assert/strict";
8 import React, { act } from "react";
9 import { renderFilesWorkspace, waitFor } from "./workspace-panel-test-harness";
10 import { WorkspaceDockRegion, type WorkspaceDockRegionProps } from "../app-shell/WorkspaceDockRegion";
11 import { LocaleProvider } from "../lib/i18n";
12 import { performResourceAction, fileNavigationOwner } from "../lib/fileNavigationCommands";
13 import { fileNavigationKey } from "../lib/fileNavigationOwner";
14
15 const reads: string[] = [];
16 const { dom, root, dockTabId } = await renderFilesWorkspace({
17 ReadPresentedFileForTab: async (_tab, _tool, path) => {
18 reads.push(`presented:${path}`);
19 return { path, body: `CONTENT ${path}`, size: 20, truncated: false, binary: false };
20 },
21 ReadFileForTab: async (_tab, path) => {
22 reads.push(`workspace:${path}`);
23 return { path, body: `CONTENT ${path}`, size: 20, truncated: false, binary: false };
24 },
25 ListDirForTab: async (_tab, dir) => dir === "" ? [{ name: "app.ts", isDir: false }] : [],
26 });
27 const props: WorkspaceDockRegionProps = {
28 visible: true, overlay: false, mode: "files", showContext: false,
29 t: key => key, onPickEntry: () => {}, remote: { onClose: () => {} }, context: {} as WorkspaceDockRegionProps["context"],
30 workspace: { open: true, tabId: "session-tab", cwd: "/repo", maximized: false, onClose: () => {}, onToggleMaximized: () => {} },
31 workspaceKey: "session-test", workspaceRoot: "/repo",
32 };
33 // A topic switch inside one project: the tab and the project memory key stay,
34 // only the session scope the dock is bound to changes.
35 let sessionTabId = "session-tab";
36 let sessionScope = "scope-one";
37 const paint = () => act(async () => root.render(
38 <LocaleProvider><WorkspaceDockRegion {...props} workspace={{
39 ...props.workspace, workspaceScopeKey: sessionScope, workspaceMemoryKey: "project-memory",
40 }} visible /></LocaleProvider>,
41 ));
42 const record = () => fileNavigationOwner().getSnapshot(fileNavigationKey({ sessionTabId, dockTabId }));
43 const open = async (path: string) => {
44 await act(async () => performResourceAction({ hostId: "local", tabId: sessionTabId, path, source: "presented", toolCallId: "call" }, "preview"));
45 };
46 await paint();
47
48 // A presented file is open in the first session.
49 await open("notes.md");
50 await paint();
51 await waitFor("first preview", () => document.body.textContent?.includes("CONTENT notes.md") === true);
52 assert.equal(record()?.selected?.resource.access.source, "presented");
53 const previewBody = document.querySelector(".workspace-preview__body");
54 assert(previewBody, "the preview area is mounted before the session changes");
55
56 // Another session in the same project keeps the dock and what it shows, but the
57 // presented tool scope belongs to the session that captured it and must not
58 // authorize a read here.
59 sessionScope = "scope-two";
60 await paint();
61 const afterSwitch = record();
62 assert(afterSwitch, "the dock keeps its record across a session change");
63 assert.equal(afterSwitch.selected?.resource.path, "notes.md", "the selected file survives a session change");
64 assert.equal(afterSwitch.selected?.resource.access.source, "workspace", "the presented scope does not cross sessions");
65 assert.equal(afterSwitch.selected?.resource.access.toolCallId, undefined);
66 assert.equal(document.querySelector(".workspace-preview__body"), previewBody, "the preview element is the same DOM node");
67 assert.equal(afterSwitch.revision, record()!.revision, "the record is the one the dock already had");
68 await waitFor("rebound preview", () => reads.includes("workspace:notes.md"));
69 assert(document.body.textContent?.includes("CONTENT notes.md"), "the file is read again under the new session");
70
71 await act(async () => root.unmount());
72 dom.window.close();
73 console.log("PASS session change: previews survive, presented scope does not, first navigation still applies");
74
74 lines Plain Text