| |
| |
| |
| |
| |
|
|
| import { |
| vi, |
| describe, |
| it, |
| expect, |
| beforeEach, |
| afterEach, |
| type MockInstance, |
| } from 'vitest'; |
| import { ideCommand } from './ideCommand.js'; |
| import { type CommandContext } from './types.js'; |
| import { IDE_DEFINITIONS } from '@google/gemini-cli-core'; |
| import * as core from '@google/gemini-cli-core'; |
|
|
| vi.mock('@google/gemini-cli-core', async (importOriginal) => { |
| const original = await importOriginal<typeof core>(); |
| return { |
| ...original, |
| getOauthClient: vi.fn(original.getOauthClient), |
| getIdeInstaller: vi.fn(original.getIdeInstaller), |
| IdeClient: { |
| getInstance: vi.fn(), |
| }, |
| }; |
| }); |
|
|
| describe('ideCommand', () => { |
| let mockContext: CommandContext; |
| let mockIdeClient: core.IdeClient; |
| let platformSpy: MockInstance; |
|
|
| beforeEach(() => { |
| vi.resetAllMocks(); |
|
|
| mockIdeClient = { |
| reconnect: vi.fn(), |
| disconnect: vi.fn(), |
| connect: vi.fn(), |
| getCurrentIde: vi.fn(), |
| getConnectionStatus: vi.fn(), |
| getDetectedIdeDisplayName: vi.fn(), |
| } as unknown as core.IdeClient; |
|
|
| vi.mocked(core.IdeClient.getInstance).mockResolvedValue(mockIdeClient); |
| vi.mocked(mockIdeClient.getDetectedIdeDisplayName).mockReturnValue( |
| 'VS Code', |
| ); |
|
|
| mockContext = { |
| ui: { |
| addItem: vi.fn(), |
| }, |
| services: { |
| settings: { |
| setValue: vi.fn(), |
| }, |
| agentContext: { |
| config: { |
| getIdeMode: vi.fn(), |
| setIdeMode: vi.fn(), |
| getUsageStatisticsEnabled: vi.fn().mockReturnValue(false), |
| }, |
| }, |
| }, |
| } as unknown as CommandContext; |
|
|
| platformSpy = vi.spyOn(process, 'platform', 'get'); |
| }); |
|
|
| afterEach(() => { |
| vi.restoreAllMocks(); |
| }); |
|
|
| it('should return the ide command', async () => { |
| vi.mocked(mockIdeClient.getCurrentIde).mockReturnValue( |
| IDE_DEFINITIONS.vscode, |
| ); |
| vi.mocked(mockIdeClient.getConnectionStatus).mockReturnValue({ |
| status: core.IDEConnectionStatus.Disconnected, |
| }); |
| const command = await ideCommand(); |
| expect(command).not.toBeNull(); |
| expect(command.name).toBe('ide'); |
| expect(command.subCommands).toHaveLength(3); |
| expect(command.subCommands?.[0].name).toBe('enable'); |
| expect(command.subCommands?.[1].name).toBe('status'); |
| expect(command.subCommands?.[2].name).toBe('install'); |
| }); |
|
|
| it('should show disable command when connected', async () => { |
| vi.mocked(mockIdeClient.getCurrentIde).mockReturnValue( |
| IDE_DEFINITIONS.vscode, |
| ); |
| vi.mocked(mockIdeClient.getConnectionStatus).mockReturnValue({ |
| status: core.IDEConnectionStatus.Connected, |
| }); |
| const command = await ideCommand(); |
| expect(command).not.toBeNull(); |
| const subCommandNames = command.subCommands?.map((cmd) => cmd.name); |
| expect(subCommandNames).toContain('disable'); |
| expect(subCommandNames).not.toContain('enable'); |
| }); |
|
|
| describe('status subcommand', () => { |
| beforeEach(() => { |
| vi.mocked(mockIdeClient.getCurrentIde).mockReturnValue( |
| IDE_DEFINITIONS.vscode, |
| ); |
| }); |
|
|
| it('should show connected status', async () => { |
| vi.mocked(mockIdeClient.getConnectionStatus).mockReturnValue({ |
| status: core.IDEConnectionStatus.Connected, |
| }); |
| const command = await ideCommand(); |
| const result = await command.subCommands!.find( |
| (c) => c.name === 'status', |
| )!.action!(mockContext, ''); |
| expect(vi.mocked(mockIdeClient.getConnectionStatus)).toHaveBeenCalled(); |
| expect(result).toEqual({ |
| type: 'message', |
| messageType: 'info', |
| content: '🟢 Connected to VS Code', |
| }); |
| }); |
|
|
| it('should show connecting status', async () => { |
| vi.mocked(mockIdeClient.getConnectionStatus).mockReturnValue({ |
| status: core.IDEConnectionStatus.Connecting, |
| }); |
| const command = await ideCommand(); |
| const result = await command.subCommands!.find( |
| (c) => c.name === 'status', |
| )!.action!(mockContext, ''); |
| expect(vi.mocked(mockIdeClient.getConnectionStatus)).toHaveBeenCalled(); |
| expect(result).toEqual({ |
| type: 'message', |
| messageType: 'info', |
| content: `🟡 Connecting...`, |
| }); |
| }); |
| it('should show disconnected status', async () => { |
| vi.mocked(mockIdeClient.getConnectionStatus).mockReturnValue({ |
| status: core.IDEConnectionStatus.Disconnected, |
| }); |
| const command = await ideCommand(); |
| const result = await command.subCommands!.find( |
| (c) => c.name === 'status', |
| )!.action!(mockContext, ''); |
| expect(vi.mocked(mockIdeClient.getConnectionStatus)).toHaveBeenCalled(); |
| expect(result).toEqual({ |
| type: 'message', |
| messageType: 'error', |
| content: `🔴 Disconnected`, |
| }); |
| }); |
|
|
| it('should show disconnected status with details', async () => { |
| const details = 'Something went wrong'; |
| vi.mocked(mockIdeClient.getConnectionStatus).mockReturnValue({ |
| status: core.IDEConnectionStatus.Disconnected, |
| details, |
| }); |
| const command = await ideCommand(); |
| const result = await command.subCommands!.find( |
| (c) => c.name === 'status', |
| )!.action!(mockContext, ''); |
| expect(vi.mocked(mockIdeClient.getConnectionStatus)).toHaveBeenCalled(); |
| expect(result).toEqual({ |
| type: 'message', |
| messageType: 'error', |
| content: `🔴 Disconnected: ${details}`, |
| }); |
| }); |
| }); |
|
|
| describe('install subcommand', () => { |
| const mockInstall = vi.fn(); |
| beforeEach(() => { |
| vi.mocked(mockIdeClient.getCurrentIde).mockReturnValue( |
| IDE_DEFINITIONS.vscode, |
| ); |
| vi.mocked(mockIdeClient.getConnectionStatus).mockReturnValue({ |
| status: core.IDEConnectionStatus.Disconnected, |
| }); |
| vi.mocked(core.getIdeInstaller).mockReturnValue({ |
| install: mockInstall, |
| }); |
| platformSpy.mockReturnValue('linux'); |
| }); |
|
|
| it('should install the extension', async () => { |
| vi.useFakeTimers(); |
| mockInstall.mockResolvedValue({ |
| success: true, |
| message: 'Successfully installed.', |
| }); |
|
|
| const command = await ideCommand(); |
|
|
| |
| vi.mocked(mockIdeClient.getConnectionStatus).mockReturnValue({ |
| status: core.IDEConnectionStatus.Connected, |
| }); |
|
|
| const actionPromise = command.subCommands!.find( |
| (c) => c.name === 'install', |
| )!.action!(mockContext, ''); |
| await vi.runAllTimersAsync(); |
| await actionPromise; |
|
|
| expect(core.getIdeInstaller).toHaveBeenCalledWith(IDE_DEFINITIONS.vscode); |
| expect(mockInstall).toHaveBeenCalled(); |
| expect(mockContext.ui.addItem).toHaveBeenCalledWith( |
| expect.objectContaining({ |
| type: 'info', |
| text: `Installing IDE companion...`, |
| }), |
| expect.any(Number), |
| ); |
| expect(mockContext.ui.addItem).toHaveBeenCalledWith( |
| expect.objectContaining({ |
| type: 'info', |
| text: 'Successfully installed.', |
| }), |
| expect.any(Number), |
| ); |
| expect(mockContext.ui.addItem).toHaveBeenCalledWith( |
| expect.objectContaining({ |
| type: 'info', |
| text: '🟢 Connected to VS Code', |
| }), |
| expect.any(Number), |
| ); |
| vi.useRealTimers(); |
| }, 10000); |
|
|
| it('should show an error if installation fails', async () => { |
| mockInstall.mockResolvedValue({ |
| success: false, |
| message: 'Installation failed.', |
| }); |
|
|
| const command = await ideCommand(); |
| await command.subCommands!.find((c) => c.name === 'install')!.action!( |
| mockContext, |
| '', |
| ); |
|
|
| expect(core.getIdeInstaller).toHaveBeenCalledWith(IDE_DEFINITIONS.vscode); |
| expect(mockInstall).toHaveBeenCalled(); |
| expect(mockContext.ui.addItem).toHaveBeenCalledWith( |
| expect.objectContaining({ |
| type: 'info', |
| text: `Installing IDE companion...`, |
| }), |
| expect.any(Number), |
| ); |
| expect(mockContext.ui.addItem).toHaveBeenCalledWith( |
| expect.objectContaining({ |
| type: 'error', |
| text: 'Installation failed.', |
| }), |
| expect.any(Number), |
| ); |
| }); |
| }); |
| }); |
|
|