返回 DeepSeek-Reasonix
theme-auto-background.test.ts
根目录 / desktop / frontend / src / __tests__ / theme-auto-background.test.ts
1 // Run: tsx src/__tests__/theme-auto-background.test.ts
2
3 import { readFileSync } from "node:fs";
4 import { dirname, resolve } from "node:path";
5 import { fileURLToPath } from "node:url";
6
7 const testDir = dirname(fileURLToPath(import.meta.url));
8 const themeSource = readFileSync(resolve(testDir, "../lib/theme.ts"), "utf8");
9 const stylesSource = readFileSync(resolve(testDir, "../styles.css"), "utf8");
10
11 let passed = 0;
12 let failed = 0;
13
14 function ok(value: boolean, label: string) {
15 if (value) {
16 process.stdout.write(` PASS ${label}\n`);
17 passed += 1;
18 } else {
19 process.stdout.write(` FAIL ${label}\n`);
20 failed += 1;
21 }
22 }
23
24 function blockAfter(source: string, needle: string, after = 0): string {
25 const selectorIndex = source.indexOf(needle, after);
26 if (selectorIndex < 0) return "";
27 const open = source.indexOf("{", selectorIndex);
28 if (open < 0) return "";
29
30 let depth = 0;
31 for (let index = open; index < source.length; index += 1) {
32 const char = source[index];
33 if (char === "{") depth += 1;
34 else if (char === "}") {
35 depth -= 1;
36 if (depth === 0) return source.slice(open + 1, index);
37 }
38 }
39 return "";
40 }
41
42 console.log("\ntheme auto native background contract");
43
44 ok(
45 themeSource.includes('const AUTO_THEME_MEDIA_QUERY = "(prefers-color-scheme: light)";'),
46 "auto theme uses one shared light color-scheme media query",
47 );
48 ok(
49 themeSource.includes("window.matchMedia?.(AUTO_THEME_MEDIA_QUERY).matches"),
50 "resolved auto theme reads the shared color-scheme query",
51 );
52 ok(
53 themeSource.includes("syncAutoThemeBackgroundListener(theme);"),
54 "applyTheme updates the native auto-theme listener",
55 );
56 ok(
57 themeSource.includes('autoThemeMediaQuery.addEventListener("change", syncAutoThemeBackground)'),
58 "auto mode listens for color-scheme changes",
59 );
60 ok(
61 themeSource.includes('autoThemeMediaQuery.removeEventListener("change", syncAutoThemeBackground)'),
62 "explicit modes remove the color-scheme listener",
63 );
64 ok(
65 themeSource.includes('if (theme !== "auto")') && themeSource.includes("clearAutoThemeBackgroundListener();"),
66 "non-auto themes clear the auto listener",
67 );
68 ok(
69 themeSource.includes('if (currentTheme === "auto"') && themeSource.includes('syncNativeWindowBackground("auto")'),
70 "color-scheme changes only sync native background while auto is active",
71 );
72 ok(
73 themeSource.includes("if (autoThemeMediaQuery || typeof window"),
74 "reapplying auto does not register duplicate listeners",
75 );
76
77 const workbenchRefreshIndex = stylesSource.indexOf("Native Workbench refresh");
78 const workbenchSource = workbenchRefreshIndex >= 0 ? stylesSource.slice(workbenchRefreshIndex) : "";
79 const workbenchLightBlock = blockAfter(workbenchSource, ':root[data-theme="light"]');
80 const workbenchAutoLightMediaIndex = workbenchSource.indexOf("@media (prefers-color-scheme: light)");
81 const workbenchAutoLightBlock = blockAfter(workbenchSource, ":root:not([data-theme])", workbenchAutoLightMediaIndex);
82
83 ok(workbenchRefreshIndex >= 0, "styles include the native workbench theme refresh section");
84 ok(workbenchAutoLightMediaIndex >= 0, "workbench refresh has an auto-mode light media override");
85 ok(
86 workbenchLightBlock.includes("--bg: #e9edf3;") && workbenchAutoLightBlock.includes("--bg: #e9edf3;"),
87 "auto light mode keeps the workbench light background after late root overrides",
88 );
89 ok(
90 workbenchLightBlock.includes("--fg: #121722;") && workbenchAutoLightBlock.includes("--fg: #121722;"),
91 "auto light mode keeps the workbench light foreground after late root overrides",
92 );
93 ok(
94 workbenchLightBlock.includes("--workspace-files-bg: #f1f4f8;") &&
95 workbenchAutoLightBlock.includes("--workspace-files-bg: #f1f4f8;"),
96 "auto light mode keeps workbench panel surfaces aligned with forced light mode",
97 );
98
99 const creationAssistantLightRule = ':root[data-theme="light"] .app--creation .msg--assistant .msg__body';
100 const creationAssistantAutoLightRule = ':root:not([data-theme]) .app--creation .msg--assistant .msg__body';
101 const creationAssistantLightIndex = stylesSource.indexOf(creationAssistantLightRule);
102 const creationAssistantAutoLightIndex = stylesSource.indexOf(creationAssistantAutoLightRule);
103 const creationAssistantLightBlock = blockAfter(stylesSource, creationAssistantLightRule);
104 const creationAssistantAutoLightBlock = blockAfter(stylesSource, creationAssistantAutoLightRule);
105
106 ok(creationAssistantLightIndex >= 0, "creation assistant text has an explicit light-mode color adjustment");
107 ok(
108 creationAssistantAutoLightIndex > creationAssistantLightIndex,
109 "creation assistant text mirrors the light-mode color adjustment for auto light mode",
110 );
111 ok(
112 creationAssistantLightBlock.includes("color-mix(in srgb, var(--fg) 90%, var(--bg))") &&
113 creationAssistantAutoLightBlock.includes("color-mix(in srgb, var(--fg) 90%, var(--bg))"),
114 "creation assistant auto light text color matches forced light mode",
115 );
116
117 console.log(`\n${passed} passed, ${failed} failed, ${passed + failed} total`);
118 if (failed > 0) process.exit(1);
119
119 lines TYPESCRIPT