Skip to content

Commit 1e010d3

Browse files
committed
Update path and path testing logic
1 parent 40dedcf commit 1e010d3

File tree

2 files changed

+139
-7
lines changed

2 files changed

+139
-7
lines changed

src/utils/__tests__/path.test.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,40 @@ describe("Path Utilities", () => {
2828
})
2929
})
3030

31+
describe("platform-specific behavior", () => {
32+
const platforms = ["win32", "darwin", "linux"]
33+
platforms.forEach(platform => {
34+
describe(`on ${platform}`, () => {
35+
beforeEach(() => {
36+
Object.defineProperty(process, "platform", { value: platform })
37+
})
38+
39+
it("should handle root paths correctly", () => {
40+
const root = platform === "win32" ? "C:\\" : "/"
41+
expect(arePathsEqual(root, root + "/")).toBe(true)
42+
expect(arePathsEqual(root, root + "//")).toBe(true)
43+
})
44+
45+
it("should normalize mixed separators", () => {
46+
const mixedPath = platform === "win32"
47+
? "C:\\Users/test\\path/file.txt"
48+
: "/Users/test\\path/file.txt"
49+
const normalPath = platform === "win32"
50+
? "C:\\Users\\test\\path\\file.txt"
51+
: "/Users/test/path/file.txt"
52+
expect(arePathsEqual(mixedPath, normalPath)).toBe(true)
53+
})
54+
55+
it("should handle parent directory traversal", () => {
56+
const base = platform === "win32" ? "C:\\Users\\test" : "/Users/test"
57+
const path1 = path.join(base, "dir", "..", "file.txt")
58+
const path2 = path.join(base, "file.txt")
59+
expect(arePathsEqual(path1, path2)).toBe(true)
60+
})
61+
})
62+
})
63+
})
64+
3165
describe("arePathsEqual", () => {
3266
describe("on Windows", () => {
3367
beforeEach(() => {
@@ -75,6 +109,73 @@ describe("Path Utilities", () => {
75109
})
76110
})
77111

112+
describe("Windows-specific paths", () => {
113+
beforeEach(() => {
114+
Object.defineProperty(process, "platform", { value: "win32" })
115+
})
116+
117+
it("should handle drive letter case variations", () => {
118+
expect(arePathsEqual("C:\\Users\\test", "c:\\users\\test")).toBe(true)
119+
expect(arePathsEqual("D:\\Files\\test", "d:\\files\\test")).toBe(true)
120+
})
121+
122+
it("should handle UNC paths", () => {
123+
expect(arePathsEqual(
124+
"\\\\server\\share\\folder",
125+
"\\\\SERVER\\share\\folder"
126+
)).toBe(true)
127+
expect(arePathsEqual(
128+
"\\\\server\\share\\folder\\",
129+
"\\\\server\\share\\folder"
130+
)).toBe(true)
131+
})
132+
133+
it("should handle extended-length paths", () => {
134+
const path1 = "\\\\?\\C:\\Very\\Long\\Path"
135+
const path2 = "\\\\?\\C:\\Very\\Long\\Path\\"
136+
expect(arePathsEqual(path1, path2)).toBe(true)
137+
})
138+
139+
it("should handle network drive paths", () => {
140+
expect(arePathsEqual(
141+
"Z:\\Shared\\Files",
142+
"z:\\shared\\files"
143+
)).toBe(true)
144+
})
145+
})
146+
147+
describe("path segment variations", () => {
148+
const platforms = ["win32", "darwin", "linux"]
149+
platforms.forEach(platform => {
150+
describe(`on ${platform}`, () => {
151+
beforeEach(() => {
152+
Object.defineProperty(process, "platform", { value: platform })
153+
})
154+
155+
it("should handle consecutive separators", () => {
156+
const base = platform === "win32" ? "C:" : ""
157+
const path1 = `${base}//home///user////file.txt`
158+
const path2 = `${base}/home/user/file.txt`
159+
expect(arePathsEqual(path1, path2)).toBe(true)
160+
})
161+
162+
it("should handle current directory references", () => {
163+
const base = platform === "win32" ? "C:" : ""
164+
const path1 = `${base}/./home/./user/./file.txt`
165+
const path2 = `${base}/home/user/file.txt`
166+
expect(arePathsEqual(path1, path2)).toBe(true)
167+
})
168+
169+
it("should handle complex parent directory traversal", () => {
170+
const base = platform === "win32" ? "C:" : ""
171+
const path1 = `${base}/a/b/c/../../d/../e/f/../g`
172+
const path2 = `${base}/a/e/g`
173+
expect(arePathsEqual(path1, path2)).toBe(true)
174+
})
175+
})
176+
})
177+
})
178+
78179
describe("edge cases", () => {
79180
it("should handle undefined paths", () => {
80181
expect(arePathsEqual(undefined, undefined)).toBe(true)

src/utils/path.ts

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,45 @@ export function arePathsEqual(path1?: string, path2?: string): boolean {
6868
}
6969

7070
function normalizePath(p: string): string {
71-
// normalize resolve ./.. segments, removes duplicate slashes, and standardizes path separators
72-
let normalized = path.normalize(p)
73-
// however it doesn't remove trailing slashes
74-
// remove trailing slash, except for root paths
75-
if (normalized.length > 1 && (normalized.endsWith("/") || normalized.endsWith("\\"))) {
76-
normalized = normalized.slice(0, -1)
71+
if (process.platform !== "win32") {
72+
// For POSIX platforms, handle path normalization manually
73+
// Convert all separators to forward slashes first
74+
let normalized = p.replace(/\\/g, "/")
75+
// Remove consecutive slashes except for UNC paths
76+
normalized = normalized.replace(/\/+/g, "/")
77+
// Remove trailing slash except for root
78+
if (normalized.length > 1 && normalized.endsWith("/")) {
79+
normalized = normalized.slice(0, -1)
80+
}
81+
// Resolve . and .. segments
82+
const segments = normalized.split("/")
83+
const result = []
84+
for (const segment of segments) {
85+
if (segment === "." || segment === "") {
86+
continue
87+
}
88+
if (segment === "..") {
89+
result.pop()
90+
} else {
91+
result.push(segment)
92+
}
93+
}
94+
// Reconstruct the path
95+
normalized = result.join("/")
96+
// Handle root path
97+
if (p.startsWith("/")) {
98+
normalized = "/" + normalized
99+
}
100+
return normalized
101+
} else {
102+
// For Windows, use built-in normalize
103+
let normalized = path.normalize(p)
104+
// Remove trailing slash except for root paths
105+
if (normalized.length > 1 && (normalized.endsWith("/") || normalized.endsWith("\\"))) {
106+
normalized = normalized.slice(0, -1)
107+
}
108+
return normalized
77109
}
78-
return normalized
79110
}
80111

81112
export function getReadablePath(cwd: string, relPath?: string): string {

0 commit comments

Comments
 (0)