Download packages/core/src/ide/ide-installer.test.ts from SaylorTwift/gemini-cli: direct link, hf CLI and curl.
- Browser
- Download file 11.9 kB
-
https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/core/src/ide/ide-installer.test.ts
- Command line
-
hf download hf://SaylorTwift/gemini-cli/packages/core/src/ide/ide-installer.test.ts
-
curl -L -o ide-installer.test.ts https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/core/src/ide/ide-installer.test.ts
11.9 kB
| /** | |
| * @license | |
| * Copyright 2025 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest'; | |
| vi.mock('node:child_process', async (importOriginal) => { | |
| const actual = await importOriginal(); | |
| return { | |
| ...(actual as object), | |
| execSync: vi.fn(), | |
| spawnSync: vi.fn(() => ({ status: 0 })), | |
| }; | |
| }); | |
| vi.mock('node:fs'); | |
| vi.mock('node:os'); | |
| vi.mock('../utils/paths.js', async (importOriginal) => { | |
| const actual = await importOriginal<typeof import('../utils/paths.js')>(); | |
| return { | |
| ...actual, | |
| homedir: vi.fn(), | |
| }; | |
| }); | |
| import { getIdeInstaller } from './ide-installer.js'; | |
| import * as child_process from 'node:child_process'; | |
| import * as fs from 'node:fs'; | |
| import * as os from 'node:os'; | |
| import * as path from 'node:path'; | |
| import { IDE_DEFINITIONS, type IdeInfo } from './detect-ide.js'; | |
| import { homedir as pathsHomedir } from '../utils/paths.js'; | |
| describe('ide-installer', () => { | |
| const HOME_DIR = '/home/user'; | |
| beforeEach(() => { | |
| vi.spyOn(os, 'homedir').mockReturnValue(HOME_DIR); | |
| vi.mocked(pathsHomedir).mockReturnValue(HOME_DIR); | |
| }); | |
| afterEach(() => { | |
| vi.restoreAllMocks(); | |
| }); | |
| describe('getIdeInstaller', () => { | |
| it.each([ | |
| { ide: IDE_DEFINITIONS.vscode }, | |
| { ide: IDE_DEFINITIONS.firebasestudio }, | |
| ])('returns a VsCodeInstaller for "$ide.name"', ({ ide }) => { | |
| const installer = getIdeInstaller(ide); | |
| expect(installer).not.toBeNull(); | |
| expect(installer?.install).toEqual(expect.any(Function)); | |
| }); | |
| it('returns an AntigravityInstaller for "antigravity"', () => { | |
| const installer = getIdeInstaller(IDE_DEFINITIONS.antigravity); | |
| expect(installer).not.toBeNull(); | |
| expect(installer?.install).toEqual(expect.any(Function)); | |
| }); | |
| }); | |
| describe('VsCodeInstaller', () => { | |
| function setup({ | |
| ide = IDE_DEFINITIONS.vscode, | |
| existsResult = false, | |
| execSync = () => '', | |
| platform = 'linux' as NodeJS.Platform, | |
| }: { | |
| ide?: IdeInfo; | |
| existsResult?: boolean; | |
| execSync?: () => string; | |
| platform?: NodeJS.Platform; | |
| } = {}) { | |
| vi.spyOn(child_process, 'execSync').mockImplementation(execSync); | |
| vi.spyOn(fs, 'existsSync').mockReturnValue(existsResult); | |
| const installer = getIdeInstaller(ide, platform)!; | |
| return { installer }; | |
| } | |
| describe('install', () => { | |
| it.each([ | |
| { | |
| platform: 'win32' as NodeJS.Platform, | |
| expectedLookupPaths: [ | |
| path.join('C:\\Program Files', 'Microsoft VS Code/bin/code.cmd'), | |
| path.join( | |
| HOME_DIR, | |
| '/AppData/Local/Programs/Microsoft VS Code/bin/code.cmd', | |
| ), | |
| ], | |
| }, | |
| { | |
| platform: 'darwin' as NodeJS.Platform, | |
| expectedLookupPaths: [ | |
| '/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code', | |
| path.join(HOME_DIR, 'Library/Application Support/Code/bin/code'), | |
| ], | |
| }, | |
| { | |
| platform: 'linux' as NodeJS.Platform, | |
| expectedLookupPaths: ['/usr/share/code/bin/code'], | |
| }, | |
| ])( | |
| 'identifies the path to code cli on platform: $platform', | |
| async ({ platform, expectedLookupPaths }) => { | |
| const { installer } = setup({ | |
| platform, | |
| execSync: () => { | |
| throw new Error('Command not found'); // `code` is not in PATH | |
| }, | |
| }); | |
| await installer.install(); | |
| for (const [idx, path] of expectedLookupPaths.entries()) { | |
| expect(fs.existsSync).toHaveBeenNthCalledWith(idx + 1, path); | |
| } | |
| }, | |
| ); | |
| it('installs the extension using code cli', async () => { | |
| const { installer } = setup({ | |
| platform: 'linux', | |
| }); | |
| await installer.install(); | |
| expect(child_process.spawnSync).toHaveBeenCalledWith( | |
| 'code', | |
| [ | |
| '--install-extension', | |
| 'google.gemini-cli-vscode-ide-companion', | |
| '--force', | |
| ], | |
| { stdio: 'pipe', shell: false }, | |
| ); | |
| }); | |
| it('installs the extension using code cli on windows', async () => { | |
| const { installer } = setup({ | |
| platform: 'win32', | |
| execSync: () => 'C:\\Program Files\\Microsoft VS Code\\bin\\code.cmd', | |
| }); | |
| await installer.install(); | |
| expect(child_process.spawnSync).toHaveBeenCalledWith( | |
| 'C:\\Program Files\\Microsoft VS Code\\bin\\code.cmd', | |
| [ | |
| '--install-extension', | |
| 'google.gemini-cli-vscode-ide-companion', | |
| '--force', | |
| ], | |
| { stdio: 'pipe', shell: true }, | |
| ); | |
| }); | |
| it.each([ | |
| { | |
| ide: IDE_DEFINITIONS.vscode, | |
| expectedMessage: | |
| 'VS Code companion extension was installed successfully', | |
| }, | |
| { | |
| ide: IDE_DEFINITIONS.firebasestudio, | |
| expectedMessage: | |
| 'Firebase Studio companion extension was installed successfully', | |
| }, | |
| ])( | |
| 'returns that the cli was installed successfully', | |
| async ({ ide, expectedMessage }) => { | |
| const { installer } = setup({ ide }); | |
| const result = await installer.install(); | |
| expect(result.success).toBe(true); | |
| expect(result.message).toContain(expectedMessage); | |
| }, | |
| ); | |
| it.each([ | |
| { | |
| ide: IDE_DEFINITIONS.vscode, | |
| expectedErr: 'VS Code CLI not found', | |
| }, | |
| { | |
| ide: IDE_DEFINITIONS.firebasestudio, | |
| expectedErr: 'Firebase Studio CLI not found', | |
| }, | |
| ])( | |
| 'should return a failure message if $ide is not installed', | |
| async ({ ide, expectedErr }) => { | |
| const { installer } = setup({ | |
| ide, | |
| execSync: () => { | |
| throw new Error('Command not found'); | |
| }, | |
| existsResult: false, | |
| }); | |
| const result = await installer.install(); | |
| expect(result.success).toBe(false); | |
| expect(result.message).toContain(expectedErr); | |
| }, | |
| ); | |
| }); | |
| }); | |
| describe('PositronInstaller', () => { | |
| function setup({ | |
| execSync = () => '', | |
| platform = 'linux' as NodeJS.Platform, | |
| existsResult = false, | |
| }: { | |
| execSync?: () => string; | |
| platform?: NodeJS.Platform; | |
| existsResult?: boolean; | |
| } = {}) { | |
| vi.spyOn(child_process, 'execSync').mockImplementation(execSync); | |
| vi.spyOn(fs, 'existsSync').mockReturnValue(existsResult); | |
| const installer = getIdeInstaller(IDE_DEFINITIONS.positron, platform)!; | |
| return { installer }; | |
| } | |
| it('installs the extension', async () => { | |
| vi.stubEnv('POSITRON', '1'); | |
| const { installer } = setup({}); | |
| const result = await installer.install(); | |
| expect(result.success).toBe(true); | |
| expect(child_process.spawnSync).toHaveBeenCalledWith( | |
| 'positron', | |
| [ | |
| '--install-extension', | |
| 'google.gemini-cli-vscode-ide-companion', | |
| '--force', | |
| ], | |
| { stdio: 'pipe', shell: false }, | |
| ); | |
| }); | |
| it('returns a failure message if the cli is not found', async () => { | |
| const { installer } = setup({ | |
| execSync: () => { | |
| throw new Error('Command not found'); | |
| }, | |
| }); | |
| const result = await installer.install(); | |
| expect(result.success).toBe(false); | |
| expect(result.message).toContain('Positron CLI not found'); | |
| }); | |
| }); | |
| }); | |
| describe('AntigravityInstaller', () => { | |
| function setup({ | |
| execSync = () => '', | |
| platform = 'linux' as NodeJS.Platform, | |
| }: { | |
| execSync?: () => string; | |
| platform?: NodeJS.Platform; | |
| } = {}) { | |
| vi.spyOn(child_process, 'execSync').mockImplementation(execSync); | |
| const installer = getIdeInstaller(IDE_DEFINITIONS.antigravity, platform)!; | |
| return { installer }; | |
| } | |
| it('installs the extension using the alias', async () => { | |
| vi.stubEnv('ANTIGRAVITY_CLI_ALIAS', 'agy'); | |
| const { installer } = setup({}); | |
| const result = await installer.install(); | |
| expect(result.success).toBe(true); | |
| expect(child_process.spawnSync).toHaveBeenCalledWith( | |
| 'agy', | |
| [ | |
| '--install-extension', | |
| 'google.gemini-cli-vscode-ide-companion', | |
| '--force', | |
| ], | |
| { stdio: 'pipe', shell: false }, | |
| ); | |
| }); | |
| it('ignores an unsafe alias and falls back to safe commands', async () => { | |
| vi.stubEnv('ANTIGRAVITY_CLI_ALIAS', 'agy;malicious_command'); | |
| const { installer } = setup(); | |
| vi.mocked(child_process.execSync).mockImplementationOnce(() => 'agy'); | |
| const result = await installer.install(); | |
| expect(result.success).toBe(true); | |
| expect(child_process.execSync).toHaveBeenCalledTimes(1); | |
| expect(child_process.execSync).toHaveBeenCalledWith('command -v agy', { | |
| stdio: 'ignore', | |
| }); | |
| expect(child_process.spawnSync).toHaveBeenCalledWith( | |
| 'agy', | |
| [ | |
| '--install-extension', | |
| 'google.gemini-cli-vscode-ide-companion', | |
| '--force', | |
| ], | |
| { stdio: 'pipe', shell: false }, | |
| ); | |
| }); | |
| it('falls back to antigravity when agy is unavailable on linux', async () => { | |
| vi.stubEnv('ANTIGRAVITY_CLI_ALIAS', 'agy'); | |
| const { installer } = setup(); | |
| vi.mocked(child_process.execSync) | |
| .mockImplementationOnce(() => { | |
| throw new Error('Command not found'); | |
| }) | |
| .mockImplementationOnce(() => 'antigravity'); | |
| const result = await installer.install(); | |
| expect(result.success).toBe(true); | |
| expect(child_process.execSync).toHaveBeenNthCalledWith( | |
| 1, | |
| 'command -v agy', | |
| { | |
| stdio: 'ignore', | |
| }, | |
| ); | |
| expect(child_process.execSync).toHaveBeenNthCalledWith( | |
| 2, | |
| 'command -v antigravity', | |
| { stdio: 'ignore' }, | |
| ); | |
| expect(child_process.spawnSync).toHaveBeenCalledWith( | |
| 'antigravity', | |
| [ | |
| '--install-extension', | |
| 'google.gemini-cli-vscode-ide-companion', | |
| '--force', | |
| ], | |
| { stdio: 'pipe', shell: false }, | |
| ); | |
| }); | |
| it('falls back to antigravity.cmd when agy.cmd is unavailable on windows', async () => { | |
| vi.stubEnv('ANTIGRAVITY_CLI_ALIAS', 'agy.cmd'); | |
| const { installer } = setup({ | |
| platform: 'win32', | |
| }); | |
| vi.mocked(child_process.execSync) | |
| .mockImplementationOnce(() => { | |
| throw new Error('Command not found'); | |
| }) | |
| .mockImplementationOnce( | |
| () => 'C:\\Program Files\\Antigravity\\bin\\antigravity.cmd', | |
| ); | |
| const result = await installer.install(); | |
| expect(result.success).toBe(true); | |
| expect(child_process.execSync).toHaveBeenNthCalledWith( | |
| 1, | |
| 'where.exe agy.cmd', | |
| ); | |
| expect(child_process.execSync).toHaveBeenNthCalledWith( | |
| 2, | |
| 'where.exe antigravity.cmd', | |
| ); | |
| expect(child_process.spawnSync).toHaveBeenCalledWith( | |
| 'C:\\Program Files\\Antigravity\\bin\\antigravity.cmd', | |
| [ | |
| '--install-extension', | |
| 'google.gemini-cli-vscode-ide-companion', | |
| '--force', | |
| ], | |
| { stdio: 'pipe', shell: true }, | |
| ); | |
| }); | |
| it('falls back to default commands if the alias is not set', async () => { | |
| vi.stubEnv('ANTIGRAVITY_CLI_ALIAS', ''); | |
| const { installer } = setup({}); | |
| const result = await installer.install(); | |
| expect(result.success).toBe(true); | |
| }); | |
| it('returns a failure message if the command is not found', async () => { | |
| vi.stubEnv('ANTIGRAVITY_CLI_ALIAS', 'not-a-command'); | |
| const { installer } = setup({ | |
| execSync: () => { | |
| throw new Error('Command not found'); | |
| }, | |
| }); | |
| const result = await installer.install(); | |
| expect(result.success).toBe(false); | |
| expect(result.message).toContain('Antigravity CLI not found'); | |
| expect(result.message).toContain('agy, antigravity'); | |
| }); | |
| }); | |