import { describe, it, expect } from 'vitest' import { render, screen } from '@testing-library/react' import { ThinkingIndicator } from '../../chat/thinking-indicator' describe('ThinkingIndicator', () => { // ============================================================ // 渲染测试 // ============================================================ describe('渲染', () => { it('应正确渲染组件', () => { const { container } = render() expect(container.firstChild).toBeInTheDocument() }) it('应设置正确的 displayName', () => { expect(ThinkingIndicator.displayName).toBe('ThinkingIndicator') }) }) // ============================================================ // 文案测试 // ============================================================ describe('text 文案', () => { it('应显示默认文案 "Spark is Thinking..."', () => { render() expect(screen.getByText('Spark is Thinking...')).toBeInTheDocument() }) it('应显示自定义文案', () => { render() expect(screen.getByText('正在思考...')).toBeInTheDocument() }) it('空文案应正常渲染', () => { const { container } = render() expect(container.firstChild).toBeInTheDocument() }) it('长文案应正常渲染', () => { const longText = 'AI is analyzing your complex multi-step request with deep reasoning...' render() expect(screen.getByText(longText)).toBeInTheDocument() }) }) // ============================================================ // Loader Slot 测试 // ============================================================ describe('loader slot', () => { it('无 loader prop 时应渲染默认三点动画', () => { const { container } = render() expect(container.querySelector('.thinking-dots')).toBeInTheDocument() }) it('默认动画应包含三个点', () => { const { container } = render() const dots = container.querySelectorAll('.thinking-dots span') expect(dots).toHaveLength(3) }) it('有 loader prop 时应渲染自定义 loader', () => { render( Custom Loader} /> ) expect(screen.getByTestId('custom-loader')).toBeInTheDocument() }) it('自定义 loader 时不应渲染默认动画', () => { const { container } = render( Custom} /> ) expect(container.querySelector('.thinking-dots')).not.toBeInTheDocument() }) it('loader 可以是复杂 React 节点', () => { render( Loading ... } /> ) expect(screen.getByTestId('complex-loader')).toBeInTheDocument() }) }) // ============================================================ // Shimmer 效果测试 // ============================================================ describe('shimmer 效果', () => { it('默认应启用 shimmer 效果', () => { render() // shimmer 使用 motion.span,在测试环境被 mock 为普通 span expect(screen.getByText('Test')).toBeInTheDocument() }) it('shimmer=true 时文本应有渐变样式类', () => { render() const textElement = screen.getByText('Shimmer Text') expect(textElement).toHaveClass('bg-clip-text') expect(textElement).toHaveClass('text-transparent') }) it('shimmer=false 时应显示静态文本', () => { render() const textElement = screen.getByText('Static Text') expect(textElement).toHaveClass('text-text-secondary') expect(textElement).not.toHaveClass('text-transparent') }) }) // ============================================================ // Shimmer 配置测试 // ============================================================ describe('shimmer 配置', () => { it('应接受自定义 shimmerDuration', () => { // shimmerDuration 用于动画,测试环境动画被 mock,仅验证 prop 接受 const { container } = render( ) expect(container.firstChild).toBeInTheDocument() }) it('应接受自定义 shimmerSpread', () => { const { container } = render( ) expect(container.firstChild).toBeInTheDocument() }) }) // ============================================================ // textClassName 测试 // ============================================================ describe('textClassName', () => { it('应将 textClassName 应用到文本元素', () => { render( ) const textElement = screen.getByText('Styled Text') expect(textElement).toHaveClass('custom-text-class') }) it('textClassName 应与默认类名共存', () => { render( ) const textElement = screen.getByText('Mixed Classes') expect(textElement).toHaveClass('my-class') expect(textElement).toHaveClass('text-sm') }) }) // ============================================================ // className 覆盖测试 // ============================================================ describe('className', () => { it('应合并自定义 className 到根元素', () => { const { container } = render( ) expect(container.firstChild).toHaveClass('custom-root-class') }) it('自定义 className 应与默认布局类共存', () => { const { container } = render( ) expect(container.firstChild).toHaveClass('my-indicator') expect(container.firstChild).toHaveClass('inline-flex') expect(container.firstChild).toHaveClass('items-center') }) }) // ============================================================ // 原生属性透传测试 // ============================================================ describe('原生属性透传', () => { it('应透传 data-* 属性', () => { render() expect(screen.getByTestId('thinking')).toBeInTheDocument() }) it('应透传 id 属性', () => { const { container } = render() expect(container.querySelector('#my-indicator')).toBeInTheDocument() }) it('应透传 aria-* 属性', () => { const { container } = render( ) expect(container.firstChild).toHaveAttribute('aria-label', 'AI is thinking') }) }) // ============================================================ // 组合场景测试 // ============================================================ describe('组合场景', () => { it('应支持所有 props 组合', () => { render( ⟳} /> ) const root = screen.getByTestId('full-config') expect(root).toHaveClass('root-class') expect(screen.getByText('完整配置测试')).toHaveClass('text-class') expect(screen.getByTestId('custom-spinner')).toBeInTheDocument() }) it('shimmer=false 时 loader 仍可自定义', () => { render( ●} /> ) expect(screen.getByTestId('static-loader')).toBeInTheDocument() expect(screen.getByText('No Shimmer')).toHaveClass('text-text-secondary') }) }) })