返回 DeepSeek-Reasonix
composer-resized-input-css.test.ts
根目录 / desktop / frontend / src / __tests__ / composer-resized-input-css.test.ts
1 // Run: tsx src/__tests__/composer-resized-input-css.test.ts
2 //
3 // Contract: in the user-resized composer (`.composer-card--resized`), the
4 // input fills the available card height and can scroll internally. These CSS
5 // fallbacks remain authoritative during a live resize, when JS temporarily
6 // removes the content-derived inline height.
7
8 import { readFileSync } from "node:fs";
9 import { dirname, resolve } from "node:path";
10 import { fileURLToPath } from "node:url";
11
12 const testDir = dirname(fileURLToPath(import.meta.url));
13 // Strip comments so declaration parsing never matches prose inside them.
14 const styles = readFileSync(resolve(testDir, "../styles.css"), "utf8").replace(/\/\*[\s\S]*?\*\//g, "");
15
16 let passed = 0;
17 let failed = 0;
18
19 function eq(a: unknown, b: unknown, label: string) {
20 if (a === b) {
21 process.stdout.write(` PASS ${label}\n`);
22 passed += 1;
23 } else {
24 process.stdout.write(` FAIL ${label}: expected ${JSON.stringify(b)}, got ${JSON.stringify(a)}\n`);
25 failed += 1;
26 }
27 }
28
29 function matchingBlocks(selector: string): string[] {
30 const blocks: string[] = [];
31 const rule = /([^{}]+)\{([^{}]*)\}/g;
32 let match: RegExpExecArray | null;
33 while ((match = rule.exec(styles)) !== null) {
34 const selectors = match[1].split(",").map((part) => part.trim());
35 if (selectors.includes(selector)) blocks.push(match[2]);
36 }
37 return blocks;
38 }
39
40 function finalDeclaration(selector: string, property: string): string | undefined {
41 let value: string | undefined;
42 for (const block of matchingBlocks(selector)) {
43 const declaration = new RegExp(`(?:^|;)\\s*${property}\\s*:\\s*([^;]+)`, "g");
44 let match: RegExpExecArray | null;
45 while ((match = declaration.exec(block)) !== null) {
46 value = match[1].trim();
47 }
48 }
49 return value;
50 }
51
52 console.log("\ncomposer resized input css");
53
54 eq(finalDeclaration(".composer-card--resized .composer__input", "height"), "100%", "resized textarea fills the card's input area");
55 eq(finalDeclaration(".composer-card--resized .composer__input", "overflow-y"), "auto", "resized textarea scrolls internally");
56 eq(finalDeclaration(".composer-card--resized .composer__rich-input", "height"), "100%", "resized rich input fills the card's input area");
57 eq(finalDeclaration(".composer-card--resized .composer__rich-input", "overflow-y"), "auto", "resized rich input scrolls internally");
58 eq(finalDeclaration(".composer-invocation-caret-anchor", "min-width"), "1px", "invocation caret anchor keeps a compact hit target");
59 eq(finalDeclaration(".composer-invocation-caret-anchor", "width"), undefined, "invocation caret anchor can expand around WebView2-hosted text");
60 eq(finalDeclaration(".composer-invocation-caret-anchor", "overflow"), undefined, "invocation caret anchor does not clip WebView2-hosted text");
61
62 console.log(`\n${passed} passed, ${failed} failed`);
63 if (failed > 0) process.exit(1);
64
64 lines TYPESCRIPT