返回 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 fixed card height and scrolls internally. JS applies no
5 // inline height in resized mode, so `height: auto` would collapse the
6 // rows=1 textarea to a single visible line forever (regression shipped in
7 // desktop-v1.17.11: typing multiline text kept the input one line tall).
8
9 import { readFileSync } from "node:fs";
10 import { dirname, resolve } from "node:path";
11 import { fileURLToPath } from "node:url";
12
13 const testDir = dirname(fileURLToPath(import.meta.url));
14 // Strip comments so declaration parsing never matches prose inside them.
15 const styles = readFileSync(resolve(testDir, "../styles.css"), "utf8").replace(/\/\*[\s\S]*?\*\//g, "");
16
17 let passed = 0;
18 let failed = 0;
19
20 function eq(a: unknown, b: unknown, label: string) {
21 if (a === b) {
22 process.stdout.write(` PASS ${label}\n`);
23 passed += 1;
24 } else {
25 process.stdout.write(` FAIL ${label}: expected ${JSON.stringify(b)}, got ${JSON.stringify(a)}\n`);
26 failed += 1;
27 }
28 }
29
30 function matchingBlocks(selector: string): string[] {
31 const blocks: string[] = [];
32 const rule = /([^{}]+)\{([^{}]*)\}/g;
33 let match: RegExpExecArray | null;
34 while ((match = rule.exec(styles)) !== null) {
35 const selectors = match[1].split(",").map((part) => part.trim());
36 if (selectors.includes(selector)) blocks.push(match[2]);
37 }
38 return blocks;
39 }
40
41 function finalDeclaration(selector: string, property: string): string | undefined {
42 let value: string | undefined;
43 for (const block of matchingBlocks(selector)) {
44 const declaration = new RegExp(`(?:^|;)\\s*${property}\\s*:\\s*([^;]+)`, "g");
45 let match: RegExpExecArray | null;
46 while ((match = declaration.exec(block)) !== null) {
47 value = match[1].trim();
48 }
49 }
50 return value;
51 }
52
53 console.log("\ncomposer resized input css");
54
55 eq(finalDeclaration(".composer-card--resized .composer__input", "height"), "100%", "resized textarea fills the card's input area");
56 eq(finalDeclaration(".composer-card--resized .composer__input", "overflow-y"), "auto", "resized textarea scrolls internally");
57 eq(finalDeclaration(".composer-card--resized .composer__rich-input", "height"), "100%", "resized rich input fills the card's input area");
58 eq(finalDeclaration(".composer-card--resized .composer__rich-input", "overflow-y"), "auto", "resized rich input scrolls internally");
59 eq(finalDeclaration(".composer-invocation-caret-anchor", "min-width"), "1px", "invocation caret anchor keeps a compact hit target");
60 eq(finalDeclaration(".composer-invocation-caret-anchor", "width"), undefined, "invocation caret anchor can expand around WebView2-hosted text");
61 eq(finalDeclaration(".composer-invocation-caret-anchor", "overflow"), undefined, "invocation caret anchor does not clip WebView2-hosted text");
62
63 console.log(`\n${passed} passed, ${failed} failed`);
64 if (failed > 0) process.exit(1);
65
65 lines TYPESCRIPT