返回 DeepSeek-Reasonix
confirm-dialog.test.tsx
根目录 / desktop / frontend / src / __tests__ / confirm-dialog.test.tsx
1 // Run: tsx src/__tests__/confirm-dialog.test.tsx
2
3 import { JSDOM } from "jsdom";
4 import React, { useState } from "react";
5 import { act } from "react";
6 import { createRoot } from "react-dom/client";
7 import { useConfirmDialog } from "../components/ConfirmDialog";
8
9 let passed = 0;
10 let failed = 0;
11
12 function ok(value: boolean, label: string) {
13 if (value) {
14 process.stdout.write(` PASS ${label}\n`);
15 passed += 1;
16 } else {
17 process.stdout.write(` FAIL ${label}\n`);
18 failed += 1;
19 }
20 }
21
22 function Harness() {
23 const { confirm, dialog } = useConfirmDialog();
24 const [result, setResult] = useState("pending");
25 const open = async () => {
26 const confirmed = await confirm({
27 title: "Delete theme",
28 message: "Delete Example? This cannot be undone.",
29 confirmLabel: "Delete",
30 cancelLabel: "Cancel",
31 tone: "danger",
32 });
33 setResult(String(confirmed));
34 };
35 return (
36 <>
37 <button id="open-confirm" type="button" onClick={() => void open()}>Open</button>
38 <output id="confirm-result">{result}</output>
39 {dialog}
40 </>
41 );
42 }
43
44 async function flush() {
45 await new Promise((resolve) => setTimeout(resolve, 20));
46 }
47
48 console.log("\nReasonix confirmation dialog");
49
50 const dom = new JSDOM("<!doctype html><html><body><div id=\"root\"></div></body></html>", {
51 pretendToBeVisual: true,
52 url: "http://localhost/",
53 });
54 (globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
55 globalThis.window = dom.window as unknown as Window & typeof globalThis;
56 globalThis.document = dom.window.document;
57 globalThis.Node = dom.window.Node;
58 globalThis.Element = dom.window.Element;
59 globalThis.HTMLElement = dom.window.HTMLElement;
60 globalThis.Event = dom.window.Event;
61 globalThis.KeyboardEvent = dom.window.KeyboardEvent;
62 globalThis.MouseEvent = dom.window.MouseEvent;
63 globalThis.requestAnimationFrame = dom.window.requestAnimationFrame.bind(dom.window);
64 globalThis.cancelAnimationFrame = dom.window.cancelAnimationFrame.bind(dom.window);
65
66 const rootElement = document.getElementById("root");
67 if (!rootElement) throw new Error("missing root");
68 const root = createRoot(rootElement);
69
70 await act(async () => {
71 root.render(<Harness />);
72 await flush();
73 });
74
75 const openButton = document.getElementById("open-confirm") as HTMLButtonElement;
76 openButton.focus();
77 await act(async () => {
78 openButton.click();
79 await flush();
80 });
81
82 const dialog = document.querySelector<HTMLElement>(".reasonix-confirm-dialog");
83 const cancelButton = Array.from(document.querySelectorAll<HTMLButtonElement>(".reasonix-confirm-dialog button"))
84 .find((button) => button.textContent === "Cancel");
85 const deleteButton = Array.from(document.querySelectorAll<HTMLButtonElement>(".reasonix-confirm-dialog button"))
86 .find((button) => button.textContent === "Delete");
87 ok(dialog?.getAttribute("role") === "dialog" && dialog.getAttribute("aria-modal") === "true", "renders an accessible modal dialog");
88 ok(dialog?.textContent?.includes("Delete Example? This cannot be undone.") === true, "renders the confirmation message");
89 ok(deleteButton?.classList.contains("btn--danger") === true, "uses the danger button recipe for deletion");
90 ok(document.activeElement === cancelButton, "focuses Cancel first for destructive confirmation");
91
92 await act(async () => {
93 document.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", bubbles: true }));
94 await flush();
95 });
96 ok(document.querySelector(".reasonix-confirm-dialog") === null, "Escape cancels and closes the dialog");
97 ok(document.getElementById("confirm-result")?.textContent === "false", "cancellation resolves false");
98 ok(document.activeElement === openButton, "restores focus to the triggering control");
99
100 await act(async () => {
101 openButton.click();
102 await flush();
103 });
104 const secondDeleteButton = Array.from(document.querySelectorAll<HTMLButtonElement>(".reasonix-confirm-dialog button"))
105 .find((button) => button.textContent === "Delete");
106 await act(async () => {
107 secondDeleteButton?.click();
108 await flush();
109 });
110 ok(document.getElementById("confirm-result")?.textContent === "true", "confirm action resolves true");
111
112 await act(async () => {
113 root.unmount();
114 });
115 dom.window.close();
116
117 console.log(`\n${passed} passed, ${failed} failed`);
118 if (failed > 0) process.exit(1);
119
119 lines Plain Text