返回 DeepSeek-Reasonix
config-load-warnings.test.ts
根目录 / desktop / frontend / src / __tests__ / config-load-warnings.test.ts
1 // Run: tsx src/__tests__/config-load-warnings.test.ts
2
3 import { JSDOM } from "jsdom";
4 import React, { act } from "react";
5 import { createRoot } from "react-dom/client";
6 import type { AppBindings } from "../lib/bridge";
7 import {
8 configLoadWarningsKey,
9 normalizeConfigLoadWarnings,
10 normalizeConfigLoadWarningsEvent,
11 normalizeConfigLoadWarningsRevision,
12 subscribeConfigLoadWarnings,
13 useConfigLoadWarnings,
14 } from "../lib/useConfigLoadWarnings";
15 import { installDesktopHostStub } from "./desktopHostStub";
16
17 let passed = 0;
18 let failed = 0;
19
20 function equal(actual: unknown, expected: unknown, label: string) {
21 if (JSON.stringify(actual) === JSON.stringify(expected)) {
22 process.stdout.write(` PASS ${label}\n`);
23 passed += 1;
24 } else {
25 process.stdout.write(` FAIL ${label}: expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}\n`);
26 failed += 1;
27 }
28 }
29
30 console.log("\nconfig load warnings");
31
32 equal(
33 normalizeConfigLoadWarnings([" warning one ", "", 42, "warning one", "warning two"]),
34 ["warning one", "warning two"],
35 "bridge payload normalization keeps unique non-empty strings",
36 );
37 equal(configLoadWarningsKey(["a", "b"]), '["a","b"]', "warning fingerprints are stable");
38 equal(configLoadWarningsKey([]), "", "empty warning lists have no fingerprint");
39 equal(normalizeConfigLoadWarningsRevision(7), 7, "safe event revisions are preserved");
40 equal(normalizeConfigLoadWarningsRevision(Number.MAX_SAFE_INTEGER + 1), 0, "unsafe event revisions fall back safely");
41 equal(
42 normalizeConfigLoadWarningsEvent({ warnings: [" current warning ", null, "current warning"], revision: 7 }),
43 { warnings: ["current warning"], revision: 7 },
44 "versioned bridge event payloads are normalized",
45 );
46 equal(
47 normalizeConfigLoadWarningsEvent(["versioned warning"], 8),
48 { warnings: ["versioned warning"], revision: 8 },
49 "array event payloads accept an additive revision argument",
50 );
51
52 const dom = new JSDOM('<!doctype html><html><body><div id="root"></div></body></html>', { url: "http://localhost/" });
53 (globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
54 globalThis.window = dom.window as unknown as Window & typeof globalThis;
55 globalThis.document = dom.window.document;
56 const desktopStub = installDesktopHostStub(({ main: { App: {} as AppBindings } }).main.App);
57
58 function emitWarnings(payload: unknown, revision: number) {
59 desktopStub.emit("config:load-warnings", payload, revision);
60 }
61
62 const received: Array<{ warnings: string[]; revision: number }> = [];
63 const stop = subscribeConfigLoadWarnings((snapshot) => received.push(snapshot));
64 desktopStub.emit("config:load-warnings", [" current warning ", null, "current warning"], 7);
65 desktopStub.emit("config:load-warnings", [], 8);
66 equal(received, [{ warnings: ["current warning"], revision: 7 }], "runtime bridge forwards normalized non-empty warnings");
67 stop();
68 equal(desktopStub.events.get("config:load-warnings")?.size ?? 0, 0, "runtime warning subscription is disposable");
69
70 type WarningHook = ReturnType<typeof useConfigLoadWarnings>;
71 let warningHook: WarningHook | undefined;
72 function Probe() {
73 warningHook = useConfigLoadWarnings();
74 return null;
75 }
76
77 const rootElement = document.getElementById("root");
78 if (!rootElement) throw new Error("missing test root");
79 const root = createRoot(rootElement);
80 await act(async () => { root.render(React.createElement(Probe)); });
81
82 await act(async () => { emitWarnings(["runtime warning"], 2); });
83 await act(async () => { warningHook?.applySnapshot([], 1); });
84 equal(warningHook?.configLoadWarnings, ["runtime warning"], "stale startup snapshots cannot clear runtime warnings");
85
86 await act(async () => { warningHook?.dismiss(); });
87 await act(async () => { emitWarnings(["runtime warning"], 3); });
88 equal(warningHook?.configLoadWarnings, [], "dismissed warnings stay hidden across repeated session builds");
89
90 await act(async () => { warningHook?.reload([], 4); });
91 await act(async () => { emitWarnings(["runtime warning"], 3); });
92 equal(warningHook?.configLoadWarnings, [], "events started before a successful reload cannot revive stale warnings");
93
94 await act(async () => { emitWarnings(["runtime warning"], 5); });
95 equal(warningHook?.configLoadWarnings, ["runtime warning"], "a newer recurrence can reuse the same warning fingerprint");
96 await act(async () => { root.unmount(); });
97
98 if (failed > 0) {
99 process.stdout.write(`\n${failed} failed, ${passed} passed\n`);
100 process.exit(1);
101 }
102 process.stdout.write(`\n${passed} passed\n`);
103
103 lines TYPESCRIPT