返回 DeepSeek-Reasonix
command-palette-interactions.test.tsx
根目录 / desktop / frontend / src / __tests__ / command-palette-interactions.test.tsx
1 // Run: tsx src/__tests__/command-palette-interactions.test.tsx
2
3 import { JSDOM } from "jsdom";
4 import React from "react";
5 import { act } from "react";
6 import { createRoot } from "react-dom/client";
7 import { CommandPalette, type PaletteItem } from "../components/CommandPalette";
8 import { LocaleProvider } from "../lib/i18n";
9
10 let passed = 0;
11 let failed = 0;
12
13 function ok(value: boolean, label: string) {
14 if (value) {
15 process.stdout.write(` PASS ${label}\n`);
16 passed += 1;
17 } else {
18 process.stdout.write(` FAIL ${label}\n`);
19 failed += 1;
20 }
21 }
22
23 async function flush() {
24 await new Promise((resolve) => setTimeout(resolve, 0));
25 }
26
27 function installDom() {
28 const dom = new JSDOM("<!doctype html><html><body><div id=\"root\"></div></body></html>", {
29 pretendToBeVisual: true,
30 url: "http://localhost/",
31 });
32 (globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
33 globalThis.window = dom.window as unknown as Window & typeof globalThis;
34 globalThis.document = dom.window.document;
35 Object.defineProperty(globalThis, "navigator", { configurable: true, value: dom.window.navigator });
36 globalThis.Node = dom.window.Node;
37 globalThis.HTMLElement = dom.window.HTMLElement;
38 globalThis.HTMLButtonElement = dom.window.HTMLButtonElement;
39 globalThis.Event = dom.window.Event;
40 globalThis.KeyboardEvent = dom.window.KeyboardEvent;
41 globalThis.MouseEvent = dom.window.MouseEvent;
42 globalThis.requestAnimationFrame = dom.window.requestAnimationFrame.bind(dom.window);
43 globalThis.cancelAnimationFrame = dom.window.cancelAnimationFrame.bind(dom.window);
44 Object.defineProperty(dom.window.HTMLElement.prototype, "attachEvent", { configurable: true, value: () => {} });
45 Object.defineProperty(dom.window.HTMLElement.prototype, "detachEvent", { configurable: true, value: () => {} });
46 Object.defineProperty(window, "matchMedia", {
47 configurable: true,
48 value: () => ({
49 matches: true,
50 media: "(prefers-reduced-motion: reduce)",
51 onchange: null,
52 addEventListener() {},
53 removeEventListener() {},
54 addListener() {},
55 removeListener() {},
56 dispatchEvent: () => false,
57 }),
58 });
59 return dom;
60 }
61
62 async function renderPalette() {
63 const rootEl = document.getElementById("root");
64 if (!rootEl) throw new Error("missing root");
65 const root = createRoot(rootEl);
66 const calls = { close: 0, run: 0 };
67 const items: PaletteItem[] = [{
68 id: "cmd-new",
69 group: "Commands",
70 title: "New session",
71 run: () => {
72 calls.run += 1;
73 },
74 }];
75 await act(async () => {
76 root.render(
77 <LocaleProvider>
78 <CommandPalette
79 open
80 onClose={() => {
81 calls.close += 1;
82 }}
83 items={items}
84 placeholder="Search"
85 emptyText="No results"
86 />
87 </LocaleProvider>,
88 );
89 await flush();
90 });
91 return { root, calls };
92 }
93
94 console.log("\ncommand palette interactions");
95
96 {
97 const dom = installDom();
98 const { root, calls } = await renderPalette();
99 const esc = document.querySelector<HTMLButtonElement>(".palette__esc");
100 ok(esc instanceof HTMLButtonElement, "header esc keycap is a real close button");
101
102 await act(async () => {
103 esc?.click();
104 await flush();
105 });
106 ok(calls.close === 1, "clicking header esc closes the command palette");
107 ok(calls.run === 0, "clicking header esc does not run the highlighted command");
108
109 await act(async () => {
110 root.unmount();
111 });
112 dom.window.close();
113 }
114
115 {
116 const dom = installDom();
117 const { root, calls } = await renderPalette();
118 const esc = document.querySelector<HTMLButtonElement>(".palette__esc");
119 if (!esc) throw new Error("missing header esc close button");
120
121 await act(async () => {
122 esc.focus();
123 esc.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true }));
124 esc.click();
125 await flush();
126 });
127
128 ok(calls.close === 1, "Enter on focused header esc closes the command palette");
129 ok(calls.run === 0, "Enter on focused header esc does not run the highlighted command");
130
131 await act(async () => {
132 root.unmount();
133 });
134 dom.window.close();
135 }
136
137 console.log(`\n${passed} passed, ${failed} failed`);
138 if (failed > 0) process.exit(1);
139
139 lines Plain Text