1import { describe, expect, it } from "vite-plus/test";
2import { parseFileUrlHref, splitFilePathPosition, resolvePathLinkTarget } from "./fileLinks.ts";
3
4describe("parseFileUrlHref", () => {
5 it.each([
6 ["file:///Users/julius/project/src/main.ts#L42", "/Users/julius/project/src/main.ts", "#L42"],
7 [
8 "file:///D:/Programme/t3code/OpenInPicker.tsx#L69",
9 "D:/Programme/t3code/OpenInPicker.tsx",
10 "#L69",
11 ],
12 ["file://server/share/workspace-image.svg", "\\\\server\\share\\workspace-image.svg", ""],
13 ["file://localhost/home/me/notes.md", "/home/me/notes.md", ""],
14 ])("parses %s", (href, path, hash) => {
15 expect(parseFileUrlHref(href)).toEqual({ path, hash });
16 });
17
18 it("keeps percent-encoding so the caller decodes once", () => {
19 expect(parseFileUrlHref("file:///Users/julius/project/file%2520name.md")?.path).toBe(
20 "/Users/julius/project/file%2520name.md",
21 );
22 expect(parseFileUrlHref("file:///c%3A/Users/x/shot.png")?.path).toBe("/c%3A/Users/x/shot.png");
23 });
24
25 it.each(["https://example.com/a.ts", "file://%", "/Users/julius/a.ts"])("rejects %s", (href) => {
26 expect(parseFileUrlHref(href)).toBeNull();
27 });
28});
29
30describe("splitFilePathPosition", () => {
31 it.each([
32 ["src/main.ts", "", { path: "src/main.ts" }],
33 ["src/main.ts:12", "", { path: "src/main.ts", line: 12 }],
34 ["src/main.ts:12:5", "", { path: "src/main.ts", line: 12, column: 5 }],
35 ["src/main.ts", "#L18C2", { path: "src/main.ts", line: 18, column: 2 }],
36 ["src/main.ts:3", "#L18C2", { path: "src/main.ts", line: 3 }],
37 ["src/main.ts:0", "", { path: "src/main.ts" }],
38 ["src/main.ts", "#section", { path: "src/main.ts" }],
39 ])("splits %s%s", (path, hash, expected) => {
40 expect(splitFilePathPosition(path, hash)).toEqual(expected);
41 });
42});
43
44describe("resolvePathLinkTarget", () => {
45 it("resolves relative paths against cwd", () => {
46 expect(
47 resolvePathLinkTarget(
48 "src/components/ThreadTerminalDrawer.tsx:42:7",
49 "/Users/julius/project",
50 ),
51 ).toBe("/Users/julius/project/src/components/ThreadTerminalDrawer.tsx:42:7");
52 });
53
54 it("keeps absolute paths unchanged", () => {
55 expect(
56 resolvePathLinkTarget("/Users/julius/project/src/main.ts:12", "/Users/julius/project"),
57 ).toBe("/Users/julius/project/src/main.ts:12");
58 });
59
60 it("keeps Windows absolute paths with forward slashes unchanged", () => {
61 expect(
62 resolvePathLinkTarget("C:/Users/julius/project/src/main.ts:12", "C:\\Users\\julius\\project"),
63 ).toBe("C:/Users/julius/project/src/main.ts:12");
64 });
65
66 it.each([
67 ["C:\\Users\\julius\\project", "C:\\Users\\julius\\notes.md:3"],
68 ["C:/Users/julius/project", "C:/Users/julius\\notes.md:3"],
69 ])("resolves home paths against the Windows cwd %s", (cwd, expected) => {
70 expect(resolvePathLinkTarget("~/notes.md:3", cwd)).toBe(expected);
71 });
72});