nandi/oripublic Fork 0
3d1f70c3b1e0d8c9a0b27adcc1bd1a9f053c050e
Commits
Clone
git clone https://git.rickub.com/nandi/ori.git
git clone ssh://git@rickub.com/nandi/ori.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

forked from bots-garden/ori

mentions.test.ts · 143 lines · 3.5 KBTypeScript Blame HistoryRaw
✨ Workspace panel, selectors, previews, desktop app, sandbox template, resizable file tree, light/dark theme 76d62ac k33g yesterday1import { describe, expect, it } from "vitest";
2import {
3 activeAttachments,
4 applyCompletion,
5 detectTrigger,
6 fileItem,
7 matchesQuery,
8 skillItems,
9} from "./mentions";
10
11describe("detectTrigger", () => {
12 it("opens the file selector on @ at the start or after a space", () => {
13 expect(detectTrigger("@", 1)).toEqual({
14 kind: "file",
15 query: "",
16 start: 0,
17 });
18 expect(detectTrigger("look at @src/ma", 15)).toEqual({
19 kind: "file",
20 query: "src/ma",
21 start: 8,
22 });
23 });
24
25 it("ignores @ glued to a word or followed by whitespace", () => {
26 expect(detectTrigger("mail me@example", 15)).toBeNull();
27 expect(detectTrigger("@src done", 9)).toBeNull();
28 });
29
30 it("only looks before the caret", () => {
31 expect(detectTrigger("@a @b", 2)).toEqual({
32 kind: "file",
33 query: "a",
34 start: 0,
35 });
36 });
37
38 it("opens the skill selector on a leading slash only", () => {
39 expect(detectTrigger("/", 1)).toEqual({
40 kind: "skill",
41 query: "",
42 start: 0,
43 });
44 expect(detectTrigger("/qua", 4)).toEqual({
45 kind: "skill",
46 query: "qua",
47 start: 0,
48 });
49 expect(detectTrigger("/quality now", 12)).toBeNull();
50 expect(detectTrigger("see /path", 9)).toBeNull();
51 });
52});
53
54describe("applyCompletion", () => {
55 it("replaces the trigger and query, adds a space and moves the caret", () => {
56 const trigger = detectTrigger("look at @src/ma please", 15);
57 if (!trigger) {
58 throw new Error("expected a trigger");
59 }
60 expect(
61 applyCompletion("look at @src/ma please", trigger, "@src/main.go"),
62 ).toEqual({
63 text: "look at @src/main.go please",
64 caret: "look at @src/main.go ".length,
65 });
66 });
67
68 it("handles the slash trigger at the start", () => {
69 expect(
70 applyCompletion(
71 "/qu",
72 { kind: "skill", query: "qu", start: 0 },
73 "/quality",
74 ),
75 ).toEqual({
76 text: "/quality ",
77 caret: 9,
78 });
79 });
80});
81
82describe("skillItems", () => {
83 it("merges skills and commands, skills winning, sorted by name", () => {
84 const items = skillItems(
85 [
86 {
87 name: "quality",
88 description: "Measure quality",
89 path: "/p",
90 source: "project",
91 },
92 {
93 name: "review",
94 description: "Skill review",
95 path: "/p2",
96 source: "user",
97 },
98 ],
99 [
100 { name: "review", description: "Command review" },
101 { name: "compact", description: "Summarise" },
102 ],
103 );
104 expect(items.map((i) => [i.label, i.source, i.insert])).toEqual([
105 ["compact", "command", "/compact"],
106 ["quality", "skill", "/quality"],
107 ["review", "skill", "/review"],
108 ]);
109 });
110
111 it("filters on label or description, case-insensitively", () => {
112 const [item] = skillItems(
113 [],
114 [{ name: "compact", description: "Summarise the thread" }],
115 );
116 expect(matchesQuery(item, "COMP")).toBe(true);
117 expect(matchesQuery(item, "thread")).toBe(true);
118 expect(matchesQuery(item, "zzz")).toBe(false);
119 expect(matchesQuery(item, "")).toBe(true);
120 });
121});
122
123describe("fileItem / activeAttachments", () => {
124 it("builds an @ insertion carrying the attachment", () => {
125 expect(
126 fileItem({ path: "/w/src/main.go", relPath: "src/main.go" }),
127 ).toMatchObject({
128 insert: "@src/main.go",
129 attachment: { path: "/w/src/main.go", name: "src/main.go" },
130 });
131 });
132
133 it("drops attachments no longer mentioned and duplicates", () => {
134 const attachments = [
135 { path: "/w/a.go", name: "a.go" },
136 { path: "/w/b.go", name: "b.go" },
137 { path: "/w/a.go", name: "a.go" },
138 ];
139 expect(activeAttachments("see @a.go", attachments)).toEqual([
140 { path: "/w/a.go", name: "a.go" },
141 ]);
142 });
143});