Download packages/cli/src/ui/hooks/useApprovalModeIndicator.test.ts from SaylorTwift/gemini-cli: direct link, hf CLI and curl.
- Browser
- Download file 23.8 kB
-
https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/cli/src/ui/hooks/useApprovalModeIndicator.test.ts
- Command line
-
hf download hf://SaylorTwift/gemini-cli/packages/cli/src/ui/hooks/useApprovalModeIndicator.test.ts
-
curl -L -o useApprovalModeIndicator.test.ts https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/cli/src/ui/hooks/useApprovalModeIndicator.test.ts
23.8 kB
| /** | |
| * @license | |
| * Copyright 2025 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import { | |
| describe, | |
| it, | |
| expect, | |
| vi, | |
| beforeEach, | |
| type MockedFunction, | |
| type Mock, | |
| } from 'vitest'; | |
| import { act } from 'react'; | |
| import { renderHook } from '../../test-utils/render.js'; | |
| import { useApprovalModeIndicator } from './useApprovalModeIndicator.js'; | |
| import { | |
| Config, | |
| ApprovalMode, | |
| type Config as ActualConfigType, | |
| } from '@google/gemini-cli-core'; | |
| import { useKeypress, type Key } from './useKeypress.js'; | |
| import { MessageType } from '../types.js'; | |
| vi.mock('./useKeypress.js'); | |
| vi.mock('@google/gemini-cli-core', async () => { | |
| const actualServerModule = await vi.importActual('@google/gemini-cli-core'); | |
| return { | |
| ...actualServerModule, | |
| Config: vi.fn(), | |
| getAdminErrorMessage: vi.fn( | |
| (featureName: string) => `[Mock] ${featureName} is disabled`, | |
| ), | |
| }; | |
| }); | |
| interface MockConfigInstanceShape { | |
| getApprovalMode: Mock<() => ApprovalMode>; | |
| setApprovalMode: Mock<(value: ApprovalMode) => void>; | |
| isYoloModeDisabled: Mock<() => boolean>; | |
| isPlanEnabled: Mock<() => boolean>; | |
| isTrustedFolder: Mock<() => boolean>; | |
| getCoreTools: Mock<() => string[]>; | |
| getToolDiscoveryCommand: Mock<() => string | undefined>; | |
| getTargetDir: Mock<() => string>; | |
| getApiKey: Mock<() => string>; | |
| getModel: Mock<() => string>; | |
| getSandbox: Mock<() => boolean | string>; | |
| getDebugMode: Mock<() => boolean>; | |
| getQuestion: Mock<() => string | undefined>; | |
| getUserAgent: Mock<() => string>; | |
| getUserMemory: Mock<() => string>; | |
| getGeminiMdFileCount: Mock<() => number>; | |
| getToolRegistry: Mock<() => { discoverTools: Mock<() => void> }>; | |
| getRemoteAdminSettings: Mock< | |
| () => { strictModeDisabled?: boolean; mcpEnabled?: boolean } | undefined | |
| >; | |
| } | |
| type UseKeypressHandler = (key: Key) => void; | |
| describe('useApprovalModeIndicator', () => { | |
| let mockConfigInstance: MockConfigInstanceShape; | |
| let capturedUseKeypressHandler: UseKeypressHandler; | |
| let mockedUseKeypress: MockedFunction<typeof useKeypress>; | |
| beforeEach(() => { | |
| vi.resetAllMocks(); | |
| ( | |
| Config as unknown as MockedFunction<() => MockConfigInstanceShape> | |
| ).mockImplementation(() => { | |
| const instanceGetApprovalModeMock = vi | |
| .fn() | |
| .mockReturnValue(ApprovalMode.DEFAULT); | |
| const instanceSetApprovalModeMock = vi.fn(); | |
| const instance: MockConfigInstanceShape = { | |
| getApprovalMode: instanceGetApprovalModeMock as Mock< | |
| () => ApprovalMode | |
| >, | |
| setApprovalMode: instanceSetApprovalModeMock as Mock< | |
| (value: ApprovalMode) => void | |
| >, | |
| isYoloModeDisabled: vi.fn().mockReturnValue(false), | |
| isPlanEnabled: vi.fn().mockReturnValue(true), | |
| isTrustedFolder: vi.fn().mockReturnValue(true) as Mock<() => boolean>, | |
| getCoreTools: vi.fn().mockReturnValue([]) as Mock<() => string[]>, | |
| getToolDiscoveryCommand: vi.fn().mockReturnValue(undefined) as Mock< | |
| () => string | undefined | |
| >, | |
| getTargetDir: vi.fn().mockReturnValue('.') as Mock<() => string>, | |
| getApiKey: vi.fn().mockReturnValue('test-api-key') as Mock< | |
| () => string | |
| >, | |
| getModel: vi.fn().mockReturnValue('test-model') as Mock<() => string>, | |
| getSandbox: vi.fn().mockReturnValue(false) as Mock< | |
| () => boolean | string | |
| >, | |
| getDebugMode: vi.fn().mockReturnValue(false) as Mock<() => boolean>, | |
| getQuestion: vi.fn().mockReturnValue(undefined) as Mock< | |
| () => string | undefined | |
| >, | |
| getUserAgent: vi.fn().mockReturnValue('test-user-agent') as Mock< | |
| () => string | |
| >, | |
| getUserMemory: vi.fn().mockReturnValue('') as Mock<() => string>, | |
| getGeminiMdFileCount: vi.fn().mockReturnValue(0) as Mock<() => number>, | |
| getToolRegistry: vi | |
| .fn() | |
| .mockReturnValue({ discoverTools: vi.fn() }) as Mock< | |
| () => { discoverTools: Mock<() => void> } | |
| >, | |
| getRemoteAdminSettings: vi.fn().mockReturnValue(undefined) as Mock< | |
| () => { strictModeDisabled?: boolean } | undefined | |
| >, | |
| }; | |
| instanceSetApprovalModeMock.mockImplementation((value: ApprovalMode) => { | |
| instanceGetApprovalModeMock.mockReturnValue(value); | |
| }); | |
| return instance; | |
| }); | |
| mockedUseKeypress = useKeypress as MockedFunction<typeof useKeypress>; | |
| mockedUseKeypress.mockImplementation( | |
| (handler: UseKeypressHandler, _options) => { | |
| capturedUseKeypressHandler = handler; | |
| }, | |
| ); | |
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | |
| mockConfigInstance = new (Config as any)() as MockConfigInstanceShape; | |
| }); | |
| it('should initialize with ApprovalMode.AUTO_EDIT if config.getApprovalMode returns ApprovalMode.AUTO_EDIT', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.AUTO_EDIT); | |
| const { result } = await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| addItem: vi.fn(), | |
| }), | |
| ); | |
| expect(result.current).toBe(ApprovalMode.AUTO_EDIT); | |
| expect(mockConfigInstance.getApprovalMode).toHaveBeenCalledTimes(1); | |
| }); | |
| it('should initialize with ApprovalMode.DEFAULT if config.getApprovalMode returns ApprovalMode.DEFAULT', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT); | |
| const { result } = await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| addItem: vi.fn(), | |
| }), | |
| ); | |
| expect(result.current).toBe(ApprovalMode.DEFAULT); | |
| expect(mockConfigInstance.getApprovalMode).toHaveBeenCalledTimes(1); | |
| }); | |
| it('should initialize with ApprovalMode.YOLO if config.getApprovalMode returns ApprovalMode.YOLO', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.YOLO); | |
| const { result } = await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| addItem: vi.fn(), | |
| }), | |
| ); | |
| expect(result.current).toBe(ApprovalMode.YOLO); | |
| expect(mockConfigInstance.getApprovalMode).toHaveBeenCalledTimes(1); | |
| }); | |
| it('should cycle the indicator and update config when Shift+Tab or Ctrl+Y is pressed', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT); | |
| const { result } = await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| addItem: vi.fn(), | |
| }), | |
| ); | |
| expect(result.current).toBe(ApprovalMode.DEFAULT); | |
| // Shift+Tab cycles to AUTO_EDIT | |
| act(() => { | |
| capturedUseKeypressHandler({ | |
| name: 'tab', | |
| shift: true, | |
| } as Key); | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith( | |
| ApprovalMode.AUTO_EDIT, | |
| ); | |
| expect(result.current).toBe(ApprovalMode.AUTO_EDIT); | |
| act(() => { | |
| capturedUseKeypressHandler({ name: 'y', ctrl: true } as Key); | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith( | |
| ApprovalMode.YOLO, | |
| ); | |
| expect(result.current).toBe(ApprovalMode.YOLO); | |
| // Shift+Tab cycles back to AUTO_EDIT (from YOLO) | |
| act(() => { | |
| capturedUseKeypressHandler({ | |
| name: 'tab', | |
| shift: true, | |
| } as Key); | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith( | |
| ApprovalMode.AUTO_EDIT, | |
| ); | |
| expect(result.current).toBe(ApprovalMode.AUTO_EDIT); | |
| // Ctrl+Y toggles YOLO | |
| act(() => { | |
| capturedUseKeypressHandler({ name: 'y', ctrl: true } as Key); | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith( | |
| ApprovalMode.YOLO, | |
| ); | |
| expect(result.current).toBe(ApprovalMode.YOLO); | |
| // Shift+Tab from YOLO jumps to AUTO_EDIT | |
| act(() => { | |
| capturedUseKeypressHandler({ | |
| name: 'tab', | |
| shift: true, | |
| } as Key); | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith( | |
| ApprovalMode.AUTO_EDIT, | |
| ); | |
| expect(result.current).toBe(ApprovalMode.AUTO_EDIT); | |
| }); | |
| it('should not toggle if only one key or other keys combinations are pressed', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT); | |
| await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| addItem: vi.fn(), | |
| }), | |
| ); | |
| act(() => { | |
| capturedUseKeypressHandler({ | |
| name: 'tab', | |
| shift: false, | |
| } as Key); | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).not.toHaveBeenCalled(); | |
| act(() => { | |
| capturedUseKeypressHandler({ | |
| name: 'unknown', | |
| shift: true, | |
| } as Key); | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).not.toHaveBeenCalled(); | |
| act(() => { | |
| capturedUseKeypressHandler({ | |
| name: 'a', | |
| shift: false, | |
| ctrl: false, | |
| } as Key); | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).not.toHaveBeenCalled(); | |
| act(() => { | |
| capturedUseKeypressHandler({ name: 'y', ctrl: false } as Key); | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).not.toHaveBeenCalled(); | |
| act(() => { | |
| capturedUseKeypressHandler({ name: 'a', ctrl: true } as Key); | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).not.toHaveBeenCalled(); | |
| act(() => { | |
| capturedUseKeypressHandler({ name: 'y', shift: true } as Key); | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).not.toHaveBeenCalled(); | |
| act(() => { | |
| capturedUseKeypressHandler({ | |
| name: 'a', | |
| shift: true, | |
| ctrl: true, | |
| } as Key); | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).not.toHaveBeenCalled(); | |
| }); | |
| it('should update indicator when config value changes externally (useEffect dependency)', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT); | |
| const { result, rerender } = await renderHook( | |
| (props: { config: ActualConfigType; addItem: () => void }) => | |
| useApprovalModeIndicator(props), | |
| { | |
| initialProps: { | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| addItem: vi.fn(), | |
| }, | |
| }, | |
| ); | |
| expect(result.current).toBe(ApprovalMode.DEFAULT); | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.AUTO_EDIT); | |
| rerender({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| addItem: vi.fn(), | |
| }); | |
| expect(result.current).toBe(ApprovalMode.AUTO_EDIT); | |
| expect(mockConfigInstance.getApprovalMode).toHaveBeenCalledTimes(3); | |
| }); | |
| describe('in untrusted folders', () => { | |
| beforeEach(() => { | |
| mockConfigInstance.isTrustedFolder.mockReturnValue(false); | |
| }); | |
| it('should not enable YOLO mode when Ctrl+Y is pressed', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT); | |
| mockConfigInstance.setApprovalMode.mockImplementation(() => { | |
| throw new Error( | |
| 'Cannot enable privileged approval modes in an untrusted folder.', | |
| ); | |
| }); | |
| const mockAddItem = vi.fn(); | |
| const { result } = await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| addItem: mockAddItem, | |
| }), | |
| ); | |
| expect(result.current).toBe(ApprovalMode.DEFAULT); | |
| act(() => { | |
| capturedUseKeypressHandler({ name: 'y', ctrl: true } as Key); | |
| }); | |
| // We expect setApprovalMode to be called, and the error to be caught. | |
| expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith( | |
| ApprovalMode.YOLO, | |
| ); | |
| expect(mockAddItem).toHaveBeenCalled(); | |
| // Verify the underlying config value was not changed | |
| expect(mockConfigInstance.getApprovalMode()).toBe(ApprovalMode.DEFAULT); | |
| }); | |
| it('should not enable AUTO_EDIT mode when Shift+Tab is pressed', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT); | |
| mockConfigInstance.setApprovalMode.mockImplementation(() => { | |
| throw new Error( | |
| 'Cannot enable privileged approval modes in an untrusted folder.', | |
| ); | |
| }); | |
| const mockAddItem = vi.fn(); | |
| const { result } = await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| addItem: mockAddItem, | |
| }), | |
| ); | |
| expect(result.current).toBe(ApprovalMode.DEFAULT); | |
| act(() => { | |
| capturedUseKeypressHandler({ | |
| name: 'tab', | |
| shift: true, | |
| } as Key); | |
| }); | |
| // We expect setApprovalMode to be called, and the error to be caught. | |
| expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith( | |
| ApprovalMode.AUTO_EDIT, | |
| ); | |
| expect(mockAddItem).toHaveBeenCalled(); | |
| // Verify the underlying config value was not changed | |
| expect(mockConfigInstance.getApprovalMode()).toBe(ApprovalMode.DEFAULT); | |
| }); | |
| it('should disable YOLO mode when Ctrl+Y is pressed', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.YOLO); | |
| const mockAddItem = vi.fn(); | |
| await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| addItem: mockAddItem, | |
| }), | |
| ); | |
| act(() => { | |
| capturedUseKeypressHandler({ name: 'y', ctrl: true } as Key); | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith( | |
| ApprovalMode.DEFAULT, | |
| ); | |
| expect(mockConfigInstance.getApprovalMode()).toBe(ApprovalMode.DEFAULT); | |
| }); | |
| it('should disable AUTO_EDIT mode when Shift+Tab is pressed', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue( | |
| ApprovalMode.AUTO_EDIT, | |
| ); | |
| const mockAddItem = vi.fn(); | |
| await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| addItem: mockAddItem, | |
| }), | |
| ); | |
| act(() => { | |
| capturedUseKeypressHandler({ | |
| name: 'tab', | |
| shift: true, | |
| } as Key); | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith( | |
| ApprovalMode.DEFAULT, | |
| ); | |
| expect(mockConfigInstance.getApprovalMode()).toBe(ApprovalMode.DEFAULT); | |
| }); | |
| it('should show a warning when trying to enable privileged modes', async () => { | |
| // Mock the error thrown by setApprovalMode | |
| const errorMessage = | |
| 'Cannot enable privileged approval modes in an untrusted folder.'; | |
| mockConfigInstance.setApprovalMode.mockImplementation(() => { | |
| throw new Error(errorMessage); | |
| }); | |
| const mockAddItem = vi.fn(); | |
| await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| addItem: mockAddItem, | |
| }), | |
| ); | |
| // Try to enable YOLO mode | |
| act(() => { | |
| capturedUseKeypressHandler({ name: 'y', ctrl: true } as Key); | |
| }); | |
| expect(mockAddItem).toHaveBeenCalledWith( | |
| { | |
| type: MessageType.INFO, | |
| text: errorMessage, | |
| }, | |
| expect.any(Number), | |
| ); | |
| // Try to enable AUTO_EDIT mode | |
| act(() => { | |
| capturedUseKeypressHandler({ | |
| name: 'tab', | |
| shift: true, | |
| } as Key); | |
| }); | |
| expect(mockAddItem).toHaveBeenCalledWith( | |
| { | |
| type: MessageType.INFO, | |
| text: errorMessage, | |
| }, | |
| expect.any(Number), | |
| ); | |
| expect(mockAddItem).toHaveBeenCalledTimes(2); | |
| }); | |
| }); | |
| describe('when YOLO mode is disabled by settings', () => { | |
| beforeEach(() => { | |
| // Ensure isYoloModeDisabled returns true for these tests | |
| if (mockConfigInstance && mockConfigInstance.isYoloModeDisabled) { | |
| mockConfigInstance.isYoloModeDisabled.mockReturnValue(true); | |
| } | |
| }); | |
| it('should not enable YOLO mode when Ctrl+Y is pressed and add an info message', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT); | |
| mockConfigInstance.getRemoteAdminSettings.mockReturnValue({ | |
| strictModeDisabled: true, | |
| }); | |
| const mockAddItem = vi.fn(); | |
| const { result } = await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| addItem: mockAddItem, | |
| }), | |
| ); | |
| expect(result.current).toBe(ApprovalMode.DEFAULT); | |
| act(() => { | |
| capturedUseKeypressHandler({ name: 'y', ctrl: true } as Key); | |
| }); | |
| // setApprovalMode should not be called because the check should return early | |
| expect(mockConfigInstance.setApprovalMode).not.toHaveBeenCalled(); | |
| // An info message should be added | |
| expect(mockAddItem).toHaveBeenCalledWith( | |
| { | |
| type: MessageType.WARNING, | |
| text: 'You cannot enter YOLO mode since it is disabled in your settings.', | |
| }, | |
| expect.any(Number), | |
| ); | |
| // The mode should not change | |
| expect(result.current).toBe(ApprovalMode.DEFAULT); | |
| }); | |
| it('should show admin error message when YOLO mode is disabled by admin', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT); | |
| mockConfigInstance.getRemoteAdminSettings.mockReturnValue({ | |
| mcpEnabled: true, | |
| }); | |
| const mockAddItem = vi.fn(); | |
| await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| addItem: mockAddItem, | |
| }), | |
| ); | |
| act(() => { | |
| capturedUseKeypressHandler({ name: 'y', ctrl: true } as Key); | |
| }); | |
| expect(mockAddItem).toHaveBeenCalledWith( | |
| { | |
| type: MessageType.WARNING, | |
| text: '[Mock] YOLO mode is disabled', | |
| }, | |
| expect.any(Number), | |
| ); | |
| }); | |
| it('should show default error message when admin settings are empty', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT); | |
| mockConfigInstance.getRemoteAdminSettings.mockReturnValue({}); | |
| const mockAddItem = vi.fn(); | |
| await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| addItem: mockAddItem, | |
| }), | |
| ); | |
| act(() => { | |
| capturedUseKeypressHandler({ name: 'y', ctrl: true } as Key); | |
| }); | |
| expect(mockAddItem).toHaveBeenCalledWith( | |
| { | |
| type: MessageType.WARNING, | |
| text: 'You cannot enter YOLO mode since it is disabled in your settings.', | |
| }, | |
| expect.any(Number), | |
| ); | |
| }); | |
| }); | |
| it('should call onApprovalModeChange when switching to YOLO mode', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT); | |
| const mockOnApprovalModeChange = vi.fn(); | |
| await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| onApprovalModeChange: mockOnApprovalModeChange, | |
| }), | |
| ); | |
| act(() => { | |
| capturedUseKeypressHandler({ name: 'y', ctrl: true } as Key); | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith( | |
| ApprovalMode.YOLO, | |
| ); | |
| expect(mockOnApprovalModeChange).toHaveBeenCalledWith(ApprovalMode.YOLO); | |
| }); | |
| it('should call onApprovalModeChange when switching to AUTO_EDIT mode', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT); | |
| const mockOnApprovalModeChange = vi.fn(); | |
| await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| onApprovalModeChange: mockOnApprovalModeChange, | |
| }), | |
| ); | |
| act(() => { | |
| capturedUseKeypressHandler({ name: 'tab', shift: true } as Key); | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith( | |
| ApprovalMode.AUTO_EDIT, | |
| ); | |
| expect(mockOnApprovalModeChange).toHaveBeenCalledWith( | |
| ApprovalMode.AUTO_EDIT, | |
| ); | |
| }); | |
| it('should call onApprovalModeChange when switching to DEFAULT mode', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.YOLO); | |
| const mockOnApprovalModeChange = vi.fn(); | |
| await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| onApprovalModeChange: mockOnApprovalModeChange, | |
| }), | |
| ); | |
| act(() => { | |
| capturedUseKeypressHandler({ name: 'y', ctrl: true } as Key); // This should toggle from YOLO to DEFAULT | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith( | |
| ApprovalMode.DEFAULT, | |
| ); | |
| expect(mockOnApprovalModeChange).toHaveBeenCalledWith(ApprovalMode.DEFAULT); | |
| }); | |
| it('should not call onApprovalModeChange when callback is not provided', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT); | |
| await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| }), | |
| ); | |
| act(() => { | |
| capturedUseKeypressHandler({ name: 'y', ctrl: true } as Key); | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith( | |
| ApprovalMode.YOLO, | |
| ); | |
| // Should not throw an error when callback is not provided | |
| }); | |
| it('should handle multiple mode changes correctly', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT); | |
| const mockOnApprovalModeChange = vi.fn(); | |
| await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| onApprovalModeChange: mockOnApprovalModeChange, | |
| }), | |
| ); | |
| // Switch to YOLO | |
| act(() => { | |
| capturedUseKeypressHandler({ name: 'y', ctrl: true } as Key); | |
| }); | |
| // Switch to AUTO_EDIT | |
| act(() => { | |
| capturedUseKeypressHandler({ name: 'tab', shift: true } as Key); | |
| }); | |
| expect(mockOnApprovalModeChange).toHaveBeenCalledTimes(2); | |
| expect(mockOnApprovalModeChange).toHaveBeenNthCalledWith( | |
| 1, | |
| ApprovalMode.YOLO, | |
| ); | |
| expect(mockOnApprovalModeChange).toHaveBeenNthCalledWith( | |
| 2, | |
| ApprovalMode.AUTO_EDIT, | |
| ); | |
| }); | |
| it('should cycle to PLAN when allowPlanMode is true', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.AUTO_EDIT); | |
| await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| addItem: vi.fn(), | |
| allowPlanMode: true, | |
| }), | |
| ); | |
| // AUTO_EDIT -> PLAN | |
| act(() => { | |
| capturedUseKeypressHandler({ name: 'tab', shift: true } as Key); | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith( | |
| ApprovalMode.PLAN, | |
| ); | |
| }); | |
| it('should cycle to DEFAULT when allowPlanMode is false', async () => { | |
| mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.AUTO_EDIT); | |
| await renderHook(() => | |
| useApprovalModeIndicator({ | |
| config: mockConfigInstance as unknown as ActualConfigType, | |
| addItem: vi.fn(), | |
| allowPlanMode: false, | |
| }), | |
| ); | |
| // AUTO_EDIT -> DEFAULT | |
| act(() => { | |
| capturedUseKeypressHandler({ name: 'tab', shift: true } as Key); | |
| }); | |
| expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith( | |
| ApprovalMode.DEFAULT, | |
| ); | |
| }); | |
| }); | |