返回 DeepSeek-Reasonix
startup-settings-contract.test.ts
根目录 / desktop / frontend / src / __tests__ / startup-settings-contract.test.ts
1 // Run: tsx src/__tests__/startup-settings-contract.test.ts
2
3 import { readFileSync } from "node:fs";
4 import { dirname, resolve } from "node:path";
5 import { fileURLToPath } from "node:url";
6 import {
7 ONBOARDING_DISMISSED_STORAGE_KEY,
8 dismissOnboarding,
9 onboardingWasDismissed,
10 shouldOpenOnboarding,
11 } from "../lib/onboarding";
12
13 let passed = 0;
14 let failed = 0;
15
16 function ok(cond: boolean, label: string) {
17 if (cond) {
18 process.stdout.write(` PASS ${label}\n`);
19 passed += 1;
20 } else {
21 process.stdout.write(` FAIL ${label}\n`);
22 failed += 1;
23 }
24 }
25
26 const here = dirname(fileURLToPath(import.meta.url));
27 const appSource = readFileSync(resolve(here, "../App.tsx"), "utf8");
28 const bridgeSource = readFileSync(resolve(here, "../lib/bridge.ts"), "utf8");
29 const settingsSource = readFileSync(resolve(here, "../components/SettingsPanel.tsx"), "utf8");
30 const enLocaleSource = readFileSync(resolve(here, "../locales/en.ts"), "utf8");
31 const zhLocaleSource = readFileSync(resolve(here, "../locales/zh.ts"), "utf8");
32 const zhTWLocaleSource = readFileSync(resolve(here, "../locales/zh-TW.ts"), "utf8");
33
34 console.log("\nstartup settings contract");
35
36 ok(
37 bridgeSource.includes("DesktopStartupSettings()"),
38 "bridge exposes a lightweight desktop startup settings call",
39 );
40 ok(
41 appSource.includes("app.DesktopStartupSettings()"),
42 "App loads startup chrome preferences through the lightweight settings call",
43 );
44 ok(
45 !/const\s+reloadSidebarImConnections[\s\S]*?app\.Settings\(\)[\s\S]*?\}, \[t\]\);/.test(appSource),
46 "sidebar IM refresh avoids rebuilding the full Settings payload",
47 );
48 ok(
49 !/const\s+syncDesktopPreferences[\s\S]*?app\.Settings\(\)[\s\S]*?\};/.test(appSource),
50 "startup preference sync avoids rebuilding the full Settings payload",
51 );
52 ok(
53 /onChooseProvider=\{\(\) => \{[\s\S]*?setSettingsFocus\(\{ target: "model-access" \}\);[\s\S]*?setSettingsTarget\("models"\);/.test(appSource),
54 "onboarding opens the model access flow instead of model usage",
55 );
56 ok(
57 /initialFocus\?\.target === "model-access"[\s\S]*?initialFocus\?\.target === "model-stats"[\s\S]*?"usage"/.test(settingsSource),
58 "model settings honor access and statistics focus targets while preserving usage as the default",
59 );
60 ok(
61 !settingsSource.includes("modelFocusHandledRef"),
62 "each fresh model focus object can re-target the same subtab again",
63 );
64 ok(
65 /setSettingsFocus\(\(current\) => \(\{[\s\S]*?target: "model-stats",[\s\S]*?requestId: \(current\?\.requestId \?\? 0\) \+ 1,[\s\S]*?\}\)\)/.test(appSource) &&
66 /initialFocus\?\.requestId/.test(settingsSource),
67 "usage statistics commands derive a monotonic request id from the shared focus state",
68 );
69 ok(
70 /case "deepseek-responses":\s*return t\("settings\.addProvider\.preset\.deepseekResponsesDesc"\)/.test(settingsSource),
71 "DeepSeek Responses preset uses a localized description",
72 );
73 ok(
74 /case "deepseek-anthropic":\s*return t\("settings\.addProvider\.preset\.deepseekAnthropicDesc"\)/.test(settingsSource),
75 "DeepSeek Anthropic preset uses a localized description",
76 );
77 ok(
78 /case "token-rhythm":\s*return t\("settings\.addProvider\.preset\.tokenRhythmDesc"\)/.test(settingsSource) &&
79 /preset\.id === "token-rhythm"\) return t\("settings\.addProvider\.preset\.tokenRhythmLabel"\)/.test(settingsSource),
80 "Token Rhythm preset localizes the English and Chinese brand names",
81 );
82 ok(
83 [enLocaleSource, zhLocaleSource, zhTWLocaleSource].every((source) =>
84 source.includes('"settings.addProvider.preset.deepseekResponsesDesc"') &&
85 source.includes('"settings.addProvider.preset.deepseekAnthropicDesc"') &&
86 source.includes('"settings.addProvider.preset.tokenRhythmLabel"') &&
87 source.includes('"settings.addProvider.preset.tokenRhythmDesc"'),
88 ),
89 "provider preset localization is present in every supported locale",
90 );
91 ok(
92 enLocaleSource.includes('"settings.addProvider.preset.tokenRhythmLabel": "Token Rhythm"') &&
93 zhLocaleSource.includes('"settings.addProvider.preset.tokenRhythmLabel": "基元律动"') &&
94 zhTWLocaleSource.includes('"settings.addProvider.preset.tokenRhythmLabel": "基元律动"'),
95 "Token Rhythm preset uses the official English and Chinese brand names",
96 );
97 ok(
98 [enLocaleSource, zhLocaleSource, zhTWLocaleSource].every((source) =>
99 source.includes('"settings.reasoningProtocol.glm"'),
100 ),
101 "GLM reasoning protocol is localized in every supported locale",
102 );
103 ok(
104 /mockPreset\("deepseek-anthropic",\s*"DeepSeek Anthropic"/.test(bridgeSource),
105 "browser mock exposes the DeepSeek Anthropic preset",
106 );
107 ok(
108 /mockPreset\("token-rhythm",\s*"Token Rhythm"/.test(bridgeSource),
109 "browser mock exposes the Token Rhythm preset",
110 );
111 ok(
112 /function mockProviderPresetDisplayRank\(id: string\): number \{\s*if \(id === "deepseek-responses"\) return -1;\s*if \(id === "deepseek-anthropic"\) return 0;/.test(bridgeSource),
113 "browser mock ranks Responses first and Anthropic as the adjacent compatibility preset",
114 );
115
116 const values = new Map<string, string>();
117 const storage = {
118 getItem: (key: string) => values.get(key) ?? null,
119 setItem: (key: string, value: string) => values.set(key, value),
120 removeItem: (key: string) => values.delete(key),
121 clear: () => values.clear(),
122 key: (index: number) => [...values.keys()][index] ?? null,
123 get length() { return values.size; },
124 } as Storage;
125
126 ok(!onboardingWasDismissed(storage), "fresh installs have no onboarding dismissal marker");
127 ok(shouldOpenOnboarding(true, storage), "missing providers open the guide before dismissal");
128 ok(!shouldOpenOnboarding(false, storage), "configured providers never open the guide");
129 dismissOnboarding(storage);
130 ok(values.get(ONBOARDING_DISMISSED_STORAGE_KEY) === "1", "skip persists a versioned dismissal marker");
131 ok(!shouldOpenOnboarding(true, storage), "persisted skip prevents repeated full-screen interruption");
132
133 console.log(`\n${passed} passed, ${failed} failed`);
134 if (failed > 0) process.exit(1);
135
135 lines TYPESCRIPT