mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-23 03:24:42 -07:00
fix(cli): implement --all flag for extensions uninstall (#21319)
This commit is contained in:
@@ -28,6 +28,7 @@ import { getErrorMessage } from '../../utils/errors.js';
|
||||
// Hoisted mocks - these survive vi.clearAllMocks()
|
||||
const mockUninstallExtension = vi.hoisted(() => vi.fn());
|
||||
const mockLoadExtensions = vi.hoisted(() => vi.fn());
|
||||
const mockGetExtensions = vi.hoisted(() => vi.fn());
|
||||
|
||||
// Mock dependencies with hoisted functions
|
||||
vi.mock('../../config/extension-manager.js', async (importOriginal) => {
|
||||
@@ -38,6 +39,7 @@ vi.mock('../../config/extension-manager.js', async (importOriginal) => {
|
||||
ExtensionManager: vi.fn().mockImplementation(() => ({
|
||||
uninstallExtension: mockUninstallExtension,
|
||||
loadExtensions: mockLoadExtensions,
|
||||
getExtensions: mockGetExtensions,
|
||||
setRequestConsent: vi.fn(),
|
||||
setRequestSetting: vi.fn(),
|
||||
})),
|
||||
@@ -93,6 +95,7 @@ describe('extensions uninstall command', () => {
|
||||
afterEach(() => {
|
||||
mockLoadExtensions.mockClear();
|
||||
mockUninstallExtension.mockClear();
|
||||
mockGetExtensions.mockClear();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
@@ -145,6 +148,41 @@ describe('extensions uninstall command', () => {
|
||||
mockCwd.mockRestore();
|
||||
});
|
||||
|
||||
it('should uninstall all extensions when --all flag is used', async () => {
|
||||
mockLoadExtensions.mockResolvedValue(undefined);
|
||||
mockUninstallExtension.mockResolvedValue(undefined);
|
||||
mockGetExtensions.mockReturnValue([{ name: 'ext1' }, { name: 'ext2' }]);
|
||||
const mockCwd = vi.spyOn(process, 'cwd').mockReturnValue('/test/dir');
|
||||
await handleUninstall({ all: true });
|
||||
|
||||
expect(mockUninstallExtension).toHaveBeenCalledTimes(2);
|
||||
expect(mockUninstallExtension).toHaveBeenCalledWith('ext1', false);
|
||||
expect(mockUninstallExtension).toHaveBeenCalledWith('ext2', false);
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
'Extension "ext1" successfully uninstalled.',
|
||||
);
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
'Extension "ext2" successfully uninstalled.',
|
||||
);
|
||||
mockCwd.mockRestore();
|
||||
});
|
||||
|
||||
it('should log a message if no extensions are installed and --all flag is used', async () => {
|
||||
mockLoadExtensions.mockResolvedValue(undefined);
|
||||
mockGetExtensions.mockReturnValue([]);
|
||||
const mockCwd = vi.spyOn(process, 'cwd').mockReturnValue('/test/dir');
|
||||
await handleUninstall({ all: true });
|
||||
|
||||
expect(mockUninstallExtension).not.toHaveBeenCalled();
|
||||
expect(emitConsoleLog).toHaveBeenCalledWith(
|
||||
'log',
|
||||
'No extensions currently installed.',
|
||||
);
|
||||
mockCwd.mockRestore();
|
||||
});
|
||||
|
||||
it('should report errors for failed uninstalls but continue with others', async () => {
|
||||
mockLoadExtensions.mockResolvedValue(undefined);
|
||||
const mockCwd = vi.spyOn(process, 'cwd').mockReturnValue('/test/dir');
|
||||
@@ -236,13 +274,14 @@ describe('extensions uninstall command', () => {
|
||||
const command = uninstallCommand;
|
||||
|
||||
it('should have correct command and describe', () => {
|
||||
expect(command.command).toBe('uninstall <names..>');
|
||||
expect(command.command).toBe('uninstall [names..]');
|
||||
expect(command.describe).toBe('Uninstalls one or more extensions.');
|
||||
});
|
||||
|
||||
describe('builder', () => {
|
||||
interface MockYargs {
|
||||
positional: Mock;
|
||||
option: Mock;
|
||||
check: Mock;
|
||||
}
|
||||
|
||||
@@ -250,11 +289,12 @@ describe('extensions uninstall command', () => {
|
||||
beforeEach(() => {
|
||||
yargsMock = {
|
||||
positional: vi.fn().mockReturnThis(),
|
||||
option: vi.fn().mockReturnThis(),
|
||||
check: vi.fn().mockReturnThis(),
|
||||
};
|
||||
});
|
||||
|
||||
it('should configure positional argument', () => {
|
||||
it('should configure arguments and options', () => {
|
||||
(command.builder as (yargs: Argv) => Argv)(
|
||||
yargsMock as unknown as Argv,
|
||||
);
|
||||
@@ -264,18 +304,31 @@ describe('extensions uninstall command', () => {
|
||||
type: 'string',
|
||||
array: true,
|
||||
});
|
||||
expect(yargsMock.option).toHaveBeenCalledWith('all', {
|
||||
type: 'boolean',
|
||||
describe: 'Uninstall all installed extensions.',
|
||||
default: false,
|
||||
});
|
||||
expect(yargsMock.check).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('check function should throw for missing names', () => {
|
||||
it('check function should throw for missing names and no --all flag', () => {
|
||||
(command.builder as (yargs: Argv) => Argv)(
|
||||
yargsMock as unknown as Argv,
|
||||
);
|
||||
const checkCallback = yargsMock.check.mock.calls[0][0];
|
||||
expect(() => checkCallback({ names: [] })).toThrow(
|
||||
'Please include at least one extension name to uninstall as a positional argument.',
|
||||
expect(() => checkCallback({ names: [], all: false })).toThrow(
|
||||
'Please include at least one extension name to uninstall as a positional argument, or use the --all flag.',
|
||||
);
|
||||
});
|
||||
|
||||
it('check function should pass if --all flag is used even without names', () => {
|
||||
(command.builder as (yargs: Argv) => Argv)(
|
||||
yargsMock as unknown as Argv,
|
||||
);
|
||||
const checkCallback = yargsMock.check.mock.calls[0][0];
|
||||
expect(() => checkCallback({ names: [], all: true })).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it('handler should call handleUninstall', async () => {
|
||||
@@ -283,10 +336,17 @@ describe('extensions uninstall command', () => {
|
||||
mockUninstallExtension.mockResolvedValue(undefined);
|
||||
const mockCwd = vi.spyOn(process, 'cwd').mockReturnValue('/test/dir');
|
||||
interface TestArgv {
|
||||
names: string[];
|
||||
[key: string]: unknown;
|
||||
names?: string[];
|
||||
all?: boolean;
|
||||
_: string[];
|
||||
$0: string;
|
||||
}
|
||||
const argv: TestArgv = { names: ['my-extension'], _: [], $0: '' };
|
||||
const argv: TestArgv = {
|
||||
names: ['my-extension'],
|
||||
all: false,
|
||||
_: [],
|
||||
$0: '',
|
||||
};
|
||||
await (command.handler as unknown as (args: TestArgv) => Promise<void>)(
|
||||
argv,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user