import { beforeEach, describe, expect, it } from 'vitest' import { fireEvent, render, screen } from '@testing-library/react' import { ThinkingBlock } from './ThinkingBlock' import { ToolCallBlock } from './ToolCallBlock' import { PermissionDialog } from './PermissionDialog' import { useChatStore } from '../../stores/chatStore' import { useSettingsStore } from '../../stores/settingsStore' import { useTabStore } from '../../stores/tabStore' describe('chat blocks', () => { beforeEach(() => { useSettingsStore.setState({ locale: 'en' }) useTabStore.setState({ activeTabId: 'active-tab', tabs: [{ sessionId: 'active-tab', title: 'Test', type: 'session' as const, status: 'idle' }] }) useChatStore.setState({ sessions: {} }) }) it('keeps thinking collapsed by default', () => { const { container } = render() expect(screen.getByText(/Thinking/)).toBeTruthy() expect(container.textContent).toContain('this is a long internal reasoning trace') expect(container.querySelector('.thinking-cursor')).toBeNull() }) it('does not animate inactive historical thinking blocks', () => { const { container } = render() expect(container.querySelector('.thinking-inline-cursor')).toBeNull() }) it('shows tool previews only after expanding the tool block', () => { const { container } = render( , ) expect(container.textContent).toContain('Read') expect(container.textContent).not.toContain('const answer = 42') fireEvent.click(screen.getByRole('button')) expect(container.textContent).toContain('Tool Input') expect(container.textContent).not.toContain('const answer = 42') }) it('does not surface bash stdout in the transcript preview', () => { const { container } = render( , ) expect(container.textContent).toContain('Bash') expect(container.textContent).not.toContain('file-a') fireEvent.click(screen.getByRole('button')) expect(container.textContent).toContain('ls -la') expect(container.textContent).not.toContain('file-a') }) it('shows a collapsed error summary for failed bash commands', () => { const { container } = render( , ) expect(container.textContent).toContain('Bash') expect(container.textContent).toContain('fatal: unrecognized argument: --no-stat') }) it('expands tool errors so full Computer Use gate messages are readable', () => { const { container } = render( , ) expect(container.textContent).toContain('mcp__computer-use__left_click') expect(container.textContent).not.toContain('Take a new screenshot') fireEvent.click(screen.getByRole('button')) expect(container.textContent).toContain('Take a new screenshot') expect(container.textContent).toContain('allowed applications') }) it('shows a diff preview for edit permission requests', () => { useChatStore.setState({ sessions: { 'active-tab': { messages: [], chatState: 'idle', connectionState: 'connected', streamingText: '', streamingToolInput: '', activeToolUseId: null, activeToolName: null, activeThinkingId: null, pendingPermission: { requestId: 'perm-1', toolName: 'Edit', input: { file_path: '/tmp/example.ts', old_string: 'const count = 1', new_string: 'const count = 2', }, }, pendingComputerUsePermission: null, tokenUsage: { input_tokens: 0, output_tokens: 0 }, elapsedSeconds: 0, statusVerb: '', slashCommands: [], agentTaskNotifications: {}, elapsedTimer: null, }, }, }) const { container } = render( , ) expect(container.textContent).toContain('/tmp/example.ts') expect(container.textContent).toContain('Allow') // react-diff-viewer-continued uses styled-components tables that don't // fully render in jsdom, so we verify the DiffViewer wrapper is mounted expect(container.querySelector('[class*="rounded-[var(--radius-lg)]"]')).toBeTruthy() }) })