Path: blob/main/extensions/copilot/src/extension/chatSessions/claude/node/test/claudeProjectFolders.spec.ts
13406 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { describe, expect, it } from 'vitest';6import { TestWorkspaceService } from '../../../../../platform/test/node/testWorkspaceService';7import { URI } from '../../../../../util/vs/base/common/uri';8import { FolderRepositoryMRUEntry, IFolderRepositoryManager } from '../../../../chatSessions/common/folderRepositoryManager';9import { computeFolderSlug, getProjectFolders } from '../claudeProjectFolders';1011// #region MockFolderRepositoryManager1213class MockFolderRepositoryManager implements IFolderRepositoryManager {14declare _serviceBrand: undefined;15private _mruEntries: FolderRepositoryMRUEntry[] = [];1617setMRUEntries(entries: FolderRepositoryMRUEntry[]): void {18this._mruEntries = entries;19}2021setNewSessionFolder(): void { }22deleteNewSessionFolder(): void { }23async getFolderRepository(): Promise<{ folder: undefined; repository: undefined; worktree: undefined; worktreeProperties: undefined; trusted: undefined }> { return { folder: undefined, repository: undefined, worktree: undefined, worktreeProperties: undefined, trusted: undefined }; }24async initializeFolderRepository(): Promise<{ folder: undefined; repository: undefined; worktree: undefined; worktreeProperties: undefined; trusted: undefined }> { return { folder: undefined, repository: undefined, worktree: undefined, worktreeProperties: undefined, trusted: undefined }; }25async initializeMultiRootFolderRepositories(): Promise<{ primary: { folder: undefined; repository: undefined; worktree: undefined; worktreeProperties: undefined; trusted: undefined }; additional: never[] }> { return { primary: { folder: undefined, repository: undefined, worktree: undefined, worktreeProperties: undefined, trusted: undefined }, additional: [] }; }26async getRepositoryInfo(): Promise<any> { return undefined; }27async getFolderMRU(): Promise<FolderRepositoryMRUEntry[]> { return this._mruEntries; }28}2930// #endregion3132describe('computeFolderSlug', () => {33it('converts a simple Unix path', () => {34const uri = URI.file('/Users/test/project');35expect(computeFolderSlug(uri)).toBe('-Users-test-project');36});3738it('converts a path with spaces', () => {39const uri = URI.file('/Users/test/my project');40expect(computeFolderSlug(uri)).toBe('-Users-test-my-project');41});4243it('converts a path with dots', () => {44const uri = URI.file('/Users/test/my.project');45expect(computeFolderSlug(uri)).toBe('-Users-test-my-project');46});4748it('converts a Windows-style drive letter path', () => {49// On Windows, URI.file('C:/Users/test/project') produces path '/c:/Users/test/project'50const uri = URI.from({ scheme: 'file', path: '/c:/Users/test/project' });51expect(computeFolderSlug(uri)).toBe('C--Users-test-project');52});5354it('uppercases the Windows drive letter', () => {55const uri = URI.from({ scheme: 'file', path: '/d:/projects/my-app' });56expect(computeFolderSlug(uri)).toBe('D--projects-my-app');57});58});5960describe('getProjectFolders', () => {61it('returns slugs for single-root workspace', async () => {62const folderUri = URI.file('/Users/test/project');63const workspace = new TestWorkspaceService([folderUri]);64const folderManager = new MockFolderRepositoryManager();6566const result = await getProjectFolders(workspace, folderManager);6768expect(result).toHaveLength(1);69expect(result[0].folderUri.toString()).toBe(folderUri.toString());70expect(result[0].slug).toBe(computeFolderSlug(folderUri));71});7273it('returns slugs for all folders in a multi-root workspace', async () => {74const folderA = URI.file('/Users/test/project-a');75const folderB = URI.file('/Users/test/project-b');76const workspace = new TestWorkspaceService([folderA, folderB]);77const folderManager = new MockFolderRepositoryManager();7879const result = await getProjectFolders(workspace, folderManager);8081expect(result).toHaveLength(2);82expect(result[0].folderUri.toString()).toBe(folderA.toString());83expect(result[0].slug).toBe(computeFolderSlug(folderA));84expect(result[1].folderUri.toString()).toBe(folderB.toString());85expect(result[1].slug).toBe(computeFolderSlug(folderB));86});8788it('falls back to MRU entries for an empty workspace', async () => {89const mruFolder = URI.file('/Users/test/recent-project');90const workspace = new TestWorkspaceService([]);91const folderManager = new MockFolderRepositoryManager();92folderManager.setMRUEntries([{ folder: mruFolder, repository: undefined, lastAccessed: Date.now() }]);9394const result = await getProjectFolders(workspace, folderManager);9596expect(result).toHaveLength(1);97expect(result[0].folderUri.toString()).toBe(mruFolder.toString());98expect(result[0].slug).toBe(computeFolderSlug(mruFolder));99});100101it('returns empty array for empty workspace with no MRU entries', async () => {102const workspace = new TestWorkspaceService([]);103const folderManager = new MockFolderRepositoryManager();104105const result = await getProjectFolders(workspace, folderManager);106107expect(result).toHaveLength(0);108});109110it('workspace folders take priority over MRU entries', async () => {111const workspaceFolder = URI.file('/Users/test/workspace');112const mruFolder = URI.file('/Users/test/mru-folder');113const workspace = new TestWorkspaceService([workspaceFolder]);114const folderManager = new MockFolderRepositoryManager();115folderManager.setMRUEntries([{ folder: mruFolder, repository: undefined, lastAccessed: Date.now() }]);116117const result = await getProjectFolders(workspace, folderManager);118119expect(result).toHaveLength(1);120expect(result[0].folderUri.toString()).toBe(workspaceFolder.toString());121});122});123124125