返回 DeepSeek-Reasonix
settings-page-navigation.test.tsx
根目录 / desktop / frontend / src / __tests__ / settings-page-navigation.test.tsx
1 import assert from "node:assert/strict";
2 import { JSDOM } from "jsdom";
3 import { baseSettings } from "../test-support/settingsTestFixtures";
4 import { installDesktopHostStub } from "./desktopHostStub";
5
6 const dom = new JSDOM('<!doctype html><div id="workspace"><button id="opener">Settings</button></div><div id="root"></div>', { url: "http://localhost", pretendToBeVisual: true });
7 Object.assign(globalThis, { window: dom.window, document: dom.window.document, HTMLElement: dom.window.HTMLElement, Node: dom.window.Node, Event: dom.window.Event, CustomEvent: dom.window.CustomEvent, localStorage: dom.window.localStorage, sessionStorage: dom.window.sessionStorage, IS_REACT_ACT_ENVIRONMENT: true });
8 Object.defineProperty(globalThis, "navigator", { configurable: true, value: dom.window.navigator });
9 globalThis.requestAnimationFrame = dom.window.requestAnimationFrame.bind(dom.window);
10 globalThis.cancelAnimationFrame = dom.window.cancelAnimationFrame.bind(dom.window);
11 window.matchMedia = () => ({ matches: true, addEventListener() {}, removeEventListener() {} }) as unknown as MediaQueryList;
12 window.scrollTo = () => {};
13 const settings = baseSettings("standard");
14 installDesktopHostStub(({ main: { App: { Settings: async () => settings, FetchAllProviderModels: async () => ({}) } } }).main.App);
15 const { default: React, act } = await import("react");
16 const { createRoot } = await import("react-dom/client");
17 const { useManagementWorkspace } = await import("../lib/useManagementWorkspace");
18 const { useAppNavigationStore } = await import("../store/appNavigation");
19 const { SettingsPanel } = await import("../components/SettingsPanel");
20 const { LocaleProvider } = await import("../lib/i18n");
21 const root = createRoot(document.getElementById("root")!);
22 const opener = document.getElementById("opener")!;
23 const workspaceRef = { current: document.getElementById("workspace") as HTMLDivElement };
24 opener.focus();
25 useAppNavigationStore.getState().openPage({ kind: "settings", tab: "general" });
26 function WorkspaceBoundary({ children }: { children: React.ReactNode }) { useManagementWorkspace(workspaceRef, true); return children; }
27 let closes = 0;
28 const routes: string[] = [];
29 function RoutedSettings() {
30 const route = useAppNavigationStore((state) => state.page);
31 const navigate = useAppNavigationStore((state) => state.setSettingsTarget);
32 return <SettingsPanel initialTab={route.kind === "settings" ? route.tab : undefined} desktopPlatform="linux" onClose={() => { closes++; }} onChanged={() => {}} onUseSubagent={() => {}} onNavigate={(tab) => { routes.push(tab); navigate(tab); }} />;
33 }
34 await act(async () => {
35 root.render(<LocaleProvider><WorkspaceBoundary><RoutedSettings /></WorkspaceBoundary></LocaleProvider>);
36 });
37 const page = document.querySelector<HTMLElement>(".settings-screen")!;
38 assert.ok(page);
39 assert.equal(workspaceRef.current.inert, true, "workspace cannot receive input while settings are open");
40 assert.equal(document.querySelector(".management-modal-backdrop"), null);
41 assert.equal(document.activeElement?.textContent?.trim(), "Back to workspace");
42 await act(async () => { document.dispatchEvent(new dom.window.KeyboardEvent("keydown", { key: "Escape", bubbles: true })); });
43 assert.equal(closes, 0, "Escape must not leave a settings page");
44 await act(async () => { page.dispatchEvent(new dom.window.MouseEvent("mousedown", { bubbles: true })); });
45 assert.equal(closes, 0, "clicking the page background must not exit");
46 const models = Array.from(page.querySelectorAll<HTMLButtonElement>("nav button")).find((button) => button.textContent?.trim() === "Model preferences")!;
47 assert.ok(models);
48 await act(async () => models.click());
49 assert.deepEqual(routes, ["models"]);
50 assert.ok(page.querySelector('[aria-current="page"]')?.textContent?.includes("Model preferences"));
51 for (const tab of ["model-stats", "providers", "models", "providers"] as const) {
52 const navButtons = Array.from(page.querySelectorAll<HTMLButtonElement>("nav button"));
53 const label = tab === "providers" ? "Model services" : tab === "models" ? "Model preferences" : "Usage stats";
54 const button = navButtons.find((item) => item.textContent?.trim() === label);
55 assert.ok(button, `${label} navigation exists`);
56 await act(async () => button.click());
57 assert.deepEqual(useAppNavigationStore.getState().page, { kind: "settings", tab });
58 assert.equal(useAppNavigationStore.getState().lastSettingsTarget, tab);
59 assert.equal(page.querySelector('[aria-current="page"]')?.textContent?.trim(), label);
60 assert.ok(page.querySelector(`.settings-page--${tab}`), `${label} remains rendered after route synchronization`);
61 }
62 const back = Array.from(page.querySelectorAll<HTMLButtonElement>("button")).find((button) => button.textContent?.trim() === "Back to workspace")!;
63 await act(async () => back.click());
64 assert.equal(closes, 1);
65 await act(async () => useAppNavigationStore.getState().returnToWorkspace());
66 await act(async () => root.unmount());
67 assert.equal(workspaceRef.current.inert, false);
68 assert.equal(document.activeElement, opener, "return restores the original entry's focus");
69 dom.window.close();
70 console.log("PASS full-page settings, navigation, explicit return, Escape and focus restoration");
71
71 lines Plain Text