1import { describe, expect, it } from "vite-plus/test";
2
3import {
4 appendBrowsePathSegment,
5 canNavigateUp,
6 getBrowseDirectoryPath,
7 findProjectByPath,
8 getBrowseLeafPathSegment,
9 getBrowseParentPath,
10 hasTrailingPathSeparator,
11 inferProjectTitleFromPath,
12 isExplicitRelativeProjectPath,
13 isFilesystemBrowseQuery,
14 normalizeProjectPathForComparison,
15 normalizeProjectPathForDispatch,
16 isUnsupportedWindowsProjectPath,
17 resolveProjectPathForDispatch,
18} from "./projectPaths";
19
20describe("projectPaths", () => {
21 it("normalizes trailing separators for dispatch and comparison", () => {
22 expect(normalizeProjectPathForDispatch(" /repo/app/ ")).toBe("/repo/app");
23 expect(normalizeProjectPathForComparison("/repo/app/")).toBe("/repo/app");
24 });
25
26 it("normalizes windows-style paths for comparison", () => {
27 expect(normalizeProjectPathForComparison("C:/Work/Repo/")).toBe("c:\\work\\repo");
28 expect(normalizeProjectPathForComparison("C:\\Work\\Repo\\")).toBe("c:\\work\\repo");
29 });
30
31 it("finds existing projects even when the input formatting differs", () => {
32 const existing = findProjectByPath(
33 [
34 { id: "project-1", cwd: "/repo/app" },
35 { id: "project-2", cwd: "C:\\Work\\Repo" },
36 ],
37 "C:/Work/Repo/",
38 );
39
40 expect(existing?.id).toBe("project-2");
41 });
42
43 it("infers project titles from normalized paths", () => {
44 expect(inferProjectTitleFromPath("/repo/app/")).toBe("app");
45 expect(inferProjectTitleFromPath("C:\\Work\\Repo\\")).toBe("Repo");
46 expect(inferProjectTitleFromPath("/home/user\\project/")).toBe("user\\project");
47 });
48
49 it("detects browse queries across supported path styles", () => {
50 expect(isFilesystemBrowseQuery(".")).toBe(false);
51 expect(isFilesystemBrowseQuery("..")).toBe(false);
52 expect(isFilesystemBrowseQuery("./")).toBe(true);
53 expect(isFilesystemBrowseQuery("../")).toBe(true);
54 expect(isFilesystemBrowseQuery("~/projects")).toBe(true);
55 expect(isFilesystemBrowseQuery("..\\docs")).toBe(true);
56 expect(isFilesystemBrowseQuery("notes")).toBe(false);
57 });
58
59 it("only treats windows-style paths as browse queries on windows", () => {
60 expect(isFilesystemBrowseQuery("C:\\Work\\Repo\\", "MacIntel")).toBe(false);
61 expect(isFilesystemBrowseQuery("C:\\Work\\Repo\\", "Win32")).toBe(true);
62 expect(isUnsupportedWindowsProjectPath("C:\\Work\\Repo\\", "MacIntel")).toBe(true);
63 expect(isUnsupportedWindowsProjectPath("C:\\Work\\Repo\\", "Win32")).toBe(false);
64 });
65
66 it("detects explicit relative project paths", () => {
67 expect(isExplicitRelativeProjectPath(".")).toBe(true);
68 expect(isExplicitRelativeProjectPath("..")).toBe(true);
69 expect(isExplicitRelativeProjectPath("./docs")).toBe(true);
70 expect(isExplicitRelativeProjectPath("..\\docs")).toBe(true);
71 expect(isExplicitRelativeProjectPath("/repo/docs")).toBe(false);
72 });
73
74 it("resolves explicit relative paths against the current project", () => {
75 expect(resolveProjectPathForDispatch(".", "/repo/app")).toBe("/repo/app");
76 expect(resolveProjectPathForDispatch("..", "/repo/app")).toBe("/repo");
77 expect(resolveProjectPathForDispatch("./docs", "/repo/app")).toBe("/repo/app/docs");
78 expect(resolveProjectPathForDispatch("../docs", "/repo/app")).toBe("/repo/docs");
79 expect(resolveProjectPathForDispatch("./Repo", "C:\\Work")).toBe("C:\\Work\\Repo");
80 expect(resolveProjectPathForDispatch("./docs", "/home/user\\project")).toBe(
81 "/home/user\\project/docs",
82 );
83 });
84
85 it("navigates browse paths with matching separators", () => {
86 expect(appendBrowsePathSegment("/repo/", "src")).toBe("/repo/src/");
87 expect(appendBrowsePathSegment("C:\\Work\\", "Repo")).toBe("C:\\Work\\Repo\\");
88 expect(appendBrowsePathSegment("/home/user\\project/", "docs")).toBe(
89 "/home/user\\project/docs/",
90 );
91 expect(getBrowseParentPath("/repo/src/")).toBe("/repo/");
92 expect(getBrowseParentPath("C:\\Work\\Repo\\")).toBe("C:\\Work\\");
93 expect(getBrowseParentPath("\\\\server\\share\\")).toBeNull();
94 expect(getBrowseParentPath("\\\\server\\share\\repo\\")).toBe("\\\\server\\share\\");
95 expect(getBrowseParentPath("C:\\")).toBeNull();
96 expect(getBrowseParentPath("/home/user\\project/docs/")).toBe("/home/user\\project/");
97 });
98
99 it("detects browse path boundaries", () => {
100 expect(hasTrailingPathSeparator("/repo/src/")).toBe(true);
101 expect(hasTrailingPathSeparator("/repo/src")).toBe(false);
102 expect(getBrowseDirectoryPath("/repo/src")).toBe("/repo/");
103 expect(getBrowseDirectoryPath("/repo/src/")).toBe("/repo/src/");
104 expect(getBrowseLeafPathSegment("/repo/src")).toBe("src");
105 expect(getBrowseLeafPathSegment("C:\\Work\\Repo\\Docs")).toBe("Docs");
106 expect(getBrowseDirectoryPath("/home/user\\project/docs")).toBe("/home/user\\project/");
107 expect(getBrowseLeafPathSegment("/home/user\\project/docs")).toBe("docs");
108 });
109
110 it("only allows browse-up after entering a directory", () => {
111 expect(canNavigateUp("~/repo")).toBe(false);
112 expect(canNavigateUp("~/a")).toBe(false);
113 expect(canNavigateUp("~/repo/")).toBe(true);
114 expect(canNavigateUp("\\\\server\\share\\")).toBe(false);
115 expect(canNavigateUp("\\\\server\\share\\repo\\")).toBe(true);
116 });
117});