返回 DeepSeek-Reasonix
command-palette-css.test.ts
根目录 / desktop / frontend / src / __tests__ / command-palette-css.test.ts
1 // Run: tsx src/__tests__/command-palette-css.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 styles = readFileSync(resolve(testDir, "../styles.css"), "utf8");
9
10 let passed = 0;
11 let failed = 0;
12
13 function eq(a: unknown, b: unknown, label: string) {
14 if (a === b) {
15 process.stdout.write(` PASS ${label}\n`);
16 passed += 1;
17 } else {
18 process.stdout.write(` FAIL ${label}: expected ${JSON.stringify(b)}, got ${JSON.stringify(a)}\n`);
19 failed += 1;
20 }
21 }
22
23 function ok(value: unknown, label: string) {
24 if (value) {
25 process.stdout.write(` PASS ${label}\n`);
26 passed += 1;
27 } else {
28 process.stdout.write(` FAIL ${label}\n`);
29 failed += 1;
30 }
31 }
32
33 function matchingBlocks(selector: string): string[] {
34 const blocks: string[] = [];
35 const rule = /([^{}]+)\{([^{}]*)\}/g;
36 let match: RegExpExecArray | null;
37 while ((match = rule.exec(styles)) !== null) {
38 const selectors = match[1].split(",").map((part) => part.trim());
39 if (selectors.includes(selector)) blocks.push(match[2]);
40 }
41 return blocks;
42 }
43
44 function finalDeclaration(selector: string, property: string): string | undefined {
45 let value: string | undefined;
46 for (const block of matchingBlocks(selector)) {
47 const declaration = new RegExp(`(?:^|;)\\s*${property}\\s*:\\s*([^;]+)`, "g");
48 let match: RegExpExecArray | null;
49 while ((match = declaration.exec(block)) !== null) {
50 value = match[1].trim();
51 }
52 }
53 return value;
54 }
55
56 console.log("\ncommand palette css");
57
58 eq(
59 finalDeclaration(".palette__item", "display"),
60 "flex",
61 "palette result rows stay in flex layout",
62 );
63
64 eq(
65 finalDeclaration(".palette__item", "flex-direction") ?? "row",
66 "row",
67 "palette result rows keep icon and text side-by-side",
68 );
69
70 eq(
71 finalDeclaration(".palette__body", "min-width"),
72 "0",
73 "palette text body can shrink before ellipsis",
74 );
75
76 for (const selector of [".palette__title", ".palette__hint"]) {
77 eq(finalDeclaration(selector, "overflow"), "hidden", `${selector} clips overflow inside the row`);
78 eq(finalDeclaration(selector, "text-overflow"), "ellipsis", `${selector} uses ellipsis for long text`);
79 eq(finalDeclaration(selector, "white-space"), "nowrap", `${selector} stays on one measured line`);
80 }
81
82 ok(
83 !matchingBlocks(".palette__item").some((block) => /(?:^|;)\s*flex-direction\s*:\s*column\s*(?:;|$)/.test(block)),
84 "no palette item rule can stack icon above long session text",
85 );
86
87 console.log(`\n${passed} passed, ${failed} failed`);
88 if (failed > 0) process.exit(1);
89
89 lines TYPESCRIPT