Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/test/getSelection.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 { beforeEach, describe, expect, it, vi } from 'vitest';
7
import { TestLogService } from '../../../../../platform/testing/common/testLogService';
8
import { MockMcpServer, parseToolResult, createMockEditor } from './testHelpers';
9
10
const { mockActiveTextEditor } = vi.hoisted(() => ({
11
mockActiveTextEditor: { value: null as unknown },
12
}));
13
14
vi.mock('vscode', () => ({
15
window: {
16
get activeTextEditor() { return mockActiveTextEditor.value; },
17
},
18
}));
19
20
import { registerGetSelectionTool, SelectionState, getSelectionInfo } from '../tools/getSelection';
21
22
interface SelectionResult {
23
text: string;
24
filePath: string;
25
fileUrl: string;
26
current: boolean;
27
selection: {
28
start: { line: number; character: number };
29
end: { line: number; character: number };
30
isEmpty: boolean;
31
};
32
}
33
34
describe('getSelectionInfo', () => {
35
it('should return selection info for a text selection', () => {
36
const editor = createMockEditor('/test/file.ts', 'Hello World', 0, 6, 0, 11);
37
const info = getSelectionInfo(editor as unknown as import('vscode').TextEditor);
38
39
expect(info.text).toBe('World');
40
expect(info.filePath).toBe('/test/file.ts');
41
expect(info.fileUrl).toBe('file:///test/file.ts');
42
expect(info.selection.start.line).toBe(0);
43
expect(info.selection.start.character).toBe(6);
44
expect(info.selection.end.line).toBe(0);
45
expect(info.selection.end.character).toBe(11);
46
expect(info.selection.isEmpty).toBe(false);
47
});
48
49
it('should return empty text for cursor position', () => {
50
const editor = createMockEditor('/test/file.ts', 'Hello World', 0, 5, 0, 5);
51
const info = getSelectionInfo(editor as unknown as import('vscode').TextEditor);
52
53
expect(info.text).toBe('');
54
expect(info.selection.isEmpty).toBe(true);
55
});
56
57
it('should handle multi-line selection', () => {
58
const editor = createMockEditor('/test/file.ts', 'Line one\nLine two\nLine three', 0, 5, 2, 4);
59
const info = getSelectionInfo(editor as unknown as import('vscode').TextEditor);
60
61
expect(info.text).toBe('one\nLine two\nLine');
62
expect(info.selection.start.line).toBe(0);
63
expect(info.selection.end.line).toBe(2);
64
});
65
});
66
67
describe('SelectionState', () => {
68
it('should start with no selection', () => {
69
const state = new SelectionState();
70
expect(state.latest).toBe(null);
71
});
72
73
it('should store and retrieve selection', () => {
74
const state = new SelectionState();
75
const selectionInfo = {
76
text: 'Hello',
77
filePath: '/test/file.ts',
78
fileUrl: 'file:///test/file.ts',
79
selection: {
80
start: { line: 0, character: 0 },
81
end: { line: 0, character: 5 },
82
isEmpty: false,
83
},
84
};
85
86
state.update(selectionInfo);
87
expect(state.latest).toBe(selectionInfo);
88
});
89
90
it('should clear selection with null', () => {
91
const state = new SelectionState();
92
state.update({
93
text: 'Hello',
94
filePath: '/test/file.ts',
95
fileUrl: 'file:///test/file.ts',
96
selection: {
97
start: { line: 0, character: 0 },
98
end: { line: 0, character: 5 },
99
isEmpty: false,
100
},
101
});
102
state.update(null);
103
expect(state.latest).toBe(null);
104
});
105
});
106
107
describe('get_selection tool', () => {
108
const logger = new TestLogService();
109
let selectionState: SelectionState;
110
let server: MockMcpServer;
111
112
beforeEach(() => {
113
selectionState = new SelectionState();
114
server = new MockMcpServer();
115
mockActiveTextEditor.value = null;
116
registerGetSelectionTool(server as unknown as import('@modelcontextprotocol/sdk/server/mcp.js').McpServer, logger, selectionState);
117
});
118
119
it('should register the get_selection tool', () => {
120
expect(server.hasToolRegistered('get_selection')).toBe(true);
121
});
122
123
it('should return null when no editor and no cached selection', async () => {
124
const handler = server.getToolHandler('get_selection')!;
125
const result = parseToolResult(await handler({}));
126
expect(result).toBe(null);
127
});
128
129
it('should return cached selection with current=false when no active editor', async () => {
130
selectionState.update({
131
text: 'cached text',
132
filePath: '/cached/file.ts',
133
fileUrl: 'file:///cached/file.ts',
134
selection: {
135
start: { line: 0, character: 0 },
136
end: { line: 0, character: 11 },
137
isEmpty: false,
138
},
139
});
140
141
const handler = server.getToolHandler('get_selection')!;
142
const result = parseToolResult<SelectionResult>(await handler({}));
143
144
expect(result.text).toBe('cached text');
145
expect(result.current).toBe(false);
146
expect(result.filePath).toBe('/cached/file.ts');
147
});
148
149
it('should return current selection with current=true when editor is active', async () => {
150
mockActiveTextEditor.value = createMockEditor('/test/file.ts', 'Hello World', 0, 0, 0, 5);
151
152
const handler = server.getToolHandler('get_selection')!;
153
const result = parseToolResult<SelectionResult>(await handler({}));
154
155
expect(result.text).toBe('Hello');
156
expect(result.current).toBe(true);
157
});
158
159
it('should return empty text for cursor position with current=true', async () => {
160
mockActiveTextEditor.value = createMockEditor('/test/file.ts', 'Hello World', 0, 5, 0, 5);
161
162
const handler = server.getToolHandler('get_selection')!;
163
const result = parseToolResult<SelectionResult>(await handler({}));
164
165
expect(result.text).toBe('');
166
expect(result.current).toBe(true);
167
expect(result.selection.isEmpty).toBe(true);
168
});
169
});
170
171