summaryrefslogtreecommitdiff
path: root/packages/cli/src/ui/commands/ideCommand.test.ts
diff options
context:
space:
mode:
authorchristine betts <[email protected]>2025-07-30 21:26:31 +0000
committerGitHub <[email protected]>2025-07-30 21:26:31 +0000
commit7bc876654254d9a11d66135735ad10f1066ad213 (patch)
tree3b21e328199f8631c0d865fdf041103c18bd261e /packages/cli/src/ui/commands/ideCommand.test.ts
parentc1fe6889569610878c45216556fb99424b5bcba4 (diff)
Introduce IDE mode installer (#4877)
Diffstat (limited to 'packages/cli/src/ui/commands/ideCommand.test.ts')
-rw-r--r--packages/cli/src/ui/commands/ideCommand.test.ts138
1 files changed, 36 insertions, 102 deletions
diff --git a/packages/cli/src/ui/commands/ideCommand.test.ts b/packages/cli/src/ui/commands/ideCommand.test.ts
index d1d72466..3c73f52c 100644
--- a/packages/cli/src/ui/commands/ideCommand.test.ts
+++ b/packages/cli/src/ui/commands/ideCommand.test.ts
@@ -15,24 +15,16 @@ import {
} from 'vitest';
import { ideCommand } from './ideCommand.js';
import { type CommandContext } from './types.js';
-import { type Config } from '@google/gemini-cli-core';
-import * as child_process from 'child_process';
-import { glob } from 'glob';
-
-import { IDEConnectionStatus } from '@google/gemini-cli-core/index.js';
+import { type Config, DetectedIde } from '@google/gemini-cli-core';
+import * as core from '@google/gemini-cli-core';
vi.mock('child_process');
vi.mock('glob');
-
-function regexEscape(value: string) {
- return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
-}
+vi.mock('@google/gemini-cli-core');
describe('ideCommand', () => {
let mockContext: CommandContext;
let mockConfig: Config;
- let execSyncSpy: MockInstance;
- let globSyncSpy: MockInstance;
let platformSpy: MockInstance;
beforeEach(() => {
@@ -47,8 +39,6 @@ describe('ideCommand', () => {
getIdeClient: vi.fn(),
} as unknown as Config;
- execSyncSpy = vi.spyOn(child_process, 'execSync');
- globSyncSpy = vi.spyOn(glob, 'sync');
platformSpy = vi.spyOn(process, 'platform', 'get');
});
@@ -64,6 +54,9 @@ describe('ideCommand', () => {
it('should return the ide command if ideMode is enabled', () => {
vi.mocked(mockConfig.getIdeMode).mockReturnValue(true);
+ vi.mocked(mockConfig.getIdeClient).mockReturnValue({
+ getCurrentIde: () => DetectedIde.VSCode,
+ } as ReturnType<Config['getIdeClient']>);
const command = ideCommand(mockConfig);
expect(command).not.toBeNull();
expect(command?.name).toBe('ide');
@@ -78,12 +71,13 @@ describe('ideCommand', () => {
vi.mocked(mockConfig.getIdeMode).mockReturnValue(true);
vi.mocked(mockConfig.getIdeClient).mockReturnValue({
getConnectionStatus: mockGetConnectionStatus,
+ getCurrentIde: () => DetectedIde.VSCode,
} as ReturnType<Config['getIdeClient']>);
});
it('should show connected status', () => {
mockGetConnectionStatus.mockReturnValue({
- status: IDEConnectionStatus.Connected,
+ status: core.IDEConnectionStatus.Connected,
});
const command = ideCommand(mockConfig);
const result = command!.subCommands![0].action!(mockContext, '');
@@ -97,7 +91,7 @@ describe('ideCommand', () => {
it('should show connecting status', () => {
mockGetConnectionStatus.mockReturnValue({
- status: IDEConnectionStatus.Connecting,
+ status: core.IDEConnectionStatus.Connecting,
});
const command = ideCommand(mockConfig);
const result = command!.subCommands![0].action!(mockContext, '');
@@ -110,7 +104,7 @@ describe('ideCommand', () => {
});
it('should show disconnected status', () => {
mockGetConnectionStatus.mockReturnValue({
- status: IDEConnectionStatus.Disconnected,
+ status: core.IDEConnectionStatus.Disconnected,
});
const command = ideCommand(mockConfig);
const result = command!.subCommands![0].action!(mockContext, '');
@@ -125,7 +119,7 @@ describe('ideCommand', () => {
it('should show disconnected status with details', () => {
const details = 'Something went wrong';
mockGetConnectionStatus.mockReturnValue({
- status: IDEConnectionStatus.Disconnected,
+ status: core.IDEConnectionStatus.Disconnected,
details,
});
const command = ideCommand(mockConfig);
@@ -140,128 +134,68 @@ describe('ideCommand', () => {
});
describe('install subcommand', () => {
+ const mockInstall = vi.fn();
beforeEach(() => {
vi.mocked(mockConfig.getIdeMode).mockReturnValue(true);
+ vi.mocked(mockConfig.getIdeClient).mockReturnValue({
+ getCurrentIde: () => DetectedIde.VSCode,
+ } as ReturnType<Config['getIdeClient']>);
+ vi.mocked(core.getIdeInstaller).mockReturnValue({
+ install: mockInstall,
+ isInstalled: vi.fn(),
+ });
platformSpy.mockReturnValue('linux');
});
- it('should show an error if VSCode is not installed', async () => {
- execSyncSpy.mockImplementation(() => {
- throw new Error('Command not found');
+ it('should install the extension', async () => {
+ mockInstall.mockResolvedValue({
+ success: true,
+ message: 'Successfully installed.',
});
const command = ideCommand(mockConfig);
-
- await command!.subCommands![1].action!(mockContext, '');
- expect(mockContext.ui.addItem).toHaveBeenCalledWith(
- expect.objectContaining({
- type: 'error',
- text: expect.stringMatching(/VS Code command-line tool .* not found/),
- }),
- expect.any(Number),
- );
- });
-
- it('should show an error if the VSIX file is not found', async () => {
- execSyncSpy.mockReturnValue(''); // VSCode is installed
- globSyncSpy.mockReturnValue([]); // No .vsix file found
-
- const command = ideCommand(mockConfig);
await command!.subCommands![1].action!(mockContext, '');
- expect(mockContext.ui.addItem).toHaveBeenCalledWith(
- expect.objectContaining({
- type: 'error',
- text: 'Could not find the required VS Code companion extension. Please file a bug via /bug.',
- }),
- expect.any(Number),
- );
- });
-
- it('should install the extension if found in the bundle directory', async () => {
- const vsixPath = '/path/to/bundle/gemini.vsix';
- execSyncSpy.mockReturnValue(''); // VSCode is installed
- globSyncSpy.mockReturnValue([vsixPath]); // Found .vsix file
-
- const command = ideCommand(mockConfig);
- await command!.subCommands![1].action!(mockContext, '');
-
- expect(globSyncSpy).toHaveBeenCalledWith(
- expect.stringContaining('.vsix'),
- );
- expect(execSyncSpy).toHaveBeenCalledWith(
- expect.stringMatching(
- new RegExp(
- `code(.cmd)? --install-extension ${regexEscape(vsixPath)} --force`,
- ),
- ),
- { stdio: 'pipe' },
- );
+ expect(core.getIdeInstaller).toHaveBeenCalledWith('vscode');
+ expect(mockInstall).toHaveBeenCalled();
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
expect.objectContaining({
type: 'info',
- text: `Installing VS Code companion extension...`,
+ text: `Installing IDE companion extension...`,
}),
expect.any(Number),
);
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
expect.objectContaining({
type: 'info',
- text: 'VS Code companion extension installed successfully. Restart gemini-cli in a fresh terminal window.',
+ text: 'Successfully installed.',
}),
expect.any(Number),
);
});
- it('should install the extension if found in the dev directory', async () => {
- const vsixPath = '/path/to/dev/gemini.vsix';
- execSyncSpy.mockReturnValue(''); // VSCode is installed
- // First glob call for bundle returns nothing, second for dev returns path.
- globSyncSpy.mockReturnValueOnce([]).mockReturnValueOnce([vsixPath]);
+ it('should show an error if installation fails', async () => {
+ mockInstall.mockResolvedValue({
+ success: false,
+ message: 'Installation failed.',
+ });
const command = ideCommand(mockConfig);
await command!.subCommands![1].action!(mockContext, '');
- expect(globSyncSpy).toHaveBeenCalledTimes(2);
- expect(execSyncSpy).toHaveBeenCalledWith(
- expect.stringMatching(
- new RegExp(
- `code(.cmd)? --install-extension ${regexEscape(vsixPath)} --force`,
- ),
- ),
- { stdio: 'pipe' },
- );
+ expect(core.getIdeInstaller).toHaveBeenCalledWith('vscode');
+ expect(mockInstall).toHaveBeenCalled();
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
expect.objectContaining({
type: 'info',
- text: 'VS Code companion extension installed successfully. Restart gemini-cli in a fresh terminal window.',
+ text: `Installing IDE companion extension...`,
}),
expect.any(Number),
);
- });
-
- it('should show an error if installation fails', async () => {
- const vsixPath = '/path/to/bundle/gemini.vsix';
- const errorMessage = 'Installation failed';
- execSyncSpy
- .mockReturnValueOnce('') // VSCode is installed check
- .mockImplementation(() => {
- // Installation command
- const error: Error & { stderr?: Buffer } = new Error(
- 'Command failed',
- );
- error.stderr = Buffer.from(errorMessage);
- throw error;
- });
- globSyncSpy.mockReturnValue([vsixPath]);
-
- const command = ideCommand(mockConfig);
- await command!.subCommands![1].action!(mockContext, '');
-
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
expect.objectContaining({
type: 'error',
- text: `Failed to install VS Code companion extension.`,
+ text: 'Installation failed.',
}),
expect.any(Number),
);