summaryrefslogtreecommitdiff
path: root/packages/cli/src/ui/hooks/useEditorSettings.test.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/cli/src/ui/hooks/useEditorSettings.test.tsx')
-rw-r--r--packages/cli/src/ui/hooks/useEditorSettings.test.tsx305
1 files changed, 305 insertions, 0 deletions
diff --git a/packages/cli/src/ui/hooks/useEditorSettings.test.tsx b/packages/cli/src/ui/hooks/useEditorSettings.test.tsx
new file mode 100644
index 00000000..f1d65056
--- /dev/null
+++ b/packages/cli/src/ui/hooks/useEditorSettings.test.tsx
@@ -0,0 +1,305 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {
+ afterEach,
+ beforeEach,
+ describe,
+ expect,
+ it,
+ vi,
+ type MockedFunction,
+} from 'vitest';
+import { act } from 'react';
+import { renderHook } from '@testing-library/react';
+import { useEditorSettings } from './useEditorSettings.js';
+import { LoadedSettings, SettingScope } from '../../config/settings.js';
+import { MessageType, type HistoryItem } from '../types.js';
+import {
+ type EditorType,
+ checkHasEditorType,
+ allowEditorTypeInSandbox,
+} from '@google/gemini-cli-core';
+import { SettingsContext } from '../contexts/SettingsContext.js';
+import { type ReactNode } from 'react';
+
+vi.mock('@google/gemini-cli-core', async () => {
+ const actual = await vi.importActual('@google/gemini-cli-core');
+ return {
+ ...actual,
+ checkHasEditorType: vi.fn(() => true),
+ allowEditorTypeInSandbox: vi.fn(() => true),
+ };
+});
+
+const mockCheckHasEditorType = vi.mocked(checkHasEditorType);
+const mockAllowEditorTypeInSandbox = vi.mocked(allowEditorTypeInSandbox);
+
+describe('useEditorSettings', () => {
+ let mockLoadedSettings: LoadedSettings;
+ let mockSetEditorError: MockedFunction<(error: string | null) => void>;
+ let mockAddItem: MockedFunction<
+ (item: Omit<HistoryItem, 'id'>, timestamp: number) => void
+ >;
+
+ const wrapper = ({ children }: { children: ReactNode }) => (
+ <SettingsContext.Provider
+ value={{ settings: mockLoadedSettings, recomputeSettings: () => {} }}
+ >
+ {children}
+ </SettingsContext.Provider>
+ );
+
+ beforeEach(() => {
+ vi.resetAllMocks();
+ mockLoadedSettings = new LoadedSettings(
+ { path: '', settings: {} },
+ { path: '', settings: {} },
+ { path: '', settings: {} },
+ [],
+ );
+ mockLoadedSettings.setValue = vi.fn();
+ mockSetEditorError = vi.fn();
+ mockAddItem = vi.fn();
+
+ // Reset mock implementations to default
+ mockCheckHasEditorType.mockReturnValue(true);
+ mockAllowEditorTypeInSandbox.mockReturnValue(true);
+ });
+
+ afterEach(() => {
+ vi.restoreAllMocks();
+ });
+
+ it('should initialize with dialog closed', () => {
+ const { result } = renderHook(
+ () => useEditorSettings(mockSetEditorError, mockAddItem),
+ { wrapper },
+ );
+
+ expect(result.current.isEditorDialogOpen).toBe(false);
+ });
+
+ it('should open editor dialog when openEditorDialog is called', () => {
+ const { result } = renderHook(
+ () => useEditorSettings(mockSetEditorError, mockAddItem),
+ { wrapper },
+ );
+
+ act(() => {
+ result.current.openEditorDialog();
+ });
+
+ expect(result.current.isEditorDialogOpen).toBe(true);
+ });
+
+ it('should close editor dialog when exitEditorDialog is called', () => {
+ const { result } = renderHook(
+ () => useEditorSettings(mockSetEditorError, mockAddItem),
+ { wrapper },
+ );
+ act(() => {
+ result.current.openEditorDialog();
+ result.current.exitEditorDialog();
+ });
+ expect(result.current.isEditorDialogOpen).toBe(false);
+ });
+
+ it('should handle editor selection successfully', () => {
+ const { result } = renderHook(
+ () => useEditorSettings(mockSetEditorError, mockAddItem),
+ { wrapper },
+ );
+
+ const editorType: EditorType = 'vscode';
+ const scope = SettingScope.User;
+
+ act(() => {
+ result.current.openEditorDialog();
+ result.current.handleEditorSelect(editorType, scope);
+ });
+
+ expect(mockLoadedSettings.setValue).toHaveBeenCalledWith(
+ scope,
+ 'preferredEditor',
+ editorType,
+ );
+
+ expect(mockAddItem).toHaveBeenCalledWith(
+ {
+ type: MessageType.INFO,
+ text: 'Editor preference set to "vscode" in User settings.',
+ },
+ expect.any(Number),
+ );
+
+ expect(mockSetEditorError).toHaveBeenCalledWith(null);
+ expect(result.current.isEditorDialogOpen).toBe(false);
+ });
+
+ it('should handle clearing editor preference (undefined editor)', () => {
+ const { result } = renderHook(
+ () => useEditorSettings(mockSetEditorError, mockAddItem),
+ { wrapper },
+ );
+
+ const scope = SettingScope.Workspace;
+
+ act(() => {
+ result.current.openEditorDialog();
+ result.current.handleEditorSelect(undefined, scope);
+ });
+
+ expect(mockLoadedSettings.setValue).toHaveBeenCalledWith(
+ scope,
+ 'preferredEditor',
+ undefined,
+ );
+
+ expect(mockAddItem).toHaveBeenCalledWith(
+ {
+ type: MessageType.INFO,
+ text: 'Editor preference cleared in Workspace settings.',
+ },
+ expect.any(Number),
+ );
+
+ expect(mockSetEditorError).toHaveBeenCalledWith(null);
+ expect(result.current.isEditorDialogOpen).toBe(false);
+ });
+
+ it('should handle different editor types', () => {
+ const { result } = renderHook(
+ () => useEditorSettings(mockSetEditorError, mockAddItem),
+ { wrapper },
+ );
+
+ const editorTypes: EditorType[] = ['cursor', 'windsurf', 'vim'];
+ const scope = SettingScope.User;
+
+ editorTypes.forEach((editorType) => {
+ act(() => {
+ result.current.handleEditorSelect(editorType, scope);
+ });
+
+ expect(mockLoadedSettings.setValue).toHaveBeenCalledWith(
+ scope,
+ 'preferredEditor',
+ editorType,
+ );
+
+ expect(mockAddItem).toHaveBeenCalledWith(
+ {
+ type: MessageType.INFO,
+ text: `Editor preference set to "${editorType}" in User settings.`,
+ },
+ expect.any(Number),
+ );
+ });
+ });
+
+ it('should handle different setting scopes', () => {
+ const { result } = renderHook(
+ () => useEditorSettings(mockSetEditorError, mockAddItem),
+ { wrapper },
+ );
+
+ const editorType: EditorType = 'vscode';
+ const scopes = [SettingScope.User, SettingScope.Workspace];
+
+ scopes.forEach((scope) => {
+ act(() => {
+ result.current.handleEditorSelect(editorType, scope);
+ });
+
+ expect(mockLoadedSettings.setValue).toHaveBeenCalledWith(
+ scope,
+ 'preferredEditor',
+ editorType,
+ );
+
+ expect(mockAddItem).toHaveBeenCalledWith(
+ {
+ type: MessageType.INFO,
+ text: `Editor preference set to "vscode" in ${scope} settings.`,
+ },
+ expect.any(Number),
+ );
+ });
+ });
+
+ it('should not set preference for unavailable editors', () => {
+ const { result } = renderHook(
+ () => useEditorSettings(mockSetEditorError, mockAddItem),
+ { wrapper },
+ );
+
+ mockCheckHasEditorType.mockReturnValue(false);
+
+ const editorType: EditorType = 'vscode';
+ const scope = SettingScope.User;
+
+ act(() => {
+ result.current.openEditorDialog();
+ result.current.handleEditorSelect(editorType, scope);
+ });
+
+ expect(mockLoadedSettings.setValue).not.toHaveBeenCalled();
+ expect(mockAddItem).not.toHaveBeenCalled();
+ expect(result.current.isEditorDialogOpen).toBe(true);
+ });
+
+ it('should not set preference for editors not allowed in sandbox', () => {
+ const { result } = renderHook(
+ () => useEditorSettings(mockSetEditorError, mockAddItem),
+ { wrapper },
+ );
+
+ mockAllowEditorTypeInSandbox.mockReturnValue(false);
+
+ const editorType: EditorType = 'vscode';
+ const scope = SettingScope.User;
+
+ act(() => {
+ result.current.openEditorDialog();
+ result.current.handleEditorSelect(editorType, scope);
+ });
+
+ expect(mockLoadedSettings.setValue).not.toHaveBeenCalled();
+ expect(mockAddItem).not.toHaveBeenCalled();
+ expect(result.current.isEditorDialogOpen).toBe(true);
+ });
+
+ it('should handle errors during editor selection', () => {
+ const { result } = renderHook(
+ () => useEditorSettings(mockSetEditorError, mockAddItem),
+ { wrapper },
+ );
+
+ const errorMessage = 'Failed to save settings';
+ (
+ mockLoadedSettings.setValue as MockedFunction<
+ typeof mockLoadedSettings.setValue
+ >
+ ).mockImplementation(() => {
+ throw new Error(errorMessage);
+ });
+
+ const editorType: EditorType = 'vscode';
+ const scope = SettingScope.User;
+
+ act(() => {
+ result.current.openEditorDialog();
+ result.current.handleEditorSelect(editorType, scope);
+ });
+
+ expect(mockSetEditorError).toHaveBeenCalledWith(
+ `Failed to set editor preference: Error: ${errorMessage}`,
+ );
+ expect(mockAddItem).not.toHaveBeenCalled();
+ expect(result.current.isEditorDialogOpen).toBe(true);
+ });
+});