返回 DeepSeek-Reasonix
dock-file-navigation.test.tsx
根目录 / desktop / frontend / src / __tests__ / dock-file-navigation.test.tsx
1 import assert from "node:assert/strict";
2 import React, { act } from "react";
3 import { renderFilesWorkspace, waitFor } from "./workspace-panel-test-harness";
4 import { WorkspaceDockRegion, type WorkspaceDockRegionProps } from "../app-shell/WorkspaceDockRegion";
5 import { LocaleProvider } from "../lib/i18n";
6 import { performResourceAction } from "../lib/presentedFileNavigation";
7 import { fileNavigationOwner } from "../lib/fileNavigationCommands";
8 import { fileNavigationKey } from "../lib/fileNavigationOwner";
9 import { useActivityBarStore } from "../store/activityBar";
10 import { useRemoteStore } from "../store/remote";
11
12 const reads: string[] = [];
13 const preview = (path: string, mode: string) => {
14 reads.push(`${mode}:${path}`);
15 return { path, body: `${mode} content ${path}`, size: 20, truncated: false, binary: false };
16 };
17 const { dom, root } = await renderFilesWorkspace({
18 ReadPresentedFileForTab: async (_tab, _tool, path) => preview(path, "preview"),
19 ReadPresentedFileSourceForTab: async (_tab, _tool, path) => preview(path, "source"),
20 ListRemoteDir: async () => [],
21 ReadRemoteFile: async (_host, path) => ({ ...preview(path, "remote"), mtimeUnix: 1 }),
22 ResolveRemoteWorkspacePathForTab: async (_tab, _host, _tool, path) => {
23 if (path === "denied.txt") throw new Error("permission denied");
24 return path;
25 },
26 ResolveRemotePresentedPathForTab: async (_tab, _host, _tool, path) => path,
27 });
28 const props: WorkspaceDockRegionProps = {
29 visible: false, overlay: false, mode: "files", showContext: false,
30 t: key => key, onPickEntry: () => {}, remote: { onClose: () => {} }, context: {} as WorkspaceDockRegionProps["context"],
31 workspace: { open: true, tabId: "navigation-session", cwd: "/repo", maximized: false, onClose: () => {}, onToggleMaximized: () => {} },
32 workspaceKey: "navigation-test", workspaceRoot: "/repo",
33 };
34 const paint = (visible: boolean) => act(async () => root.render(<LocaleProvider><WorkspaceDockRegion {...props} visible={visible} /></LocaleProvider>));
35 // The committed record of whichever dock the command targeted.
36 const committed = (dockTabId: string | null | undefined) => dockTabId
37 ? fileNavigationOwner().getSnapshot(fileNavigationKey({ sessionTabId: "navigation-session", dockTabId }))
38 : null;
39 await act(async () => { useActivityBarStore.setState({ workspaceRoot: "/repo", tabs: [], activeTabId: null }); });
40 await paint(false);
41 for (const hostId of ["local", "remote-test"]) {
42 await act(async () => useRemoteStore.setState({ statuses: { "remote-test": { state: "connected" } } }));
43 for (const action of ["preview", "source", "reveal-tree"] as const) {
44 const path = `${hostId}-${action}.txt`;
45 await act(async () => performResourceAction({ hostId, tabId: "navigation-session", path, source: "presented", toolCallId: "call" }, action));
46 await paint(true);
47 await waitFor(path, () => document.body.textContent?.includes(`content ${path}`) === true);
48 const record = committed(useActivityBarStore.getState().activeTabId);
49 assert.equal(record?.selected?.resource.path, path, "the targeted dock committed the opened resource");
50 assert.equal(record?.selected?.resource.access.source, "presented");
51 assert.equal(record?.selected?.resource.access.toolCallId, "call");
52 await paint(true);
53 assert(document.body.textContent?.includes(`content ${path}`));
54 console.log("PASS real dock", hostId, action);
55 }
56 if (hostId === "local") {
57 const original = useActivityBarStore.getState().activeTabId!;
58 await act(async () => useActivityBarStore.getState().addTab("file", "Other files"));
59 await act(async () => useActivityBarStore.getState().activateTab(original));
60 await waitFor("restored presented resource", () => document.body.textContent?.includes("content local-reveal-tree.txt") === true);
61 console.log("PASS presented resource identity survives view remount without replay");
62 await act(async () => performResourceAction({ hostId, tabId: "navigation-session", path: "src/alias.ts", source: "workspace" }, "preview"));
63 await act(async () => performResourceAction({ hostId, tabId: "navigation-session", path: "/repo/src/alias.ts", source: "workspace" }, "preview"));
64 const aliasRecord = committed(useActivityBarStore.getState().activeTabId)!;
65 assert.equal(aliasRecord.entries.filter((entry) => entry.resource.identityPath === "/repo/src/alias.ts").length, 1,
66 "canonical identity deduplicates relative and absolute spellings through the real command path");
67 }
68 }
69 // A remote workspace path the host refuses reports its failure to the row that
70 // asked, and commits nothing the dock could render.
71 await paint(false);
72 const denied = await performResourceAction({ hostId: "remote-test", tabId: "navigation-session", path: "denied.txt", source: "workspace", toolCallId: "call" }, "preview");
73 assert.equal(denied.status, "failed");
74 assert.match((denied as { error: Error }).error.message, /permission denied/);
75 assert(!committed(useActivityBarStore.getState().activeTabId)?.entries.some(entry => entry.resource.path === "denied.txt"),
76 "a refused path never reaches the dock's record");
77
78 await act(async () => root.unmount());
79 dom.window.close();
80 assert(reads.length >= 6);
81
81 lines Plain Text