Path: blob/main/extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/test/getSelection.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 { beforeEach, describe, expect, it, vi } from 'vitest';6import { TestLogService } from '../../../../../platform/testing/common/testLogService';7import { MockMcpServer, parseToolResult, createMockEditor } from './testHelpers';89const { mockActiveTextEditor } = vi.hoisted(() => ({10mockActiveTextEditor: { value: null as unknown },11}));1213vi.mock('vscode', () => ({14window: {15get activeTextEditor() { return mockActiveTextEditor.value; },16},17}));1819import { registerGetSelectionTool, SelectionState, getSelectionInfo } from '../tools/getSelection';2021interface SelectionResult {22text: string;23filePath: string;24fileUrl: string;25current: boolean;26selection: {27start: { line: number; character: number };28end: { line: number; character: number };29isEmpty: boolean;30};31}3233describe('getSelectionInfo', () => {34it('should return selection info for a text selection', () => {35const editor = createMockEditor('/test/file.ts', 'Hello World', 0, 6, 0, 11);36const info = getSelectionInfo(editor as unknown as import('vscode').TextEditor);3738expect(info.text).toBe('World');39expect(info.filePath).toBe('/test/file.ts');40expect(info.fileUrl).toBe('file:///test/file.ts');41expect(info.selection.start.line).toBe(0);42expect(info.selection.start.character).toBe(6);43expect(info.selection.end.line).toBe(0);44expect(info.selection.end.character).toBe(11);45expect(info.selection.isEmpty).toBe(false);46});4748it('should return empty text for cursor position', () => {49const editor = createMockEditor('/test/file.ts', 'Hello World', 0, 5, 0, 5);50const info = getSelectionInfo(editor as unknown as import('vscode').TextEditor);5152expect(info.text).toBe('');53expect(info.selection.isEmpty).toBe(true);54});5556it('should handle multi-line selection', () => {57const editor = createMockEditor('/test/file.ts', 'Line one\nLine two\nLine three', 0, 5, 2, 4);58const info = getSelectionInfo(editor as unknown as import('vscode').TextEditor);5960expect(info.text).toBe('one\nLine two\nLine');61expect(info.selection.start.line).toBe(0);62expect(info.selection.end.line).toBe(2);63});64});6566describe('SelectionState', () => {67it('should start with no selection', () => {68const state = new SelectionState();69expect(state.latest).toBe(null);70});7172it('should store and retrieve selection', () => {73const state = new SelectionState();74const selectionInfo = {75text: 'Hello',76filePath: '/test/file.ts',77fileUrl: 'file:///test/file.ts',78selection: {79start: { line: 0, character: 0 },80end: { line: 0, character: 5 },81isEmpty: false,82},83};8485state.update(selectionInfo);86expect(state.latest).toBe(selectionInfo);87});8889it('should clear selection with null', () => {90const state = new SelectionState();91state.update({92text: 'Hello',93filePath: '/test/file.ts',94fileUrl: 'file:///test/file.ts',95selection: {96start: { line: 0, character: 0 },97end: { line: 0, character: 5 },98isEmpty: false,99},100});101state.update(null);102expect(state.latest).toBe(null);103});104});105106describe('get_selection tool', () => {107const logger = new TestLogService();108let selectionState: SelectionState;109let server: MockMcpServer;110111beforeEach(() => {112selectionState = new SelectionState();113server = new MockMcpServer();114mockActiveTextEditor.value = null;115registerGetSelectionTool(server as unknown as import('@modelcontextprotocol/sdk/server/mcp.js').McpServer, logger, selectionState);116});117118it('should register the get_selection tool', () => {119expect(server.hasToolRegistered('get_selection')).toBe(true);120});121122it('should return null when no editor and no cached selection', async () => {123const handler = server.getToolHandler('get_selection')!;124const result = parseToolResult(await handler({}));125expect(result).toBe(null);126});127128it('should return cached selection with current=false when no active editor', async () => {129selectionState.update({130text: 'cached text',131filePath: '/cached/file.ts',132fileUrl: 'file:///cached/file.ts',133selection: {134start: { line: 0, character: 0 },135end: { line: 0, character: 11 },136isEmpty: false,137},138});139140const handler = server.getToolHandler('get_selection')!;141const result = parseToolResult<SelectionResult>(await handler({}));142143expect(result.text).toBe('cached text');144expect(result.current).toBe(false);145expect(result.filePath).toBe('/cached/file.ts');146});147148it('should return current selection with current=true when editor is active', async () => {149mockActiveTextEditor.value = createMockEditor('/test/file.ts', 'Hello World', 0, 0, 0, 5);150151const handler = server.getToolHandler('get_selection')!;152const result = parseToolResult<SelectionResult>(await handler({}));153154expect(result.text).toBe('Hello');155expect(result.current).toBe(true);156});157158it('should return empty text for cursor position with current=true', async () => {159mockActiveTextEditor.value = createMockEditor('/test/file.ts', 'Hello World', 0, 5, 0, 5);160161const handler = server.getToolHandler('get_selection')!;162const result = parseToolResult<SelectionResult>(await handler({}));163164expect(result.text).toBe('');165expect(result.current).toBe(true);166expect(result.selection.isEmpty).toBe(true);167});168});169170171