| |
| |
| |
| |
| |
|
|
| import { renderWithProviders } from '../../test-utils/render.js'; |
| import { waitFor } from '../../test-utils/async.js'; |
| import { describe, it, expect, vi, beforeEach } from 'vitest'; |
| import { act } from 'react'; |
| import { AgentConfigDialog } from './AgentConfigDialog.js'; |
| import { LoadedSettings, SettingScope } from '../../config/settings.js'; |
| import type { AgentDefinition } from '@google/gemini-cli-core'; |
|
|
| enum TerminalKeys { |
| ENTER = '\u000D', |
| TAB = '\t', |
| UP_ARROW = '\u001B[A', |
| DOWN_ARROW = '\u001B[B', |
| ESCAPE = '\u001B', |
| } |
|
|
| const createMockSettings = ( |
| userSettings = {}, |
| workspaceSettings = {}, |
| ): LoadedSettings => { |
| const settings = new LoadedSettings( |
| { |
| settings: { ui: { customThemes: {} }, mcpServers: {}, agents: {} }, |
| originalSettings: { |
| ui: { customThemes: {} }, |
| mcpServers: {}, |
| agents: {}, |
| }, |
| path: '/system/settings.json', |
| }, |
| { |
| settings: {}, |
| originalSettings: {}, |
| path: '/system/system-defaults.json', |
| }, |
| { |
| settings: { |
| ui: { customThemes: {} }, |
| mcpServers: {}, |
| agents: { overrides: {} }, |
| ...userSettings, |
| }, |
| originalSettings: { |
| ui: { customThemes: {} }, |
| mcpServers: {}, |
| agents: { overrides: {} }, |
| ...userSettings, |
| }, |
| path: '/user/settings.json', |
| }, |
| { |
| settings: { |
| ui: { customThemes: {} }, |
| mcpServers: {}, |
| agents: { overrides: {} }, |
| ...workspaceSettings, |
| }, |
| originalSettings: { |
| ui: { customThemes: {} }, |
| mcpServers: {}, |
| agents: { overrides: {} }, |
| ...workspaceSettings, |
| }, |
| path: '/workspace/settings.json', |
| }, |
| true, |
| [], |
| ); |
|
|
| |
| settings.setValue = vi.fn(); |
|
|
| return settings; |
| }; |
|
|
| const createMockAgentDefinition = ( |
| overrides: Partial<AgentDefinition> = {}, |
| ): AgentDefinition => |
| ({ |
| name: 'test-agent', |
| displayName: 'Test Agent', |
| description: 'A test agent for testing', |
| kind: 'local', |
| modelConfig: { |
| model: 'inherit', |
| generateContentConfig: { |
| temperature: 1.0, |
| }, |
| }, |
| runConfig: { |
| maxTimeMinutes: 5, |
| maxTurns: 10, |
| }, |
| experimental: false, |
| ...overrides, |
| }) as AgentDefinition; |
|
|
| describe('AgentConfigDialog', () => { |
| let mockOnClose: ReturnType<typeof vi.fn>; |
| let mockOnSave: ReturnType<typeof vi.fn>; |
|
|
| beforeEach(() => { |
| vi.clearAllMocks(); |
| mockOnClose = vi.fn(); |
| mockOnSave = vi.fn(); |
| }); |
|
|
| const renderDialog = async ( |
| settings: LoadedSettings, |
| definition: AgentDefinition = createMockAgentDefinition(), |
| ) => { |
| const result = await renderWithProviders( |
| <AgentConfigDialog |
| agentName="test-agent" |
| displayName="Test Agent" |
| definition={definition} |
| settings={settings} |
| onClose={mockOnClose} |
| onSave={mockOnSave} |
| />, |
| { settings, uiState: { mainAreaWidth: 100 } }, |
| ); |
| return result; |
| }; |
|
|
| describe('rendering', () => { |
| it('should render the dialog with title', async () => { |
| const settings = createMockSettings(); |
| const { lastFrame, unmount } = await renderDialog(settings); |
| expect(lastFrame()).toContain('Configure: Test Agent'); |
| unmount(); |
| }); |
|
|
| it('should render all configuration fields', async () => { |
| const settings = createMockSettings(); |
| const { lastFrame, unmount } = await renderDialog(settings); |
| const frame = lastFrame(); |
|
|
| expect(frame).toContain('Enabled'); |
| expect(frame).toContain('Model'); |
| expect(frame).toContain('Temperature'); |
| expect(frame).toContain('Top P'); |
| expect(frame).toContain('Top K'); |
| expect(frame).toContain('Max Output Tokens'); |
| expect(frame).toContain('Max Time (minutes)'); |
| expect(frame).toContain('Max Turns'); |
| unmount(); |
| }); |
|
|
| it('should render scope selector', async () => { |
| const settings = createMockSettings(); |
| const { lastFrame, unmount } = await renderDialog(settings); |
|
|
| expect(lastFrame()).toContain('Apply To'); |
| expect(lastFrame()).toContain('User Settings'); |
| expect(lastFrame()).toContain('Workspace Settings'); |
| unmount(); |
| }); |
|
|
| it('should render help text', async () => { |
| const settings = createMockSettings(); |
| const { lastFrame, unmount } = await renderDialog(settings); |
|
|
| expect(lastFrame()).toContain('Use Enter to select'); |
| expect(lastFrame()).toContain('Tab to change focus'); |
| expect(lastFrame()).toContain('Esc to close'); |
| unmount(); |
| }); |
| }); |
|
|
| describe('keyboard navigation', () => { |
| it('should close dialog on Escape', async () => { |
| const settings = createMockSettings(); |
| const { stdin, waitUntilReady, unmount } = await renderDialog(settings); |
|
|
| await act(async () => { |
| stdin.write(TerminalKeys.ESCAPE); |
| }); |
| |
| await act(async () => { |
| await waitUntilReady(); |
| }); |
|
|
| await waitFor(() => { |
| expect(mockOnClose).toHaveBeenCalled(); |
| }); |
| unmount(); |
| }); |
|
|
| it('should navigate down with arrow key', async () => { |
| const settings = createMockSettings(); |
| const { lastFrame, stdin, waitUntilReady, unmount } = |
| await renderDialog(settings); |
|
|
| |
| expect(lastFrame()).toContain('●'); |
|
|
| |
| await act(async () => { |
| stdin.write(TerminalKeys.DOWN_ARROW); |
| }); |
| await waitUntilReady(); |
|
|
| await waitFor(() => { |
| |
| expect(lastFrame()).toContain('Model'); |
| }); |
| unmount(); |
| }); |
|
|
| it('should switch focus with Tab', async () => { |
| const settings = createMockSettings(); |
| const { lastFrame, stdin, waitUntilReady, unmount } = |
| await renderDialog(settings); |
|
|
| |
| expect(lastFrame()).toContain('> Configure: Test Agent'); |
|
|
| |
| await act(async () => { |
| stdin.write(TerminalKeys.TAB); |
| }); |
| await waitUntilReady(); |
|
|
| await waitFor(() => { |
| expect(lastFrame()).toContain('> Apply To'); |
| }); |
| unmount(); |
| }); |
| }); |
|
|
| describe('boolean toggle', () => { |
| it('should toggle enabled field on Enter', async () => { |
| const settings = createMockSettings(); |
| const { stdin, waitUntilReady, unmount } = await renderDialog(settings); |
|
|
| |
| await act(async () => { |
| stdin.write(TerminalKeys.ENTER); |
| }); |
| await waitUntilReady(); |
|
|
| await waitFor(() => { |
| expect(settings.setValue).toHaveBeenCalledWith( |
| SettingScope.User, |
| 'agents.overrides.test-agent.enabled', |
| false, |
| ); |
| expect(mockOnSave).toHaveBeenCalled(); |
| }); |
| unmount(); |
| }); |
| }); |
|
|
| describe('default values', () => { |
| it('should show values from agent definition as defaults', async () => { |
| const definition = createMockAgentDefinition({ |
| modelConfig: { |
| model: 'gemini-2.0-flash', |
| generateContentConfig: { |
| temperature: 0.7, |
| }, |
| }, |
| runConfig: { |
| maxTimeMinutes: 10, |
| maxTurns: 20, |
| }, |
| }); |
| const settings = createMockSettings(); |
| const { lastFrame, unmount } = await renderDialog(settings, definition); |
| const frame = lastFrame(); |
|
|
| expect(frame).toContain('gemini-2.0-flash'); |
| expect(frame).toContain('0.7'); |
| expect(frame).toContain('10'); |
| expect(frame).toContain('20'); |
| unmount(); |
| }); |
|
|
| it('should show experimental agents as disabled by default', async () => { |
| const definition = createMockAgentDefinition({ |
| experimental: true, |
| }); |
| const settings = createMockSettings(); |
| const { lastFrame, unmount } = await renderDialog(settings, definition); |
|
|
| |
| expect(lastFrame()).toContain('false'); |
| unmount(); |
| }); |
| }); |
|
|
| describe('existing overrides', () => { |
| it('should show existing override values with * indicator', async () => { |
| const settings = createMockSettings({ |
| agents: { |
| overrides: { |
| 'test-agent': { |
| enabled: false, |
| modelConfig: { |
| model: 'custom-model', |
| }, |
| }, |
| }, |
| }, |
| }); |
| const { lastFrame, unmount } = await renderDialog(settings); |
| const frame = lastFrame(); |
|
|
| |
| expect(frame).toContain('custom-model'); |
| expect(frame).toContain('false'); |
| unmount(); |
| }); |
| it('should respond to availableTerminalHeight and truncate list', async () => { |
| const settings = createMockSettings(); |
| |
| |
| const { lastFrame, unmount } = await renderWithProviders( |
| <AgentConfigDialog |
| agentName="test-agent" |
| displayName="Test Agent" |
| definition={createMockAgentDefinition()} |
| settings={settings} |
| onClose={mockOnClose} |
| onSave={mockOnSave} |
| availableTerminalHeight={20} |
| />, |
| { settings, uiState: { mainAreaWidth: 100 } }, |
| ); |
| await waitFor(() => |
| expect(lastFrame()).toContain('Configure: Test Agent'), |
| ); |
|
|
| const frame = lastFrame(); |
| |
| expect(frame).toContain('▼'); |
| unmount(); |
| }); |
| }); |
| }); |
|
|