返回 DeepSeek-Reasonix
terminal-panel-commands.test.tsx
根目录 / desktop / frontend / src / __tests__ / terminal-panel-commands.test.tsx
1 import assert from "node:assert/strict";
2 import React, { act } from "react";
3 import { createRoot } from "react-dom/client";
4 import { JSDOM } from "jsdom";
5 import { useTerminalPanelCommands } from "../app-runtime/useTerminalPanelCommands";
6 import { useLayoutStore } from "../store/layout";
7 import { useAppNavigationStore } from "../store/appNavigation";
8 import { useTerminalStore } from "../store/terminal";
9 import { AppBottomRegions } from "../app-shell/AppBottomRegions";
10 import { TopicbarSessionActions } from "../components/TopicbarSessionActions";
11 import { LocaleProvider, useT } from "../lib/i18n";
12 import { ToastProvider } from "../lib/toast";
13
14 const dom = new JSDOM("<div id='root'></div>", { url: "http://localhost" });
15 Object.assign(globalThis, { window: dom.window, document: dom.window.document, localStorage: dom.window.localStorage,
16 KeyboardEvent: dom.window.KeyboardEvent, IS_REACT_ACT_ENVIRONMENT: true });
17 const root = createRoot(document.getElementById("root")!);
18 const originalCreate = useTerminalStore.getState().createSession;
19 const calls: string[] = [];
20 useTerminalStore.setState({ createSession: async (tab, path) => { calls.push(`${tab}:${path}`); return null; } });
21 let commands!: ReturnType<typeof useTerminalPanelCommands>;
22 const noop = () => {};
23 function Probe({ remote }: { remote: boolean }) {
24 const page = useAppNavigationStore(state => state.page);
25 commands = useTerminalPanelCommands({ tabId: "A", enabled: !remote, shortcutsEnabled: page.kind === "workspace" });
26 const t = useT();
27 return <>
28 <TopicbarSessionActions sessionHasContent={false} getSessionMarkdown={() => ""} exportSession={noop}
29 toggleTerminal={commands.toggleTerminalPanel} terminalEnabled={!remote} terminalOpen={false} openSessionSummary={noop} tasksOpen={false} />
30 {remote && <AppBottomRegions terminal={{ open: false, contentVisible: true, remoteSurface: true, t,
31 panel: { tabId: "A", open: false, onClose: commands.closeTerminalPanel },
32 resizer: { min: 100, max: 400, value: 200, onPointerDown: noop, onKeyDown: noop, onReset: noop },
33 }} />}
34 </>;
35 }
36 const paint = (remote: boolean) => act(async () => root.render(<LocaleProvider><ToastProvider><Probe remote={remote} /></ToastProvider></LocaleProvider>));
37 const key = (shiftKey = false) => document.dispatchEvent(new KeyboardEvent("keydown", { key: "`", ctrlKey: true, shiftKey, bubbles: true }));
38 try {
39 useLayoutStore.getState().setTerminalPanelOpen(false);
40 await paint(true);
41 const terminalButton = document.querySelector(".lucide-terminal")?.closest("button")
42 ?? [...document.querySelectorAll("button")].find(button => button.getAttribute("aria-label") === "Terminal");
43 assert.ok(terminalButton);
44 assert.equal(terminalButton.disabled, true);
45 assert.equal(document.querySelector(".terminal-drawer")?.childElementCount, 0, "remote surface cannot mount a warm local TerminalPanel");
46 await act(async () => { key(); key(true); commands.openTerminalForPath("remote-path"); });
47 assert.equal(useLayoutStore.getState().terminalPanelOpen, false);
48 assert.deepEqual(calls, [], "remote shortcut and direct commands share one local-tool capability gate");
49 await paint(false);
50 await act(async () => useAppNavigationStore.getState().openPage({ kind: "automation" }));
51 await act(async () => key());
52 assert.equal(useAppNavigationStore.getState().page.kind, "automation");
53 assert.equal(useLayoutStore.getState().terminalPanelOpen, false, "management pages suppress workspace shortcuts without changing stored geometry");
54 await act(async () => useAppNavigationStore.getState().returnToWorkspace());
55 await act(async () => key());
56 assert.equal(useLayoutStore.getState().terminalPanelOpen, true);
57 await act(async () => key(true));
58 assert.deepEqual(calls, ["A:."]);
59 await act(async () => commands.closeTerminalPanel());
60 assert.equal(useLayoutStore.getState().terminalPanelOpen, false);
61 await act(async () => root.unmount());
62 commands.openTerminalForPath("stale"); key(true);
63 assert.deepEqual(calls, ["A:."], "unmount revokes commands and removes shortcut listeners");
64 console.log("terminal commands: remote capability, warm mount exclusion, native key routing and disposal passed");
65 } finally { useTerminalStore.setState({ createSession: originalCreate }); dom.window.close(); }
66
66 lines Plain Text