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
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import { describe, expect, it } from "vitest";
import {
	activeAttachments,
	applyCompletion,
	detectTrigger,
	fileItem,
	matchesQuery,
	skillItems,
} from "./mentions";

describe("detectTrigger", () => {
	it("opens the file selector on @ at the start or after a space", () => {
		expect(detectTrigger("@", 1)).toEqual({
			kind: "file",
			query: "",
			start: 0,
		});
		expect(detectTrigger("look at @src/ma", 15)).toEqual({
			kind: "file",
			query: "src/ma",
			start: 8,
		});
	});

	it("ignores @ glued to a word or followed by whitespace", () => {
		expect(detectTrigger("mail me@example", 15)).toBeNull();
		expect(detectTrigger("@src done", 9)).toBeNull();
	});

	it("only looks before the caret", () => {
		expect(detectTrigger("@a @b", 2)).toEqual({
			kind: "file",
			query: "a",
			start: 0,
		});
	});

	it("opens the skill selector on a leading slash only", () => {
		expect(detectTrigger("/", 1)).toEqual({
			kind: "skill",
			query: "",
			start: 0,
		});
		expect(detectTrigger("/qua", 4)).toEqual({
			kind: "skill",
			query: "qua",
			start: 0,
		});
		expect(detectTrigger("/quality now", 12)).toBeNull();
		expect(detectTrigger("see /path", 9)).toBeNull();
	});
});

describe("applyCompletion", () => {
	it("replaces the trigger and query, adds a space and moves the caret", () => {
		const trigger = detectTrigger("look at @src/ma please", 15);
		if (!trigger) {
			throw new Error("expected a trigger");
		}
		expect(
			applyCompletion("look at @src/ma please", trigger, "@src/main.go"),
		).toEqual({
			text: "look at @src/main.go  please",
			caret: "look at @src/main.go ".length,
		});
	});

	it("handles the slash trigger at the start", () => {
		expect(
			applyCompletion(
				"/qu",
				{ kind: "skill", query: "qu", start: 0 },
				"/quality",
			),
		).toEqual({
			text: "/quality ",
			caret: 9,
		});
	});
});

describe("skillItems", () => {
	it("merges skills and commands, skills winning, sorted by name", () => {
		const items = skillItems(
			[
				{
					name: "quality",
					description: "Measure quality",
					path: "/p",
					source: "project",
				},
				{
					name: "review",
					description: "Skill review",
					path: "/p2",
					source: "user",
				},
			],
			[
				{ name: "review", description: "Command review" },
				{ name: "compact", description: "Summarise" },
			],
		);
		expect(items.map((i) => [i.label, i.source, i.insert])).toEqual([
			["compact", "command", "/compact"],
			["quality", "skill", "/quality"],
			["review", "skill", "/review"],
		]);
	});

	it("filters on label or description, case-insensitively", () => {
		const [item] = skillItems(
			[],
			[{ name: "compact", description: "Summarise the thread" }],
		);
		expect(matchesQuery(item, "COMP")).toBe(true);
		expect(matchesQuery(item, "thread")).toBe(true);
		expect(matchesQuery(item, "zzz")).toBe(false);
		expect(matchesQuery(item, "")).toBe(true);
	});
});

describe("fileItem / activeAttachments", () => {
	it("builds an @ insertion carrying the attachment", () => {
		expect(
			fileItem({ path: "/w/src/main.go", relPath: "src/main.go" }),
		).toMatchObject({
			insert: "@src/main.go",
			attachment: { path: "/w/src/main.go", name: "src/main.go" },
		});
	});

	it("drops attachments no longer mentioned and duplicates", () => {
		const attachments = [
			{ path: "/w/a.go", name: "a.go" },
			{ path: "/w/b.go", name: "b.go" },
			{ path: "/w/a.go", name: "a.go" },
		];
		expect(activeAttachments("see @a.go", attachments)).toEqual([
			{ path: "/w/a.go", name: "a.go" },
		]);
	});
});