Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions src/__mocks__/services/ripgrep/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Mock implementation for the ripgrep service
*
* This mock provides stable implementations of all ripgrep service functions,
* making sure to handle undefined values safely to prevent test failures.
* Each function is documented with its purpose and behavior in tests.
*/

/**
* Mock implementation of getBinPath
* Always returns a valid path to avoid path resolution errors in tests
*
* @param vscodeAppRoot - Optional VSCode app root path (can be undefined)
* @returns Promise resolving to a mock path to the ripgrep binary
*/
export const getBinPath = jest.fn().mockImplementation(async (vscodeAppRoot?: string): Promise<string> => {
return "/mock/path/to/rg"
})

/**
* Mock implementation of regexSearchFiles
* Always returns a static search result string to avoid executing real searches
*
* @param cwd - Optional working directory (can be undefined)
* @param directoryPath - Optional directory to search (can be undefined)
* @param regex - Optional regex pattern (can be undefined)
* @param filePattern - Optional file pattern (can be undefined)
* @returns Promise resolving to a mock search result
*/
export const regexSearchFiles = jest
.fn()
.mockImplementation(
async (cwd?: string, directoryPath?: string, regex?: string, filePattern?: string): Promise<string> => {
return "Mock search results"
},
)

/**
* Mock implementation of truncateLine
* Returns the input line or empty string if undefined
*
* @param line - The line to truncate (can be undefined)
* @param maxLength - Optional maximum length (can be undefined)
* @returns The original line or empty string if undefined
*/
export const truncateLine = jest.fn().mockImplementation((line?: string, maxLength?: number): string => {
return line || ""
})
39 changes: 39 additions & 0 deletions src/core/__mocks__/mock-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Mock setup for Cline tests
*
* This file contains centralized mock configurations for services
* that require special handling in tests. It prevents test failures
* related to undefined values, missing dependencies, or filesystem access.
*
* Services mocked here:
* - ripgrep: Prevents path.join issues with undefined parameters
* - list-files: Prevents dependency on actual ripgrep binary
*/

/**
* Mock the ripgrep service
* This prevents issues with path.join and undefined parameters in tests
*/
jest.mock("../../services/ripgrep", () => ({
// Always returns a valid path to the ripgrep binary
getBinPath: jest.fn().mockResolvedValue("/mock/path/to/rg"),

// Returns static search results
regexSearchFiles: jest.fn().mockResolvedValue("Mock search results"),

// Safe implementation of truncateLine that handles edge cases
truncateLine: jest.fn().mockImplementation((line: string) => line || ""),
}))

/**
* Mock the list-files module
* This prevents dependency on the ripgrep binary and filesystem access
*/
jest.mock("../../services/glob/list-files", () => ({
// Returns empty file list with boolean flag indicating if limit was reached
listFiles: jest.fn().mockImplementation(() => {
return Promise.resolve([[], false])
}),
}))

export {}
24 changes: 24 additions & 0 deletions src/core/__tests__/Cline.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,31 @@ describe("Cline", () => {
})

describe("API conversation handling", () => {
/**
* Mock environment details retrieval to avoid filesystem access in tests
*
* This setup:
* 1. Prevents file listing operations that might cause test instability
* 2. Preserves test-specific mocks when they exist (via _mockGetEnvironmentDetails)
* 3. Provides a stable, empty environment by default
*/
beforeEach(() => {
// Mock the method with a stable implementation
jest.spyOn(Cline.prototype, "getEnvironmentDetails").mockImplementation(
// Use 'any' type to allow for dynamic test properties
async function (this: any, verbose: boolean = false): Promise<string> {
// Use test-specific mock if available
if (this._mockGetEnvironmentDetails) {
return this._mockGetEnvironmentDetails()
}
// Default to empty environment details for stability
return ""
},
)
})

it("should clean conversation history before sending to API", async () => {
// Cline.create will now use our mocked getEnvironmentDetails
const [cline, task] = Cline.create({
provider: mockProvider,
apiConfiguration: mockApiConfig,
Expand Down
70 changes: 70 additions & 0 deletions src/services/glob/__mocks__/list-files.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* Mock implementation of list-files module
*
* IMPORTANT NOTES:
* 1. This file must be placed in src/services/glob/__mocks__/ to properly mock the module
* 2. DO NOT IMPORT any modules from the application code to avoid circular dependencies
* 3. All dependencies are mocked/stubbed locally for isolation
*
* This implementation provides predictable behavior for tests without requiring
* actual filesystem access or ripgrep binary.
*/

/**
* Mock function for path resolving without importing path module
* Provides basic path resolution for testing
*
* @param dirPath - Directory path to resolve
* @returns Absolute mock path
*/
const mockResolve = (dirPath: string): string => {
return dirPath.startsWith("/") ? dirPath : `/mock/path/${dirPath}`
}

/**
* Mock function to check if paths are equal without importing path module
* Provides simple equality comparison for testing
*
* @param path1 - First path to compare
* @param path2 - Second path to compare
* @returns Whether paths are equal
*/
const mockArePathsEqual = (path1: string, path2: string): boolean => {
return path1 === path2
}

/**
* Mock implementation of listFiles function
* Returns different results based on input path for testing different scenarios
*
* @param dirPath - Directory path to list files from
* @param recursive - Whether to list files recursively
* @param limit - Maximum number of files to return
* @returns Promise resolving to [file paths, limit reached flag]
*/
export const listFiles = jest.fn((dirPath: string, recursive: boolean, limit: number) => {
// Special case: Root or home directories
// Prevents tests from trying to list all files in these directories
if (dirPath === "/" || dirPath === "/root" || dirPath === "/home/user") {
return Promise.resolve([[dirPath], false])
}

// Special case: Tree-sitter tests
// Some tests expect the second value to be a Set instead of a boolean
if (dirPath.includes("test/path")) {
return Promise.resolve([[], new Set()])
}

// Special case: For testing directories with actual content
if (dirPath.includes("mock/content")) {
const mockFiles = [
`${mockResolve(dirPath)}/file1.txt`,
`${mockResolve(dirPath)}/file2.js`,
`${mockResolve(dirPath)}/folder1/`,
]
return Promise.resolve([mockFiles, false])
}

// Default case: Return empty list for most tests
return Promise.resolve([[], false])
})
Loading