2025-07-16 18:36:14 -04:00
|
|
|
/**
|
|
|
|
|
* @license
|
|
|
|
|
* Copyright 2025 Google LLC
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*/
|
|
|
|
|
|
2025-07-23 14:01:51 -07:00
|
|
|
import {
|
|
|
|
|
MockInstance,
|
|
|
|
|
vi,
|
|
|
|
|
describe,
|
|
|
|
|
it,
|
|
|
|
|
expect,
|
|
|
|
|
beforeEach,
|
|
|
|
|
afterEach,
|
|
|
|
|
} from 'vitest';
|
2025-07-16 18:36:14 -04:00
|
|
|
import { ideCommand } from './ideCommand.js';
|
|
|
|
|
import { type CommandContext } from './types.js';
|
2025-07-30 21:26:31 +00:00
|
|
|
import { type Config, DetectedIde } from '@google/gemini-cli-core';
|
|
|
|
|
import * as core from '@google/gemini-cli-core';
|
2025-07-16 18:36:14 -04:00
|
|
|
|
|
|
|
|
vi.mock('child_process');
|
|
|
|
|
vi.mock('glob');
|
2025-07-30 21:26:31 +00:00
|
|
|
vi.mock('@google/gemini-cli-core');
|
2025-07-23 14:01:51 -07:00
|
|
|
|
2025-07-16 18:36:14 -04:00
|
|
|
describe('ideCommand', () => {
|
|
|
|
|
let mockContext: CommandContext;
|
|
|
|
|
let mockConfig: Config;
|
2025-07-23 14:01:51 -07:00
|
|
|
let platformSpy: MockInstance;
|
2025-07-16 18:36:14 -04:00
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
mockContext = {
|
|
|
|
|
ui: {
|
|
|
|
|
addItem: vi.fn(),
|
|
|
|
|
},
|
|
|
|
|
} as unknown as CommandContext;
|
|
|
|
|
|
|
|
|
|
mockConfig = {
|
|
|
|
|
getIdeMode: vi.fn(),
|
2025-07-25 17:46:55 +00:00
|
|
|
getIdeClient: vi.fn(),
|
2025-07-16 18:36:14 -04:00
|
|
|
} as unknown as Config;
|
|
|
|
|
|
|
|
|
|
platformSpy = vi.spyOn(process, 'platform', 'get');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
|
vi.restoreAllMocks();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should return null if ideMode is not enabled', () => {
|
2025-07-23 14:01:51 -07:00
|
|
|
vi.mocked(mockConfig.getIdeMode).mockReturnValue(false);
|
2025-07-16 18:36:14 -04:00
|
|
|
const command = ideCommand(mockConfig);
|
|
|
|
|
expect(command).toBeNull();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should return the ide command if ideMode is enabled', () => {
|
2025-07-23 14:01:51 -07:00
|
|
|
vi.mocked(mockConfig.getIdeMode).mockReturnValue(true);
|
2025-07-30 21:26:31 +00:00
|
|
|
vi.mocked(mockConfig.getIdeClient).mockReturnValue({
|
|
|
|
|
getCurrentIde: () => DetectedIde.VSCode,
|
|
|
|
|
} as ReturnType<Config['getIdeClient']>);
|
2025-07-16 18:36:14 -04:00
|
|
|
const command = ideCommand(mockConfig);
|
|
|
|
|
expect(command).not.toBeNull();
|
|
|
|
|
expect(command?.name).toBe('ide');
|
|
|
|
|
expect(command?.subCommands).toHaveLength(2);
|
|
|
|
|
expect(command?.subCommands?.[0].name).toBe('status');
|
|
|
|
|
expect(command?.subCommands?.[1].name).toBe('install');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('status subcommand', () => {
|
2025-07-25 17:46:55 +00:00
|
|
|
const mockGetConnectionStatus = vi.fn();
|
2025-07-16 18:36:14 -04:00
|
|
|
beforeEach(() => {
|
2025-07-23 14:01:51 -07:00
|
|
|
vi.mocked(mockConfig.getIdeMode).mockReturnValue(true);
|
2025-07-25 17:46:55 +00:00
|
|
|
vi.mocked(mockConfig.getIdeClient).mockReturnValue({
|
|
|
|
|
getConnectionStatus: mockGetConnectionStatus,
|
2025-07-30 21:26:31 +00:00
|
|
|
getCurrentIde: () => DetectedIde.VSCode,
|
2025-07-25 17:46:55 +00:00
|
|
|
} as ReturnType<Config['getIdeClient']>);
|
2025-07-16 18:36:14 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should show connected status', () => {
|
2025-07-25 17:46:55 +00:00
|
|
|
mockGetConnectionStatus.mockReturnValue({
|
2025-07-30 21:26:31 +00:00
|
|
|
status: core.IDEConnectionStatus.Connected,
|
2025-07-25 17:46:55 +00:00
|
|
|
});
|
2025-07-16 18:36:14 -04:00
|
|
|
const command = ideCommand(mockConfig);
|
2025-07-23 14:01:51 -07:00
|
|
|
const result = command!.subCommands![0].action!(mockContext, '');
|
2025-07-25 17:46:55 +00:00
|
|
|
expect(mockGetConnectionStatus).toHaveBeenCalled();
|
2025-07-16 18:36:14 -04:00
|
|
|
expect(result).toEqual({
|
|
|
|
|
type: 'message',
|
|
|
|
|
messageType: 'info',
|
|
|
|
|
content: '🟢 Connected',
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should show connecting status', () => {
|
2025-07-25 17:46:55 +00:00
|
|
|
mockGetConnectionStatus.mockReturnValue({
|
2025-07-30 21:26:31 +00:00
|
|
|
status: core.IDEConnectionStatus.Connecting,
|
2025-07-25 17:46:55 +00:00
|
|
|
});
|
2025-07-16 18:36:14 -04:00
|
|
|
const command = ideCommand(mockConfig);
|
2025-07-23 14:01:51 -07:00
|
|
|
const result = command!.subCommands![0].action!(mockContext, '');
|
2025-07-25 17:46:55 +00:00
|
|
|
expect(mockGetConnectionStatus).toHaveBeenCalled();
|
2025-07-16 18:36:14 -04:00
|
|
|
expect(result).toEqual({
|
|
|
|
|
type: 'message',
|
|
|
|
|
messageType: 'info',
|
2025-07-25 17:46:55 +00:00
|
|
|
content: `🟡 Connecting...`,
|
2025-07-16 18:36:14 -04:00
|
|
|
});
|
|
|
|
|
});
|
2025-07-25 17:46:55 +00:00
|
|
|
it('should show disconnected status', () => {
|
|
|
|
|
mockGetConnectionStatus.mockReturnValue({
|
2025-07-30 21:26:31 +00:00
|
|
|
status: core.IDEConnectionStatus.Disconnected,
|
2025-07-25 17:46:55 +00:00
|
|
|
});
|
2025-07-16 18:36:14 -04:00
|
|
|
const command = ideCommand(mockConfig);
|
2025-07-23 14:01:51 -07:00
|
|
|
const result = command!.subCommands![0].action!(mockContext, '');
|
2025-07-25 17:46:55 +00:00
|
|
|
expect(mockGetConnectionStatus).toHaveBeenCalled();
|
2025-07-16 18:36:14 -04:00
|
|
|
expect(result).toEqual({
|
|
|
|
|
type: 'message',
|
2025-07-25 17:46:55 +00:00
|
|
|
messageType: 'error',
|
|
|
|
|
content: `🔴 Disconnected`,
|
2025-07-16 18:36:14 -04:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2025-07-25 17:46:55 +00:00
|
|
|
it('should show disconnected status with details', () => {
|
|
|
|
|
const details = 'Something went wrong';
|
|
|
|
|
mockGetConnectionStatus.mockReturnValue({
|
2025-07-30 21:26:31 +00:00
|
|
|
status: core.IDEConnectionStatus.Disconnected,
|
2025-07-25 17:46:55 +00:00
|
|
|
details,
|
|
|
|
|
});
|
2025-07-16 18:36:14 -04:00
|
|
|
const command = ideCommand(mockConfig);
|
2025-07-23 14:01:51 -07:00
|
|
|
const result = command!.subCommands![0].action!(mockContext, '');
|
2025-07-25 17:46:55 +00:00
|
|
|
expect(mockGetConnectionStatus).toHaveBeenCalled();
|
2025-07-16 18:36:14 -04:00
|
|
|
expect(result).toEqual({
|
|
|
|
|
type: 'message',
|
|
|
|
|
messageType: 'error',
|
2025-07-25 17:46:55 +00:00
|
|
|
content: `🔴 Disconnected: ${details}`,
|
2025-07-16 18:36:14 -04:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('install subcommand', () => {
|
2025-07-30 21:26:31 +00:00
|
|
|
const mockInstall = vi.fn();
|
2025-07-16 18:36:14 -04:00
|
|
|
beforeEach(() => {
|
2025-07-23 14:01:51 -07:00
|
|
|
vi.mocked(mockConfig.getIdeMode).mockReturnValue(true);
|
2025-07-30 21:26:31 +00:00
|
|
|
vi.mocked(mockConfig.getIdeClient).mockReturnValue({
|
|
|
|
|
getCurrentIde: () => DetectedIde.VSCode,
|
|
|
|
|
} as ReturnType<Config['getIdeClient']>);
|
|
|
|
|
vi.mocked(core.getIdeInstaller).mockReturnValue({
|
|
|
|
|
install: mockInstall,
|
|
|
|
|
isInstalled: vi.fn(),
|
|
|
|
|
});
|
2025-07-16 18:36:14 -04:00
|
|
|
platformSpy.mockReturnValue('linux');
|
|
|
|
|
});
|
|
|
|
|
|
2025-07-30 21:26:31 +00:00
|
|
|
it('should install the extension', async () => {
|
|
|
|
|
mockInstall.mockResolvedValue({
|
|
|
|
|
success: true,
|
|
|
|
|
message: 'Successfully installed.',
|
2025-07-16 18:36:14 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const command = ideCommand(mockConfig);
|
2025-07-23 14:01:51 -07:00
|
|
|
await command!.subCommands![1].action!(mockContext, '');
|
2025-07-16 18:36:14 -04:00
|
|
|
|
2025-07-30 21:26:31 +00:00
|
|
|
expect(core.getIdeInstaller).toHaveBeenCalledWith('vscode');
|
|
|
|
|
expect(mockInstall).toHaveBeenCalled();
|
2025-07-16 18:36:14 -04:00
|
|
|
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
|
|
|
|
expect.objectContaining({
|
|
|
|
|
type: 'info',
|
2025-07-30 21:26:31 +00:00
|
|
|
text: `Installing IDE companion extension...`,
|
2025-07-16 18:36:14 -04:00
|
|
|
}),
|
|
|
|
|
expect.any(Number),
|
|
|
|
|
);
|
|
|
|
|
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
|
|
|
|
expect.objectContaining({
|
|
|
|
|
type: 'info',
|
2025-07-30 21:26:31 +00:00
|
|
|
text: 'Successfully installed.',
|
2025-07-16 18:36:14 -04:00
|
|
|
}),
|
|
|
|
|
expect.any(Number),
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
2025-07-30 21:26:31 +00:00
|
|
|
it('should show an error if installation fails', async () => {
|
|
|
|
|
mockInstall.mockResolvedValue({
|
|
|
|
|
success: false,
|
|
|
|
|
message: 'Installation failed.',
|
|
|
|
|
});
|
2025-07-16 18:36:14 -04:00
|
|
|
|
|
|
|
|
const command = ideCommand(mockConfig);
|
2025-07-23 14:01:51 -07:00
|
|
|
await command!.subCommands![1].action!(mockContext, '');
|
2025-07-16 18:36:14 -04:00
|
|
|
|
2025-07-30 21:26:31 +00:00
|
|
|
expect(core.getIdeInstaller).toHaveBeenCalledWith('vscode');
|
|
|
|
|
expect(mockInstall).toHaveBeenCalled();
|
2025-07-16 18:36:14 -04:00
|
|
|
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
|
|
|
|
expect.objectContaining({
|
|
|
|
|
type: 'info',
|
2025-07-30 21:26:31 +00:00
|
|
|
text: `Installing IDE companion extension...`,
|
2025-07-16 18:36:14 -04:00
|
|
|
}),
|
|
|
|
|
expect.any(Number),
|
|
|
|
|
);
|
|
|
|
|
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
|
|
|
|
expect.objectContaining({
|
|
|
|
|
type: 'error',
|
2025-07-30 21:26:31 +00:00
|
|
|
text: 'Installation failed.',
|
2025-07-16 18:36:14 -04:00
|
|
|
}),
|
|
|
|
|
expect.any(Number),
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|