1import { renderToStaticMarkup } from "react-dom/server";
2import { ProviderDriverKind } from "@t3tools/contracts";
3import { describe, expect, it } from "vite-plus/test";
4
5import { ComposerCommandMenu, composerSuggestionOptionId } from "./ComposerCommandMenu";
6
7describe("composerSuggestionOptionId", () => {
8 it("keeps whitespace, escape-like paths, and malformed UTF-16 distinct", () => {
9 const paths = [
10 "docs/my file.md",
11 "docs/my_file.md",
12 "docs/my%20file.md",
13 "docs/my\tfile.md",
14 "docs/\ud800.md",
15 "docs/\ud801.md",
16 "docs/\udc00.md",
17 "docs/\ufffd.md",
18 "docs/\\ud800.md",
19 "docs/\ud83d\ude80.md",
20 ];
21 const ids = paths.map((path) => composerSuggestionOptionId("suggestions", `path:file:${path}`));
22
23 expect(new Set(ids).size).toBe(paths.length);
24 for (const id of ids) expect(id).not.toMatch(/\s|[\ud800-\udfff]/u);
25 expect(composerSuggestionOptionId("other-composer", paths[0]!)).not.toBe(
26 composerSuggestionOptionId("suggestions", paths[0]!),
27 );
28 });
29});
30
31describe("ComposerCommandMenu", () => {
32 it("renders slash commands with their descriptions", () => {
33 const markup = renderToStaticMarkup(
34 <ComposerCommandMenu
35 listId="test-suggestions"
36 items={[
37 {
38 id: "slash:model",
39 type: "slash-command",
40 command: "model",
41 label: "/model",
42 description: "Switch response model for this thread",
43 },
44 ]}
45 resolvedTheme="dark"
46 isLoading={false}
47 triggerKind="slash-command"
48 activeItemId="slash:model"
49 onHighlightedItemChange={() => {}}
50 onSelect={() => {}}
51 />,
52 );
53
54 expect(markup).toContain("/model");
55 expect(markup).toContain("Switch response model for this thread");
56 });
57
58 it("shows the app source for an app skill", () => {
59 const markup = renderToStaticMarkup(
60 <ComposerCommandMenu
61 listId="test-suggestions"
62 items={[
63 {
64 id: "skill:codex:browser",
65 type: "skill",
66 provider: ProviderDriverKind.make("codex"),
67 skill: {
68 name: "browser",
69 path: "/Users/maria/.codex/plugins/browser/skills/browser/SKILL.md",
70 scope: "user",
71 enabled: true,
72 },
73 label: "Browser",
74 description: "Open and control the in-app browser",
75 },
76 ]}
77 resolvedTheme="dark"
78 isLoading={false}
79 triggerKind="skill"
80 activeItemId="skill:codex:browser"
81 onHighlightedItemChange={() => {}}
82 onSelect={() => {}}
83 />,
84 );
85
86 expect(markup).toContain("Browser");
87 expect(markup).toContain('data-slot="badge"');
88 expect(markup).toContain(">App Skill</span>");
89 expect(markup).toContain("Open and control the in-app browser");
90 expect(markup).toContain("<svg");
91 });
92
93 it("shows the repo source for a slash skill", () => {
94 const markup = renderToStaticMarkup(
95 <ComposerCommandMenu
96 listId="test-suggestions"
97 items={[
98 {
99 id: "skill:codex:ask-matt",
100 type: "skill",
101 provider: ProviderDriverKind.make("codex"),
102 skill: {
103 name: "ask-matt",
104 displayName: "Ask Matt",
105 path: "/skills/ask-matt/SKILL.md",
106 scope: "repo",
107 enabled: true,
108 },
109 label: "/skill:ask-matt",
110 description: "Find the right skill or workflow",
111 },
112 ]}
113 resolvedTheme="dark"
114 isLoading={false}
115 triggerKind="slash-command"
116 activeItemId="skill:codex:ask-matt"
117 onHighlightedItemChange={() => {}}
118 onSelect={() => {}}
119 />,
120 );
121
122 expect(markup).toContain('<span class="text-secondary-label">/skill:</span>Ask Matt');
123 expect(markup).toContain('data-slot="badge"');
124 expect(markup).toContain("lucide-folder");
125 expect(markup).toContain(">Repo</span>");
126 expect(markup).toContain("Find the right skill or workflow");
127 });
128});