mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-11 13:51:10 -07:00
228 lines
6.6 KiB
TypeScript
228 lines
6.6 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import * as fs from 'node:fs';
|
|
import * as path from 'node:path';
|
|
import {
|
|
beforeAll,
|
|
afterAll,
|
|
beforeEach,
|
|
describe,
|
|
expect,
|
|
it,
|
|
vi,
|
|
afterEach,
|
|
} from 'vitest';
|
|
import { createExtension } from '../test-utils/createExtension.js';
|
|
import { ExtensionManager } from './extension-manager.js';
|
|
import { themeManager, DEFAULT_THEME } from '../ui/themes/theme-manager.js';
|
|
import { GEMINI_DIR, type Config } from '@google/gemini-cli-core';
|
|
import { createTestMergedSettings, SettingScope } from './settings.js';
|
|
|
|
describe('ExtensionManager theme loading', () => {
|
|
let extensionManager: ExtensionManager;
|
|
let userExtensionsDir: string;
|
|
let tempHomeDir: string;
|
|
|
|
beforeAll(async () => {
|
|
tempHomeDir = await fs.promises.mkdtemp(
|
|
path.join(fs.realpathSync('/tmp'), 'gemini-cli-test-'),
|
|
);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
if (tempHomeDir) {
|
|
await fs.promises.rm(tempHomeDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
beforeEach(() => {
|
|
process.env['GEMINI_CLI_HOME'] = tempHomeDir;
|
|
userExtensionsDir = path.join(tempHomeDir, GEMINI_DIR, 'extensions');
|
|
// Ensure userExtensionsDir is clean for each test
|
|
fs.rmSync(userExtensionsDir, { recursive: true, force: true });
|
|
fs.mkdirSync(userExtensionsDir, { recursive: true });
|
|
|
|
extensionManager = new ExtensionManager({
|
|
settings: createTestMergedSettings({
|
|
experimental: { extensionConfig: true },
|
|
security: { blockGitExtensions: false },
|
|
admin: { extensions: { enabled: true }, mcp: { enabled: true } },
|
|
}),
|
|
requestConsent: async () => true,
|
|
requestSetting: async () => '',
|
|
workspaceDir: tempHomeDir,
|
|
enabledExtensionOverrides: [],
|
|
});
|
|
vi.clearAllMocks();
|
|
themeManager.clearExtensionThemes();
|
|
themeManager.loadCustomThemes({});
|
|
themeManager.setActiveTheme(DEFAULT_THEME.name);
|
|
});
|
|
|
|
afterEach(() => {
|
|
delete process.env['GEMINI_CLI_HOME'];
|
|
});
|
|
|
|
it('should register themes from an extension when started', async () => {
|
|
const registerSpy = vi.spyOn(themeManager, 'registerExtensionThemes');
|
|
createExtension({
|
|
extensionsDir: userExtensionsDir,
|
|
name: 'my-theme-extension',
|
|
themes: [
|
|
{
|
|
name: 'My-Awesome-Theme',
|
|
type: 'custom',
|
|
text: {
|
|
primary: '#FF00FF',
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
await extensionManager.loadExtensions();
|
|
|
|
const mockConfig = {
|
|
getEnableExtensionReloading: () => false,
|
|
getMcpClientManager: () => ({
|
|
startExtension: vi.fn().mockResolvedValue(undefined),
|
|
}),
|
|
getGeminiClient: () => ({
|
|
isInitialized: () => false,
|
|
updateSystemInstruction: vi.fn(),
|
|
setTools: vi.fn(),
|
|
}),
|
|
getHookSystem: () => undefined,
|
|
getWorkingDir: () => tempHomeDir,
|
|
shouldLoadMemoryFromIncludeDirectories: () => false,
|
|
getDebugMode: () => false,
|
|
getFileExclusions: () => ({
|
|
isIgnored: () => false,
|
|
}),
|
|
getGeminiMdFilePaths: () => [],
|
|
getMcpServers: () => ({}),
|
|
getAllowedMcpServers: () => [],
|
|
getSanitizationConfig: () => ({
|
|
allowedEnvironmentVariables: [],
|
|
blockedEnvironmentVariables: [],
|
|
enableEnvironmentVariableRedaction: false,
|
|
}),
|
|
getShellExecutionConfig: () => ({
|
|
terminalWidth: 80,
|
|
terminalHeight: 24,
|
|
showColor: false,
|
|
pager: 'cat',
|
|
sanitizationConfig: {
|
|
allowedEnvironmentVariables: [],
|
|
blockedEnvironmentVariables: [],
|
|
enableEnvironmentVariableRedaction: false,
|
|
},
|
|
}),
|
|
getToolRegistry: () => ({
|
|
getTools: () => [],
|
|
}),
|
|
getProxy: () => undefined,
|
|
getFileService: () => ({
|
|
findFiles: async () => [],
|
|
}),
|
|
getExtensionLoader: () => ({
|
|
getExtensions: () => [],
|
|
}),
|
|
isTrustedFolder: () => true,
|
|
getImportFormat: () => 'tree',
|
|
reloadSkills: vi.fn(),
|
|
} as unknown as Config;
|
|
|
|
await extensionManager.start(mockConfig);
|
|
|
|
expect(registerSpy).toHaveBeenCalledWith('my-theme-extension', [
|
|
{
|
|
name: 'My-Awesome-Theme',
|
|
type: 'custom',
|
|
text: {
|
|
primary: '#FF00FF',
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('should revert to default theme when extension is stopped', async () => {
|
|
const extensionName = 'my-theme-extension';
|
|
const themeName = 'My-Awesome-Theme';
|
|
const namespacedThemeName = `${themeName} (${extensionName})`;
|
|
|
|
createExtension({
|
|
extensionsDir: userExtensionsDir,
|
|
name: extensionName,
|
|
themes: [
|
|
{
|
|
name: themeName,
|
|
type: 'custom',
|
|
text: {
|
|
primary: '#FF00FF',
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
await extensionManager.loadExtensions();
|
|
|
|
const mockConfig = {
|
|
getWorkingDir: () => tempHomeDir,
|
|
shouldLoadMemoryFromIncludeDirectories: () => false,
|
|
getWorkspaceContext: () => ({
|
|
getDirectories: () => [],
|
|
}),
|
|
getDebugMode: () => false,
|
|
getFileService: () => ({
|
|
findFiles: async () => [],
|
|
}),
|
|
getExtensionLoader: () => ({
|
|
getExtensions: () => [],
|
|
}),
|
|
isTrustedFolder: () => true,
|
|
getImportFormat: () => 'tree',
|
|
getFileFilteringOptions: () => ({
|
|
respectGitIgnore: true,
|
|
respectGeminiIgnore: true,
|
|
}),
|
|
getDiscoveryMaxDirs: () => 200,
|
|
getMcpClientManager: () => ({
|
|
getMcpInstructions: () => '',
|
|
startExtension: vi.fn().mockResolvedValue(undefined),
|
|
stopExtension: vi.fn().mockResolvedValue(undefined),
|
|
}),
|
|
setUserMemory: vi.fn(),
|
|
setGeminiMdFileCount: vi.fn(),
|
|
setGeminiMdFilePaths: vi.fn(),
|
|
getEnableExtensionReloading: () => true,
|
|
getGeminiClient: () => ({
|
|
isInitialized: () => false,
|
|
updateSystemInstruction: vi.fn(),
|
|
setTools: vi.fn(),
|
|
}),
|
|
getHookSystem: () => undefined,
|
|
getProxy: () => undefined,
|
|
getAgentRegistry: () => ({
|
|
reload: vi.fn().mockResolvedValue(undefined),
|
|
}),
|
|
reloadSkills: vi.fn(),
|
|
} as unknown as Config;
|
|
|
|
await extensionManager.start(mockConfig);
|
|
|
|
// Set the active theme to the one from the extension
|
|
themeManager.setActiveTheme(namespacedThemeName);
|
|
expect(themeManager.getActiveTheme().name).toBe(namespacedThemeName);
|
|
|
|
// Stop the extension
|
|
await extensionManager.disableExtension(extensionName, SettingScope.User);
|
|
|
|
// Check that the active theme has reverted to the default
|
|
expect(themeManager.getActiveTheme().name).toBe(DEFAULT_THEME.name);
|
|
});
|
|
});
|