返回 DeepSeek-Reasonix
settings-storage-page.test.ts
根目录 / desktop / frontend / src / __tests__ / settings-storage-page.test.ts
1 // Run: tsx src/__tests__/settings-storage-page.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 settings = readFileSync(resolve(testDir, "../components/SettingsPanel.tsx"), "utf8");
9 const settingsNavigation = readFileSync(resolve(testDir, "../components/SettingsNavigation.tsx"), "utf8");
10 const storagePage = readFileSync(resolve(testDir, "../components/StorageSettingsPage.tsx"), "utf8");
11 const bridge = readFileSync(resolve(testDir, "../lib/bridge.ts"), "utf8");
12 const backend = readFileSync(resolve(testDir, "../../../storage_app.go"), "utf8");
13 const styles = readFileSync(resolve(testDir, "../styles.css"), "utf8");
14 const types = readFileSync(resolve(testDir, "../lib/types.ts"), "utf8");
15 const locales = ["zh.ts", "zh-TW.ts", "en.ts"].map((name) => readFileSync(resolve(testDir, `../locales/${name}`), "utf8"));
16
17 let passed = 0;
18 let failed = 0;
19
20 function ok(condition: boolean, label: string) {
21 if (condition) {
22 process.stdout.write(` PASS ${label}\n`);
23 passed += 1;
24 } else {
25 process.stdout.write(` FAIL ${label}\n`);
26 failed += 1;
27 }
28 }
29
30 console.log("\nsettings storage page contract");
31
32 const tabs = settingsNavigation.match(/SETTINGS_NAV_TABS: SettingsTab\[\] = \[([^\]]+)\]/)?.[1] ?? "";
33 ok(/"storage",\s*"updates",?\s*$/.test(tabs), "Storage is immediately before Updates in the settings navigation");
34 ok(types.includes('| "storage" | "updates";'), "SettingsTab exposes the storage route before updates");
35 ok(settings.includes('tab === "storage"') && settings.includes("<StorageSettingsPage />"), "Storage renders the same page on every platform");
36 ok(settings.includes('import("./StorageSettingsPage")'), "Storage data and UI are loaded only when its page bundle renders");
37
38 const generalStart = settings.indexOf("function GeneralSection(");
39 const generalEnd = settings.indexOf("function ModelsSection(", generalStart);
40 const generalSection = settings.slice(generalStart, generalEnd);
41 ok(!generalSection.includes("StorageSettingsPage"), "General no longer embeds storage settings");
42 ok(storagePage.includes('className="mem-input"') && !storagePage.includes('className="text-input"'), "Storage paths reuse the shared settings input recipe");
43 ok(storagePage.includes("readOnly"), "Every storage path input is explicitly read-only");
44
45 const mutations = ["PickStorageFolder", "MigrateStorage", "SetDefaultWorkspace"];
46 ok(mutations.every((name) => !storagePage.includes(name) && !bridge.includes(name) && !backend.includes(name)), "Storage mutation methods are absent from UI and desktop bridge surfaces");
47 ok(!styles.includes("settings-path-control"), "Storage page contains no migration-only styling");
48 ok(!storagePage.includes("storageReadOnlyHint") && locales.every((locale) => !locale.includes("settings.storageReadOnlyHint")), "Storage page omits the redundant read-only notice");
49 ok(locales.every((locale) => !/Windows.*(?:迁移|移動|migrat)/i.test(locale.match(/"settings\.pageDesc\.storage":\s*"[^"]*"/)?.[0] ?? "")), "Storage page descriptions do not advertise Windows migration");
50 ok(!/\b(?:Walk|sizeBytes|availableBytes|models)\b/.test(backend), "Storage query performs no recursive size scan and exposes no synthetic models path");
51 ok(!types.includes("storage: StorageSettingsView"), "General SettingsView no longer owns storage data");
52 ok(!settings.match(/const needsSettings[^;]+/)?.[0].includes('tab === "storage"'), "Storage remains available when the general settings request fails");
53
54 console.log(`\n${passed} passed, ${failed} failed, ${passed + failed} total`);
55 if (failed > 0) process.exit(1);
56
56 lines TYPESCRIPT