Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/chatSessions/claude/node/test/skillConfigLocations.spec.ts
13406 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
7
import { IConfigurationService } from '../../../../../platform/configuration/common/configurationService';
8
import { InMemoryConfigurationService } from '../../../../../platform/configuration/test/common/inMemoryConfigurationService';
9
import { SKILLS_LOCATION_KEY } from '../../../../../platform/customInstructions/common/promptTypes';
10
import { INativeEnvService } from '../../../../../platform/env/common/envService';
11
import { NullNativeEnvService } from '../../../../../platform/env/common/nullEnvService';
12
import { IWorkspaceService } from '../../../../../platform/workspace/common/workspaceService';
13
import { Event } from '../../../../../util/vs/base/common/event';
14
import { DisposableStore } from '../../../../../util/vs/base/common/lifecycle';
15
import { URI } from '../../../../../util/vs/base/common/uri';
16
import { createExtensionUnitTestingServices } from '../../../../test/node/services';
17
import { resolveSkillConfigLocations } from '../../../common/skillConfigLocations';
18
19
function createWorkspaceService(folders: URI[] = [URI.file('/workspace')]): IWorkspaceService {
20
return {
21
_serviceBrand: undefined,
22
onDidChangeWorkspaceFolders: Event.None,
23
getWorkspaceFolders: () => folders,
24
} as unknown as IWorkspaceService;
25
}
26
27
describe('resolveSkillConfigLocations', () => {
28
const disposables = new DisposableStore();
29
let baseConfigurationService: IConfigurationService;
30
31
beforeEach(() => {
32
const services = disposables.add(createExtensionUnitTestingServices());
33
const accessor = services.createTestingAccessor();
34
baseConfigurationService = accessor.get(IConfigurationService);
35
});
36
37
afterEach(() => {
38
disposables.clear();
39
});
40
41
function resolve(options?: {
42
configLocations?: Record<string, boolean>;
43
workspaceFolders?: URI[];
44
userHome?: URI;
45
}): URI[] {
46
const configService = new InMemoryConfigurationService(baseConfigurationService);
47
if (options?.configLocations) {
48
configService.setNonExtensionConfig(SKILLS_LOCATION_KEY, options.configLocations);
49
}
50
51
const envService: INativeEnvService = options?.userHome
52
? new class extends NullNativeEnvService { override get userHome() { return options.userHome!; } }()
53
: new NullNativeEnvService();
54
55
const workspaceService = createWorkspaceService(options?.workspaceFolders);
56
57
return resolveSkillConfigLocations(configService, envService, workspaceService);
58
}
59
60
it('returns empty array when no config is set', () => {
61
expect(resolve()).toEqual([]);
62
});
63
64
it('returns empty array when config is not an object', () => {
65
const configService = new InMemoryConfigurationService(baseConfigurationService);
66
configService.setNonExtensionConfig(SKILLS_LOCATION_KEY, 'not-an-object');
67
const result = resolveSkillConfigLocations(
68
configService,
69
new NullNativeEnvService(),
70
createWorkspaceService(),
71
);
72
expect(result).toEqual([]);
73
});
74
75
it('expands tilde-prefixed paths using user home directory', () => {
76
const result = resolve({
77
configLocations: { '~/my-skills': true },
78
userHome: URI.file('/home/user'),
79
});
80
expect(result).toHaveLength(1);
81
expect(result[0].path).toBe('/home/user/my-skills');
82
});
83
84
it('handles absolute paths', () => {
85
const result = resolve({
86
configLocations: { '/absolute/skills/path': true },
87
});
88
expect(result).toHaveLength(1);
89
expect(result[0].path).toBe('/absolute/skills/path');
90
});
91
92
it('joins relative paths to each workspace folder', () => {
93
const result = resolve({
94
configLocations: { 'relative/skills': true },
95
workspaceFolders: [URI.file('/workspace1'), URI.file('/workspace2')],
96
});
97
expect(result).toHaveLength(2);
98
expect(result[0].path).toBe('/workspace1/relative/skills');
99
expect(result[1].path).toBe('/workspace2/relative/skills');
100
});
101
102
it('ignores config entries with value !== true', () => {
103
const result = resolve({
104
configLocations: {
105
'/included': true,
106
'/excluded': false,
107
},
108
});
109
expect(result).toHaveLength(1);
110
expect(result[0].path).toBe('/included');
111
});
112
113
it('handles mixed path types', () => {
114
const result = resolve({
115
configLocations: {
116
'~/home-skills': true,
117
'/absolute-skills': true,
118
'relative-skills': true,
119
},
120
userHome: URI.file('/home/user'),
121
workspaceFolders: [URI.file('/workspace')],
122
});
123
expect(result).toHaveLength(3);
124
expect(result[0].path).toBe('/home/user/home-skills');
125
expect(result[1].path).toBe('/absolute-skills');
126
expect(result[2].path).toBe('/workspace/relative-skills');
127
});
128
129
it('trims whitespace from location keys', () => {
130
const result = resolve({
131
configLocations: { ' /trimmed ': true },
132
});
133
expect(result).toHaveLength(1);
134
expect(result[0].path).toBe('/trimmed');
135
});
136
});
137
138