Skip to content

Commit 8860be7

Browse files
committed
fix: update tests to handle cross-platform path resolution
1 parent 82dfd28 commit 8860be7

File tree

1 file changed

+173
-20
lines changed

1 file changed

+173
-20
lines changed

src/services/glob/__tests__/list-files.spec.ts

Lines changed: 173 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,11 @@ describe("list-files symlink support", () => {
9595

9696
mockSpawn.mockReturnValue(mockProcess as any)
9797

98+
// Use a test directory path
99+
const testDir = "/test/dir"
100+
98101
// Call listFiles to trigger ripgrep execution
99-
await listFiles("/test/dir", false, 100)
102+
await listFiles(testDir, false, 100)
100103

101104
// Verify that spawn was called with --follow flag (the critical fix)
102105
const [rgPath, args] = mockSpawn.mock.calls[0]
@@ -105,9 +108,12 @@ describe("list-files symlink support", () => {
105108
expect(args).toContain("--hidden")
106109
expect(args).toContain("--follow") // This is the critical assertion - the fix should add this flag
107110

108-
// Platform-agnostic path check - verify the last argument is the resolved path
109-
const expectedPath = path.resolve("/test/dir")
110-
expect(args[args.length - 1]).toBe(expectedPath)
111+
// Platform-agnostic path check - verify the last argument ends with the expected path
112+
const lastArg = args[args.length - 1]
113+
// On Windows, the path might be resolved to something like D:\test\dir
114+
// On Unix, it would be /test/dir
115+
// So we just check that it ends with the expected segments
116+
expect(lastArg).toMatch(/[/\\]test[/\\]dir$/)
111117
})
112118

113119
it("should include --follow flag for recursive listings too", async () => {
@@ -136,8 +142,11 @@ describe("list-files symlink support", () => {
136142

137143
mockSpawn.mockReturnValue(mockProcess as any)
138144

145+
// Use a test directory path
146+
const testDir = "/test/dir"
147+
139148
// Call listFiles with recursive=true
140-
await listFiles("/test/dir", true, 100)
149+
await listFiles(testDir, true, 100)
141150

142151
// Verify that spawn was called with --follow flag (the critical fix)
143152
const [rgPath, args] = mockSpawn.mock.calls[0]
@@ -146,9 +155,12 @@ describe("list-files symlink support", () => {
146155
expect(args).toContain("--hidden")
147156
expect(args).toContain("--follow") // This should be present in recursive mode too
148157

149-
// Platform-agnostic path check - verify the last argument is the resolved path
150-
const expectedPath = path.resolve("/test/dir")
151-
expect(args[args.length - 1]).toBe(expectedPath)
158+
// Platform-agnostic path check - verify the last argument ends with the expected path
159+
const lastArg = args[args.length - 1]
160+
// On Windows, the path might be resolved to something like D:\test\dir
161+
// On Unix, it would be /test/dir
162+
// So we just check that it ends with the expected segments
163+
expect(lastArg).toMatch(/[/\\]test[/\\]dir$/)
152164
})
153165

154166
it("should ensure first-level directories are included when limit is reached", async () => {
@@ -171,18 +183,19 @@ describe("list-files symlink support", () => {
171183
on: vi.fn((event, callback) => {
172184
if (event === "data") {
173185
// Return many file paths to trigger the limit
186+
// Note: ripgrep returns relative paths
174187
const paths =
175188
[
176-
"/test/dir/a_dir/",
177-
"/test/dir/a_dir/subdir1/",
178-
"/test/dir/a_dir/subdir1/file1.txt",
179-
"/test/dir/a_dir/subdir1/file2.txt",
180-
"/test/dir/a_dir/subdir2/",
181-
"/test/dir/a_dir/subdir2/file3.txt",
182-
"/test/dir/a_dir/file4.txt",
183-
"/test/dir/a_dir/file5.txt",
184-
"/test/dir/file1.txt",
185-
"/test/dir/file2.txt",
189+
"a_dir/",
190+
"a_dir/subdir1/",
191+
"a_dir/subdir1/file1.txt",
192+
"a_dir/subdir1/file2.txt",
193+
"a_dir/subdir2/",
194+
"a_dir/subdir2/file3.txt",
195+
"a_dir/file4.txt",
196+
"a_dir/file5.txt",
197+
"file1.txt",
198+
"file2.txt",
186199
// Note: b_dir and c_dir are missing from ripgrep output
187200
].join("\n") + "\n"
188201
setTimeout(() => callback(paths), 10)
@@ -358,9 +371,10 @@ describe("hidden directory exclusion", () => {
358371
on: vi.fn((event, callback) => {
359372
if (event === "data") {
360373
// Simulate files that should be found in .roo/temp
374+
// Note: ripgrep returns relative paths
361375
setTimeout(() => {
362-
callback(".roo/temp/teste1.md\n")
363-
callback(".roo/temp/22/test2.md\n")
376+
callback("teste1.md\n")
377+
callback("22/test2.md\n")
364378
}, 10)
365379
}
366380
}),
@@ -406,3 +420,142 @@ describe("hidden directory exclusion", () => {
406420
expect(topLevelFile).toBeTruthy()
407421
})
408422
})
423+
424+
describe("buildRecursiveArgs edge cases", () => {
425+
beforeEach(() => {
426+
vi.clearAllMocks()
427+
})
428+
429+
it("should correctly detect hidden directories with trailing slashes", async () => {
430+
const mockSpawn = vi.mocked(childProcess.spawn)
431+
const mockProcess = {
432+
stdout: {
433+
on: vi.fn((event, callback) => {
434+
if (event === "data") {
435+
setTimeout(() => callback("file.txt\n"), 10)
436+
}
437+
}),
438+
},
439+
stderr: {
440+
on: vi.fn(),
441+
},
442+
on: vi.fn((event, callback) => {
443+
if (event === "close") {
444+
setTimeout(() => callback(0), 20)
445+
}
446+
}),
447+
kill: vi.fn(),
448+
}
449+
mockSpawn.mockReturnValue(mockProcess as any)
450+
451+
// Test with trailing slash on hidden directory
452+
await listFiles("/test/.hidden/", true, 100)
453+
454+
const [rgPath, args] = mockSpawn.mock.calls[0]
455+
// When targeting a hidden directory, these flags should be present
456+
expect(args).toContain("--no-ignore-vcs")
457+
expect(args).toContain("--no-ignore")
458+
expect(args).toContain("-g")
459+
const gIndex = args.indexOf("-g")
460+
expect(args[gIndex + 1]).toBe("*")
461+
})
462+
463+
it("should correctly detect hidden directories with redundant separators", async () => {
464+
const mockSpawn = vi.mocked(childProcess.spawn)
465+
const mockProcess = {
466+
stdout: {
467+
on: vi.fn((event, callback) => {
468+
if (event === "data") {
469+
setTimeout(() => callback("file.txt\n"), 10)
470+
}
471+
}),
472+
},
473+
stderr: {
474+
on: vi.fn(),
475+
},
476+
on: vi.fn((event, callback) => {
477+
if (event === "close") {
478+
setTimeout(() => callback(0), 20)
479+
}
480+
}),
481+
kill: vi.fn(),
482+
}
483+
mockSpawn.mockReturnValue(mockProcess as any)
484+
485+
// Test with redundant separators before hidden directory
486+
await listFiles("/test//.hidden", true, 100)
487+
488+
const [rgPath, args] = mockSpawn.mock.calls[0]
489+
// When targeting a hidden directory, these flags should be present
490+
expect(args).toContain("--no-ignore-vcs")
491+
expect(args).toContain("--no-ignore")
492+
expect(args).toContain("-g")
493+
const gIndex = args.indexOf("-g")
494+
expect(args[gIndex + 1]).toBe("*")
495+
})
496+
497+
it("should correctly detect nested hidden directories with mixed separators", async () => {
498+
const mockSpawn = vi.mocked(childProcess.spawn)
499+
const mockProcess = {
500+
stdout: {
501+
on: vi.fn((event, callback) => {
502+
if (event === "data") {
503+
setTimeout(() => callback("file.txt\n"), 10)
504+
}
505+
}),
506+
},
507+
stderr: {
508+
on: vi.fn(),
509+
},
510+
on: vi.fn((event, callback) => {
511+
if (event === "close") {
512+
setTimeout(() => callback(0), 20)
513+
}
514+
}),
515+
kill: vi.fn(),
516+
}
517+
mockSpawn.mockReturnValue(mockProcess as any)
518+
519+
// Test with complex path including hidden directory
520+
await listFiles("/test//normal/.hidden//subdir/", true, 100)
521+
522+
const [rgPath, args] = mockSpawn.mock.calls[0]
523+
// When targeting a path containing a hidden directory, these flags should be present
524+
expect(args).toContain("--no-ignore-vcs")
525+
expect(args).toContain("--no-ignore")
526+
expect(args).toContain("-g")
527+
const gIndex = args.indexOf("-g")
528+
expect(args[gIndex + 1]).toBe("*")
529+
})
530+
531+
it("should not detect hidden directories when path only has dots in filenames", async () => {
532+
const mockSpawn = vi.mocked(childProcess.spawn)
533+
const mockProcess = {
534+
stdout: {
535+
on: vi.fn((event, callback) => {
536+
if (event === "data") {
537+
setTimeout(() => callback("file.txt\n"), 10)
538+
}
539+
}),
540+
},
541+
stderr: {
542+
on: vi.fn(),
543+
},
544+
on: vi.fn((event, callback) => {
545+
if (event === "close") {
546+
setTimeout(() => callback(0), 20)
547+
}
548+
}),
549+
kill: vi.fn(),
550+
}
551+
mockSpawn.mockReturnValue(mockProcess as any)
552+
553+
// Test with a path that has dots but no hidden directories
554+
await listFiles("/test/file.with.dots/normal", true, 100)
555+
556+
const [rgPath, args] = mockSpawn.mock.calls[0]
557+
// Should NOT have the special flags for hidden directories
558+
expect(args).not.toContain("--no-ignore-vcs")
559+
expect(args).not.toContain("--no-ignore")
560+
})
561+
})

0 commit comments

Comments
 (0)