Path: blob/main/extensions/copilot/src/platform/prompts/test/node/promptPathRepresentationService.spec.ts
13405 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 { beforeEach, describe, expect, it } from 'vitest';6import { Schemas } from '../../../../util/vs/base/common/network';7import { isWindows } from '../../../../util/vs/base/common/platform';8import { URI } from '../../../../util/vs/base/common/uri';9import { TestWorkspaceService } from '../../../test/node/testWorkspaceService';10import { PromptPathRepresentationService, TestPromptPathRepresentationService } from '../../common/promptPathRepresentationService';1112/**13* Subclass that simulates a Windows environment for testing Windows-specific code paths.14*/15class WindowsPromptPathRepresentationService extends PromptPathRepresentationService {16protected override isWindows() {17return true;18}19}2021describe('PromptPathRepresentationService', () => {22let workspaceService: TestWorkspaceService;23let service: PromptPathRepresentationService;2425beforeEach(() => {26workspaceService = new TestWorkspaceService();27service = new PromptPathRepresentationService(workspaceService);28});2930describe('getFilePath', () => {31it('returns fsPath for file scheme URIs', () => {32const uri = URI.file('/home/user/project/file.ts');33expect(service.getFilePath(uri)).toBe(uri.fsPath);34});3536it('returns fsPath for vscode-remote scheme URIs', () => {37const uri = URI.from({ scheme: Schemas.vscodeRemote, path: '/home/user/project/file.ts', authority: 'ssh-remote+myhost' });38expect(service.getFilePath(uri)).toBe(uri.fsPath);39});4041it('returns toString for other schemes', () => {42const uri = URI.from({ scheme: 'untitled', path: '/Untitled-1' });43expect(service.getFilePath(uri)).toBe(uri.toString());44});4546it('returns toString for virtual filesystem schemes', () => {47const uri = URI.from({ scheme: 'vscode-vfs', authority: 'github', path: '/owner/repo/file.ts' });48expect(service.getFilePath(uri)).toBe(uri.toString());49});50});5152describe('resolveFilePath', () => {53it('resolves posix absolute paths to file URIs', () => {54const result = service.resolveFilePath('/home/user/project/file.ts');55expect(result).toBeDefined();56expect(result!.scheme).toBe(Schemas.file);57expect(result!.path).toBe('/home/user/project/file.ts');58});5960it('resolves posix paths with a custom predominant scheme', () => {61const result = service.resolveFilePath('/home/user/project/file.ts', Schemas.vscodeRemote);62expect(result).toBeDefined();63expect(result!.scheme).toBe(Schemas.vscodeRemote);64expect(result!.path).toBe('/home/user/project/file.ts');65});6667it('parses valid URI strings', () => {68const result = service.resolveFilePath('https://example.com/path');69expect(result).toBeDefined();70expect(result!.scheme).toBe('https');71});7273it('parses file:// URI strings', () => {74const result = service.resolveFilePath('file:///home/user/file.ts');75expect(result).toBeDefined();76expect(result!.scheme).toBe(Schemas.file);77expect(result!.path).toBe('/home/user/file.ts');78});7980it('parses vscode-vfs URI strings', () => {81const result = service.resolveFilePath('vscode-vfs://github/owner/repo/file.ts');82expect(result).toBeDefined();83expect(result!.scheme).toBe('vscode-vfs');84expect(result!.path).toBe('/owner/repo/file.ts');85});8687it('returns undefined for relative paths', () => {88expect(service.resolveFilePath('src/file.ts')).toBeUndefined();89});9091it('returns undefined for empty strings', () => {92expect(service.resolveFilePath('')).toBeUndefined();93});9495it('returns undefined for plain text without scheme', () => {96expect(service.resolveFilePath('just some text')).toBeUndefined();97});9899it('roundtrips posix file URIs through getFilePath and resolveFilePath', () => {100const original = URI.file('/home/user/project/file.ts');101const filepath = service.getFilePath(original);102const resolved = service.resolveFilePath(filepath);103expect(resolved).toBeDefined();104expect(resolved!.path).toBe(original.path);105});106});107108describe.skipIf(!isWindows)('resolveFilePath (Windows simulation)', () => {109let windowsService: WindowsPromptPathRepresentationService;110111beforeEach(() => {112workspaceService = new TestWorkspaceService();113windowsService = new WindowsPromptPathRepresentationService(workspaceService);114});115116it('resolves Windows drive-letter paths', () => {117const result = windowsService.resolveFilePath('C:\\Users\\user\\file.ts');118expect(result).toBeDefined();119expect(result!.scheme).toBe(Schemas.file);120expect(result!.path).toBe('/C:/Users/user/file.ts');121});122123it('collapses double-escaped backslashes', () => {124const result = windowsService.resolveFilePath('C:\\\\Users\\\\user\\\\file.ts');125expect(result).toBeDefined();126expect(result!.path).toBe('/C:/Users/user/file.ts');127});128129it('preserves UNC paths while collapsing extra backslashes', () => {130const result = windowsService.resolveFilePath('\\\\server\\share\\\\file.ts');131expect(result).toBeDefined();132// UNC paths get their leading \\ preserved133expect(result?.toString()).toContain('server');134expect(result?.toString()).toContain('share');135});136137it('resolves backslash-only path as Windows path', () => {138const result = windowsService.resolveFilePath('\\Users\\file.ts');139expect(result).toBeDefined();140expect(result!.scheme).toBe(Schemas.file);141});142143it('prepends drive letter for posix paths on Windows when workspace folders exist', () => {144const folderUri = URI.file('D:/Projects/myapp');145workspaceService = new TestWorkspaceService([folderUri]);146windowsService = new WindowsPromptPathRepresentationService(workspaceService);147148const result = windowsService.resolveFilePath('/Projects/myapp/src/file.ts');149expect(result).toBeDefined();150expect(result!.scheme).toBe(Schemas.file);151// Should have prepended the drive letter from workspace folder152expect(result!.path).toMatch(/^\/d:/i);153});154155it('does not prepend drive letter for posix paths on Windows when workspace folders don\'t exist', () => {156const folderUri = URI.file('D:/Projects/myapp2');157workspaceService = new TestWorkspaceService([folderUri]);158windowsService = new WindowsPromptPathRepresentationService(workspaceService);159160const result = windowsService.resolveFilePath('/Projects/myapp/src/file.ts');161expect(result).toBeDefined();162expect(result!.scheme).toBe(Schemas.file);163// Should not have prepended the drive letter since workspace folders don't match164expect(result!.path).toBe('/Projects/myapp/src/file.ts');165});166167it('does not prepend drive letter for posix paths when predominant scheme is not file', () => {168const folderUri = URI.file('C:/Projects');169workspaceService = new TestWorkspaceService([folderUri]);170windowsService = new WindowsPromptPathRepresentationService(workspaceService);171172const result = windowsService.resolveFilePath('/Projects/file.ts', Schemas.vscodeRemote);173expect(result).toBeDefined();174expect(result!.scheme).toBe(Schemas.vscodeRemote);175// Should not have a drive letter since predominantScheme is not file176expect(result!.path).toBe('/Projects/file.ts');177});178179it('resolves posix paths without drive letter prepended when no workspace folders', () => {180const result = windowsService.resolveFilePath('/home/user/file.ts');181expect(result).toBeDefined();182// No workspace folders, so no drive letter rectification183expect(result!.path).toBe('/home/user/file.ts');184});185});186187describe('getExampleFilePath', () => {188it.skipIf(isWindows)('returns a posix-style file path on non-Windows', () => {189const result = service.getExampleFilePath('/workspace/src/file.ts');190expect(result).toContain('/workspace/src/file.ts');191});192193it.skipIf(!isWindows)('returns a Windows-style path when on Windows', () => {194workspaceService = new TestWorkspaceService();195const windowsService = new WindowsPromptPathRepresentationService(workspaceService);196const result = windowsService.getExampleFilePath('/workspace/src/file.ts');197// On Windows, the example path should include c: drive letter (URI.file lowercases it)198expect(result.toLowerCase()).toContain('c:');199expect(result).toContain('workspace');200});201});202});203204describe('TestPromptPathRepresentationService', () => {205let workspaceService: TestWorkspaceService;206let service: TestPromptPathRepresentationService;207208beforeEach(() => {209workspaceService = new TestWorkspaceService();210service = new TestPromptPathRepresentationService(workspaceService);211});212213describe('getFilePath', () => {214it('returns posix path for file scheme URIs (not fsPath)', () => {215const uri = URI.file('/home/user/project/file.ts');216expect(service.getFilePath(uri)).toBe(uri.path);217});218219it('returns posix path for vscode-remote scheme URIs', () => {220const uri = URI.from({ scheme: Schemas.vscodeRemote, path: '/home/user/project/file.ts', authority: 'ssh-remote+myhost' });221expect(service.getFilePath(uri)).toBe(uri.path);222});223224it('returns toString for other schemes', () => {225const uri = URI.from({ scheme: 'untitled', path: '/Untitled-1' });226expect(service.getFilePath(uri)).toBe(uri.toString());227});228});229230describe('getExampleFilePath', () => {231it('always returns posix path regardless of platform', () => {232const result = service.getExampleFilePath('/workspace/src/file.ts');233expect(result).toBe('/workspace/src/file.ts');234});235});236});237238239