import { describe, it, expect } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import { ToolInvocationCard } from '../../chat/tool-invocation-card'
describe('ToolInvocationCard', () => {
// ============================================================
// 基础渲染测试
// ============================================================
describe('基础渲染', () => {
it('应正确渲染工具名称', () => {
render()
expect(screen.getByText('read_file')).toBeInTheDocument()
})
it('应设置正确的 displayName', () => {
expect(ToolInvocationCard.displayName).toBe('ToolInvocationCard')
})
it('应渲染参数文本', () => {
render(
)
expect(screen.getByText('query: hello world')).toBeInTheDocument()
})
it('无参数时不应渲染参数区域', () => {
const { container } = render(
)
// 只有工具名和状态文本
expect(container.textContent).toContain('list_dir')
})
})
// ============================================================
// 状态图标测试
// ============================================================
describe('状态图标', () => {
it('running 状态应显示时间图标', () => {
const { container } = render(
)
// 默认 timeIcon 使用 TimeLine 组件
expect(container.querySelector('svg')).toBeInTheDocument()
})
it('success 状态应显示勾选图标', () => {
const { container } = render(
)
expect(container.querySelector('svg')).toBeInTheDocument()
})
it('error 状态应显示关闭图标', () => {
const { container } = render(
)
expect(container.querySelector('svg')).toBeInTheDocument()
})
it('应接受自定义图标', () => {
render(
✓}
/>
)
expect(screen.getByTestId('custom-check')).toBeInTheDocument()
})
})
// ============================================================
// 状态文本测试
// ============================================================
describe('状态文本', () => {
it('running 状态应显示默认文本 "Running"', () => {
render()
expect(screen.getByText('Running')).toBeInTheDocument()
})
it('success 状态应显示默认文本 "Ran"', () => {
render()
expect(screen.getByText('Ran')).toBeInTheDocument()
})
it('error 状态应显示默认文本 "Failed"', () => {
render()
expect(screen.getByText('Failed')).toBeInTheDocument()
})
it('应接受自定义 statusText', () => {
render(
)
expect(screen.getByText('执行完成')).toBeInTheDocument()
})
it('statusText 为空字符串时不应显示状态文本', () => {
render(
)
expect(screen.queryByText('Ran')).not.toBeInTheDocument()
})
it('应接受自定义状态文本 props', () => {
render(
)
expect(screen.getByText('执行中')).toBeInTheDocument()
})
})
// ============================================================
// 响应展示测试
// ============================================================
describe('响应展示', () => {
it('有响应时应可展开', () => {
render(
)
// 点击展开
fireEvent.click(screen.getByText('test'))
expect(screen.getByText('Tool output result')).toBeInTheDocument()
})
it('defaultExpanded=true 时应默认展开', () => {
render(
)
expect(screen.getByText('Already expanded')).toBeInTheDocument()
})
it('无响应时不应显示展开箭头', () => {
const { container } = render(
)
// 没有箭头图标(ArrowRightLine 的 SVG)
const arrows = container.querySelectorAll('[class*="rotate"]')
expect(arrows).toHaveLength(0)
})
it('running 状态时不应可展开', () => {
render(
)
// running 状态下点击不应展开
fireEvent.click(screen.getByText('test'))
expect(screen.queryByText('Some response')).not.toBeInTheDocument()
})
it('点击应切换展开/收起状态', () => {
render(
)
const trigger = screen.getByText('toggle-test')
// 第一次点击展开
fireEvent.click(trigger)
expect(screen.getByText('Toggle content')).toBeInTheDocument()
// 第二次点击收起
fireEvent.click(trigger)
expect(screen.queryByText('Toggle content')).not.toBeInTheDocument()
})
})
// ============================================================
// 响应标签测试
// ============================================================
describe('响应标签', () => {
it('展开时应显示默认标签 "Response"', () => {
render(
)
expect(screen.getByText('Response')).toBeInTheDocument()
})
it('应接受自定义 responseLabel', () => {
render(
)
expect(screen.getByText('输出结果')).toBeInTheDocument()
})
})
// ============================================================
// 错误状态样式测试
// ============================================================
describe('错误状态', () => {
it('error 状态响应应应用错误样式', () => {
render(
)
const errorText = screen.getByText('Error message')
expect(errorText).toHaveClass('text-error')
})
it('success 状态响应不应有错误样式', () => {
render(
)
const successText = screen.getByText('Success message')
expect(successText).not.toHaveClass('text-error')
})
})
// ============================================================
// className 测试
// ============================================================
describe('className', () => {
it('应合并自定义 className', () => {
const { container } = render(
)
expect(container.firstChild).toHaveClass('custom-card')
})
})
// ============================================================
// 自定义图标 props 测试
// ============================================================
describe('自定义图标 props', () => {
it('应接受自定义 timeIcon', () => {
render(
⏱}
/>
)
expect(screen.getByTestId('time-icon')).toBeInTheDocument()
})
it('应接受自定义 closeCircleIcon', () => {
render(
✗}
/>
)
expect(screen.getByTestId('close-icon')).toBeInTheDocument()
})
it('应接受自定义 arrowRightIcon', () => {
render(
→}
/>
)
expect(screen.getByTestId('arrow-icon')).toBeInTheDocument()
})
})
// ============================================================
// 组合场景测试
// ============================================================
describe('组合场景', () => {
it('应支持所有 props 组合', () => {
render(
✓}
arrowRightIcon={→}
/>
)
expect(screen.getByText('full_test')).toBeInTheDocument()
expect(screen.getByText('/path/to/file.ts')).toBeInTheDocument()
expect(screen.getByText('已完成')).toBeInTheDocument()
expect(screen.getByText('文件内容')).toBeInTheDocument()
expect(screen.getByText('File content here...')).toBeInTheDocument()
expect(screen.getByTestId('check')).toBeInTheDocument()
expect(screen.getByTestId('arrow')).toBeInTheDocument()
})
})
})