返回 DeepSeek-Reasonix
notification-volume-slider.test.tsx
根目录 / desktop / frontend / src / __tests__ / notification-volume-slider.test.tsx
1 // Run: tsx src/__tests__/notification-volume-slider.test.tsx
2
3 import React, { act } from "react";
4 import { JSDOM } from "jsdom";
5
6 let passed = 0;
7 let failed = 0;
8 function ok(value: boolean, label: string) {
9 if (value) {
10 process.stdout.write(` PASS ${label}\n`);
11 passed += 1;
12 } else {
13 process.stdout.write(` FAIL ${label}\n`);
14 failed += 1;
15 }
16 }
17
18 console.log("\nnotification volume slider");
19 const dom = new JSDOM('<!doctype html><html><body><div id="root"></div></body></html>', {
20 pretendToBeVisual: true,
21 url: "http://localhost/",
22 });
23 (globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
24 globalThis.window = dom.window as unknown as Window & typeof globalThis;
25 globalThis.document = dom.window.document;
26 Object.defineProperty(globalThis, "navigator", { configurable: true, value: dom.window.navigator });
27 globalThis.HTMLElement = dom.window.HTMLElement;
28 globalThis.Event = dom.window.Event;
29
30 const [{ createRoot }, { NotificationVolumeSlider }, { LocaleProvider }] = await Promise.all([
31 import("react-dom/client"),
32 import("../components/NotificationVolumeSlider"),
33 import("../lib/i18n"),
34 ]);
35
36 const rootElement = document.getElementById("root");
37 if (!rootElement) throw new Error("missing root");
38 const root = createRoot(rootElement);
39 const changes: number[] = [];
40 await act(async () => {
41 root.render(
42 <LocaleProvider>
43 <NotificationVolumeSlider value={70} onChange={(value) => changes.push(value)} />
44 </LocaleProvider>,
45 );
46 });
47
48 const input = document.querySelector<HTMLInputElement>('input[type="range"]');
49 const output = document.querySelector<HTMLOutputElement>("output");
50 ok(input?.getAttribute("aria-label") === "Notification volume", "slider exposes its localized purpose");
51 ok(input?.min === "0" && input?.max === "100" && input?.step === "5", "slider exposes the full 0–100% keyboard range");
52 ok(input?.value === "70" && input?.getAttribute("aria-valuetext") === "70%", "slider announces the default percentage");
53 ok(output?.textContent === "70%" && output?.getAttribute("for") === input?.id, "visible percentage is associated with the slider");
54 await act(async () => {
55 if (!input) return;
56 const setter = Object.getOwnPropertyDescriptor(dom.window.HTMLInputElement.prototype, "value")?.set;
57 setter?.call(input, "85");
58 input.dispatchEvent(new dom.window.Event("input", { bubbles: true }));
59 input.dispatchEvent(new dom.window.Event("change", { bubbles: true }));
60 });
61 ok(changes.at(-1) === 85, "dragging or using the keyboard emits the next percentage");
62
63 await act(async () => root.unmount());
64 dom.window.close();
65 process.stdout.write(`\n${passed} passed, ${failed} failed\n`);
66 if (failed > 0) process.exit(1);
67
67 lines Plain Text