1import { ThreadId } from "@t3tools/contracts";
2import { describe, expect, it } from "vite-plus/test";
3
4import { fileChipMenu, fileChipShareSource, resolveFileChipTarget } from "./fileChipMenu";
5
6describe("resolveFileChipTarget", () => {
7 it("resolves a workspace-relative link to both paths", () => {
8 expect(resolveFileChipTarget("src/app.ts:12", "/repo")).toEqual({
9 fullPath: "/repo/src/app.ts",
10 relativePath: "src/app.ts",
11 });
12 });
13
14 it("keeps only the full path for a host file outside the workspace", () => {
15 expect(resolveFileChipTarget("/tmp/report.md", "/repo")).toEqual({
16 fullPath: "/tmp/report.md",
17 });
18 });
19
20 it("keeps only the relative path when the workspace root is unknown", () => {
21 expect(resolveFileChipTarget("src/app.ts", null)).toEqual({ relativePath: "src/app.ts" });
22 });
23
24 it("ignores links that are not files or cannot be opened", () => {
25 expect(resolveFileChipTarget("https://example.com/app.ts", "/repo")).toBeNull();
26 expect(resolveFileChipTarget("~/report.md", "/repo")).toBeNull();
27 expect(resolveFileChipTarget("../other/file.ts", "/repo")).toBeNull();
28 });
29});
30
31describe("fileChipMenu", () => {
32 it("offers only the copies the target can satisfy", () => {
33 expect(fileChipMenu({ fullPath: "/tmp/report.md" })).toEqual({
34 title: "/tmp/report.md",
35 actions: [
36 { id: "copy-full-path", title: "Copy full path" },
37 { id: "open-file", title: "Open in file viewer" },
38 ],
39 });
40 expect(fileChipMenu({ relativePath: "src/app.ts" }).actions.map(({ id }) => id)).toEqual([
41 "copy-relative-path",
42 "open-file",
43 ]);
44 });
45});
46
47describe("file chip downloads", () => {
48 const threadId = ThreadId.make("thread-1");
49
50 it.each([
51 [
52 "/tmp/maria-counter/maria-counter-final.mp4",
53 "/tmp/maria-counter/maria-counter-final.mp4",
54 "video/mp4",
55 ],
56 ["/tmp/take%2520%23one.mp4:12", "/tmp/take%20#one.mp4", "video/mp4"],
57 ["/tmp/report.pdf", "/tmp/report.pdf", "application/pdf"],
58 ["screens/image.PNG", "/repo/screens/image.PNG", "image/png"],
59 ])("offers a host download for %s", (href, path, mimeType) => {
60 const target = resolveFileChipTarget(href, "/repo")!;
61 expect(fileChipMenu(target).actions).toContainEqual({
62 id: "save",
63 title: "Save or share",
64 });
65 expect(fileChipShareSource(target, threadId)).toEqual({
66 name: path.split("/").at(-1),
67 mimeType,
68 resource: { _tag: "media-file", threadId, path },
69 });
70 });
71
72 it("retains the thread context for a relative file without a known workspace root", () => {
73 expect(
74 fileChipShareSource(resolveFileChipTarget("clips/demo.mp4", null)!, threadId),
75 ).toMatchObject({
76 resource: { _tag: "media-file", threadId, path: "clips/demo.mp4" },
77 });
78 });
79
80 it("does not offer downloads the host asset endpoint cannot serve", () => {
81 for (const href of ["src/app.ts", "/tmp/archive.zip", "/tmp/clip.mp4.txt"]) {
82 const target = resolveFileChipTarget(href, "/repo")!;
83 expect(fileChipShareSource(target, threadId)).toBeNull();
84 expect(fileChipMenu(target).actions.some(({ id }) => id === "save")).toBe(false);
85 }
86 });
87});