Path: blob/main/extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/test/addSelection.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 type { InProcHttpServer } from '../inProcHttpServer';8import { MockHttpServer, MockSessionTracker, createMockEditor, createMockEditorWithScheme } from './testHelpers';910const { mockRegisterCommand, mockActiveTextEditor, mockShowQuickPick } = vi.hoisted(() => ({11mockRegisterCommand: vi.fn(),12mockActiveTextEditor: { value: null as unknown },13mockShowQuickPick: vi.fn(),14}));1516vi.mock('vscode', () => ({17window: {18get activeTextEditor() { return mockActiveTextEditor.value; },19showWarningMessage: vi.fn(),20showQuickPick: (...args: unknown[]) => mockShowQuickPick(...args),21},22commands: {23registerCommand: (...args: unknown[]) => mockRegisterCommand(...args),24},25}));2627import * as vscode from 'vscode';28import { ADD_SELECTION_COMMAND, registerAddSelectionCommand } from '../commands/addSelection';29import { ADD_FILE_REFERENCE_NOTIFICATION } from '../commands/sendContext';3031describe('addSelection command', () => {32const logger = new TestLogService();33let httpServer: MockHttpServer;34let sessionTracker: MockSessionTracker;35let registeredCommands: Map<string, (...args: unknown[]) => unknown>;3637beforeEach(() => {38vi.clearAllMocks();39httpServer = new MockHttpServer();40sessionTracker = new MockSessionTracker();41registeredCommands = new Map();42mockActiveTextEditor.value = null;4344// Default: one connected session45httpServer.setConnectedSessionIds(['session-1']);4647mockRegisterCommand.mockImplementation((name: string, callback: (...args: unknown[]) => unknown) => {48registeredCommands.set(name, callback);49return { dispose: () => { } };50});51});5253it('should register the command', () => {54registerAddSelectionCommand(logger, httpServer as unknown as InProcHttpServer, sessionTracker.asTracker());55expect(registeredCommands.has(ADD_SELECTION_COMMAND)).toBe(true);56});5758it('should send selection notification from active editor with selection', async () => {59mockActiveTextEditor.value = createMockEditor('/test/file.ts', 'line 0\nline 1\nline 2', 1, 0, 1, 6);6061registerAddSelectionCommand(logger, httpServer as unknown as InProcHttpServer, sessionTracker.asTracker());62await registeredCommands.get(ADD_SELECTION_COMMAND)!();6364expect(httpServer.sendNotification).toHaveBeenCalledWith(65'session-1',66ADD_FILE_REFERENCE_NOTIFICATION,67expect.objectContaining({68filePath: '/test/file.ts',69selection: {70start: { line: 1, character: 0 },71end: { line: 1, character: 6 },72},73selectedText: 'line 1',74}),75);76});7778it('should send context with null selection when no text is selected (fallback to file)', async () => {79mockActiveTextEditor.value = createMockEditor('/test/file.ts', 'Hello World', 0, 0, 0, 0);8081registerAddSelectionCommand(logger, httpServer as unknown as InProcHttpServer, sessionTracker.asTracker());82await registeredCommands.get(ADD_SELECTION_COMMAND)!();8384expect(httpServer.sendNotification).toHaveBeenCalledWith(85'session-1',86ADD_FILE_REFERENCE_NOTIFICATION,87expect.objectContaining({88filePath: '/test/file.ts',89selection: null,90selectedText: null,91}),92);93});9495it('should show warning when no active editor', async () => {96registerAddSelectionCommand(logger, httpServer as unknown as InProcHttpServer, sessionTracker.asTracker());97await registeredCommands.get(ADD_SELECTION_COMMAND)!();9899expect(httpServer.sendNotification).not.toHaveBeenCalled();100expect(vscode.window.showWarningMessage).toHaveBeenCalledWith(101'No active editor. Open a file to add a reference.',102);103});104105it('should show warning when no sessions are connected', async () => {106httpServer.setConnectedSessionIds([]);107mockActiveTextEditor.value = createMockEditor('/test/file.ts', 'content', 0, 0, 0, 0);108109registerAddSelectionCommand(logger, httpServer as unknown as InProcHttpServer, sessionTracker.asTracker());110await registeredCommands.get(ADD_SELECTION_COMMAND)!();111112expect(httpServer.sendNotification).not.toHaveBeenCalled();113expect(vscode.window.showWarningMessage).toHaveBeenCalledWith(114'No Copilot CLI sessions are connected.',115);116});117118it('should show picker when multiple sessions are connected', async () => {119httpServer.setConnectedSessionIds(['session-1', 'session-2']);120mockShowQuickPick.mockResolvedValue({ sessionId: 'session-2', label: 'session-2' });121mockActiveTextEditor.value = createMockEditor('/test/file.ts', 'content', 0, 0, 0, 7);122123registerAddSelectionCommand(logger, httpServer as unknown as InProcHttpServer, sessionTracker.asTracker());124await registeredCommands.get(ADD_SELECTION_COMMAND)!();125126expect(mockShowQuickPick).toHaveBeenCalled();127expect(httpServer.sendNotification).toHaveBeenCalledWith(128'session-2',129ADD_FILE_REFERENCE_NOTIFICATION,130expect.objectContaining({ filePath: '/test/file.ts' }),131);132});133134describe('URI scheme validation', () => {135it('should allow file scheme', async () => {136mockActiveTextEditor.value = createMockEditorWithScheme('/test/file.ts', 'content', 0, 0, 0, 7, 'file');137138registerAddSelectionCommand(logger, httpServer as unknown as InProcHttpServer, sessionTracker.asTracker());139await registeredCommands.get(ADD_SELECTION_COMMAND)!();140141expect(httpServer.sendNotification).toHaveBeenCalled();142});143144it('should reject vscode-remote scheme with warning', async () => {145mockActiveTextEditor.value = createMockEditorWithScheme('/test/file.ts', 'content', 0, 0, 0, 7, 'vscode-remote');146147registerAddSelectionCommand(logger, httpServer as unknown as InProcHttpServer, sessionTracker.asTracker());148await registeredCommands.get(ADD_SELECTION_COMMAND)!();149150expect(httpServer.sendNotification).not.toHaveBeenCalled();151expect(vscode.window.showWarningMessage).toHaveBeenCalledWith(152'Cannot send virtual files to Copilot CLI.',153);154});155156it('should reject output scheme with warning', async () => {157mockActiveTextEditor.value = createMockEditorWithScheme('/Output', 'content', 0, 0, 0, 7, 'output');158159registerAddSelectionCommand(logger, httpServer as unknown as InProcHttpServer, sessionTracker.asTracker());160await registeredCommands.get(ADD_SELECTION_COMMAND)!();161162expect(httpServer.sendNotification).not.toHaveBeenCalled();163expect(vscode.window.showWarningMessage).toHaveBeenCalledWith(164'Cannot send virtual files to Copilot CLI.',165);166});167168it('should reject untitled scheme with warning', async () => {169mockActiveTextEditor.value = createMockEditorWithScheme('/Untitled-1', 'content', 0, 0, 0, 7, 'untitled');170171registerAddSelectionCommand(logger, httpServer as unknown as InProcHttpServer, sessionTracker.asTracker());172await registeredCommands.get(ADD_SELECTION_COMMAND)!();173174expect(httpServer.sendNotification).not.toHaveBeenCalled();175expect(vscode.window.showWarningMessage).toHaveBeenCalledWith(176'Cannot send virtual files to Copilot CLI.',177);178});179180it('should reject vscode-chat-code-block scheme with warning', async () => {181mockActiveTextEditor.value = createMockEditorWithScheme('/block', 'content', 0, 0, 0, 7, 'vscode-chat-code-block');182183registerAddSelectionCommand(logger, httpServer as unknown as InProcHttpServer, sessionTracker.asTracker());184await registeredCommands.get(ADD_SELECTION_COMMAND)!();185186expect(httpServer.sendNotification).not.toHaveBeenCalled();187expect(vscode.window.showWarningMessage).toHaveBeenCalledWith(188'Cannot send virtual files to Copilot CLI.',189);190});191});192});193194195