返回 DeepSeek-Reasonix
ref-token.test.tsx
根目录 / desktop / frontend / src / __tests__ / ref-token.test.tsx
1 // Run: tsx src/__tests__/ref-token.test.tsx
2 //
3 // Tests for the @-token escape grammar (lib/refToken) and the composer/file
4 // menu helpers that produce and consume it. Mirrors internal/control/refs.go:
5 // backslash-escaped space/tab stays in the token; other backslashes are
6 // literal so Windows separators keep their meaning.
7
8 import { escapeRefPath, unescapeRefPath, activeRefTokenRe } from "../lib/refToken";
9 import { activeFileReferenceToken, pickInlineFileReference } from "../components/FileReferenceMenu";
10 import { composerPickFileEntry } from "../components/Composer";
11 import type { DirEntry } from "../lib/types";
12
13 let passed = 0;
14 let failed = 0;
15
16 function eq(a: unknown, b: unknown, label: string) {
17 if (JSON.stringify(a) === JSON.stringify(b)) {
18 process.stdout.write(` PASS ${label}\n`);
19 passed += 1;
20 } else {
21 process.stdout.write(` FAIL ${label}: expected ${JSON.stringify(b)}, got ${JSON.stringify(a)}\n`);
22 failed += 1;
23 }
24 }
25
26 console.log("\nref token grammar");
27
28 eq(escapeRefPath("my file.txt"), "my\\ file.txt", "escapeRefPath escapes spaces");
29 eq(escapeRefPath("plain.txt"), "plain.txt", "escapeRefPath leaves space-free paths alone");
30 eq(unescapeRefPath("my\\ file.txt"), "my file.txt", "unescapeRefPath reverses escaping");
31 eq(unescapeRefPath("C:\\dir\\file.png"), "C:\\dir\\file.png", "unescapeRefPath keeps Windows separators literal");
32 eq(unescapeRefPath(escapeRefPath("a b\tc.md")), "a b\tc.md", "escape/unescape round-trips");
33
34 eq(activeRefTokenRe.exec("see @docs/my\\ file.md")?.[1], "docs/my\\ file.md", "active token keeps escaped spaces");
35 eq(activeRefTokenRe.exec("see @docs/x plain")?.[1], undefined, "unescaped space still ends the token");
36
37 console.log("\nfile reference menu");
38
39 eq(
40 activeFileReferenceToken("look at @docs/my\\ dir/fra"),
41 { raw: "docs/my\\ dir/fra", dir: "docs/my\\ dir/", frag: "fra" },
42 "token splits dir/frag with escapes intact",
43 );
44 eq(activeFileReferenceToken("@my\\ f")?.frag, "my f", "frag is unescaped for entry matching");
45
46 const spacedFile: DirEntry = { name: "my file.md", isDir: false };
47 eq(
48 pickInlineFileReference("see @docs/", "docs/", "docs/", spacedFile),
49 "see @docs/my\\ file.md ",
50 "inline pick escapes whitespace in the inserted ref",
51 );
52 eq(
53 pickInlineFileReference("see @docs/my\\ dir/", "docs/my\\ dir/", "docs/my\\ dir/", spacedFile),
54 "see @docs/my\\ dir/my\\ file.md ",
55 "inline pick through an escaped dir stays escaped once",
56 );
57
58 console.log("\ncomposer pick");
59
60 const inline = composerPickFileEntry("note @sr", "sr", "", { name: "my report.pdf", isDir: false });
61 eq(inline.text, "note @my\\ report.pdf ", "composer inline fallback escapes whitespace");
62 eq(inline.workspaceRef, undefined, "no structured ref for a name-only entry");
63
64 const structured = composerPickFileEntry("note @sr", "sr", "", {
65 name: "my report.pdf",
66 path: "docs/my report.pdf",
67 isDir: false,
68 });
69 eq(structured.workspaceRef?.path, "docs/my report.pdf", "structured ref keeps the real unescaped path");
70 eq(structured.text, "note ", "structured pick removes the typed token");
71
72 process.stdout.write(`\n${passed} passed, ${failed} failed\n`);
73 if (failed > 0) process.exit(1);
74
74 lines Plain Text