Open History to see the last commit
1import { ThreadId } from "@t3tools/contracts";
2import { describe, expect, it } from "vite-plus/test";
3
4import {
5 fileChipClipboardPath,
6 fileChipMenu,
7 fileChipShareSource,
8 resolveFileChipTarget,
9} from "./fileChipMenu";
10
11it("copies native paths from a relative file chip in a Windows workspace", () => {
12 const target = resolveFileChipTarget("reports/Device Users.csv", "C:/repo");
13 if (!target) throw new Error("File-chip target unavailable");
14 expect(fileChipClipboardPath(target, "copy-full-path")).toBe(
15 String.raw`C:\repo\reports\Device Users.csv`,
16 );
17 expect(fileChipClipboardPath(target, "copy-relative-path")).toBe(
18 String.raw`reports\Device Users.csv`,
19 );
20});
21
22describe("resolveFileChipTarget", () => {
23 it("resolves a workspace-relative link to both paths", () => {
24 expect(resolveFileChipTarget("src/app.ts:12", "/repo")).toEqual({
25 fullPath: "/repo/src/app.ts",
26 relativePath: "src/app.ts",
27 });
28 });
29
30 it("resolves home links for navigation and menu actions", () => {
31 expect(resolveFileChipTarget("~/.agents/AGENTS.md:314", "/home/maria/repo")).toEqual({
32 fullPath: "/home/maria/.agents/AGENTS.md",
33 });
34 expect(resolveFileChipTarget("~/repo/src/main.ts:12", "/home/maria/repo")).toEqual({
35 fullPath: "/home/maria/repo/src/main.ts",
36 relativePath: "src/main.ts",
37 });
38 expect(resolveFileChipTarget("~/.agents/AGENTS.md", "C:\\Users\\maria\\repo")).toEqual({
39 fullPath: "C:\\Users\\maria\\.agents\\AGENTS.md",
40 });
41 });
42
43 it("ignores links that are not files or cannot be opened", () => {
44 expect(resolveFileChipTarget("https://example.com/app.ts", "/repo")).toBeNull();
45 expect(resolveFileChipTarget("~/report.md", "/repo")).toBeNull();
46 expect(resolveFileChipTarget("../other/file.ts", "/repo")).toBeNull();
47 });
48});
49
50describe("file chip downloads", () => {
51 const threadId = ThreadId.make("thread-1");
52
53 it.each([
54 [
55 "/tmp/maria-counter/maria-counter-final.mp4",
56 "/tmp/maria-counter/maria-counter-final.mp4",
57 "video/mp4",
58 ],
59 ["/tmp/take%2520%23one.mp4:12", "/tmp/take%20#one.mp4", "video/mp4"],
60 ["/tmp/report.pdf", "/tmp/report.pdf", "application/pdf"],
61 ["screens/image.PNG", "/repo/screens/image.PNG", "image/png"],
62 ["/tmp/archive.zip", "/tmp/archive.zip", "application/zip"],
63 ["/tmp/archive.jar", "/tmp/archive.jar", "application/java-archive"],
64 ["/tmp/archive.tar", "/tmp/archive.tar", "application/x-tar"],
65 ["/tmp/archive.gz", "/tmp/archive.gz", "application/gzip"],
66 ["/tmp/archive.tgz", "/tmp/archive.tgz", "application/gzip"],
67 ["/tmp/data.sqlite", "/tmp/data.sqlite", "application/vnd.sqlite3"],
68 ["/tmp/data.sqlite3", "/tmp/data.sqlite3", "application/vnd.sqlite3"],
69 ["/tmp/data.db", "/tmp/data.db", "application/vnd.sqlite3"],
70 ])("offers a host download for %s", (href, path, mimeType) => {
71 const target = resolveFileChipTarget(href, "/repo")!;
72 expect(fileChipMenu(target).actions).toContainEqual({
73 id: "save",
74 title: "Save or share",
75 });
76 expect(fileChipShareSource(target, threadId)).toEqual({
77 name: path.split("/").at(-1),
78 mimeType,
79 resource: { _tag: "media-file", threadId, path },
80 });
81 });
82
83 it("does not offer downloads the host asset endpoint cannot serve", () => {
84 for (const href of ["src/app.ts", "/tmp/archive.7z", "/tmp/clip.mp4.txt"]) {
85 const target = resolveFileChipTarget(href, "/repo")!;
86 expect(fileChipShareSource(target, threadId)).toBeNull();
87 expect(fileChipMenu(target).actions.some(({ id }) => id === "save")).toBe(false);
88 }
89 });
90});