import { describe, it, expect, vi, beforeEach } from 'vitest' import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { ChoiceOption, ChoiceGrid, TextInput, ConfidenceLevel, RecordingControl } from '../../shared/AnswerActions' // Mock BluePlayButton vi.mock('@/components/shared/BluePlayButton', () => ({ BluePlayButton: ({ onPlayStart, disabled, title }: any) => ( ) })) describe('ChoiceOption', () => { const mockOnSelect = vi.fn() beforeEach(() => { vi.clearAllMocks() }) describe('基礎渲染', () => { it('應該顯示選項文字', () => { render( ) expect(screen.getByText('hello')).toBeInTheDocument() expect(screen.getByLabelText('選項 1: hello')).toBeInTheDocument() }) }) describe('選擇狀態樣式', () => { it('應該在選中時應用選中樣式', () => { render( ) const button = screen.getByRole('button') expect(button).toHaveClass('border-blue-500', 'bg-blue-50', 'text-blue-700') }) it('應該在顯示結果且正確時應用正確樣式', () => { render( ) const button = screen.getByRole('button') expect(button).toHaveClass('border-green-500', 'bg-green-50', 'text-green-700') }) it('應該在顯示結果且錯誤時應用錯誤樣式', () => { render( ) const button = screen.getByRole('button') expect(button).toHaveClass('border-red-500', 'bg-red-50', 'text-red-700') }) }) describe('交互功能', () => { it('應該在點擊時調用 onSelect', async () => { const user = userEvent.setup() render( ) await user.click(screen.getByRole('button')) expect(mockOnSelect).toHaveBeenCalledWith('clickable') }) it('應該在 disabled 時不調用 onSelect', async () => { const user = userEvent.setup() render( ) await user.click(screen.getByRole('button')) expect(mockOnSelect).not.toHaveBeenCalled() }) it('應該在 showResult 時不調用 onSelect', async () => { const user = userEvent.setup() render( ) await user.click(screen.getByRole('button')) expect(mockOnSelect).not.toHaveBeenCalled() }) }) }) describe('ChoiceGrid', () => { const mockOptions = ['option1', 'option2', 'option3', 'option4'] const mockOnSelect = vi.fn() beforeEach(() => { vi.clearAllMocks() }) describe('基礎渲染', () => { it('應該渲染所有選項', () => { render( ) mockOptions.forEach(option => { expect(screen.getByText(option)).toBeInTheDocument() }) }) it('應該使用響應式網格布局', () => { const { container } = render( ) expect(container.firstChild).toHaveClass('grid', 'grid-cols-1', 'sm:grid-cols-2') }) }) describe('選擇狀態管理', () => { it('應該正確顯示選中狀態', () => { render( ) const selectedButton = screen.getByLabelText('選項 2: option2') expect(selectedButton).toHaveClass('border-blue-500') }) it('應該在顯示結果時正確標記正確答案', () => { render( ) const correctButton = screen.getByLabelText('選項 3: option3') const wrongButton = screen.getByLabelText('選項 1: option1') expect(correctButton).toHaveClass('border-green-500') expect(wrongButton).toHaveClass('border-red-500') }) }) }) describe('TextInput', () => { const mockOnChange = vi.fn() const mockOnSubmit = vi.fn() beforeEach(() => { vi.clearAllMocks() }) describe('基礎功能', () => { it('應該處理文字輸入', async () => { const user = userEvent.setup() render( ) const input = screen.getByRole('textbox') await user.type(input, 'hello') expect(mockOnChange).toHaveBeenCalledTimes(5) // 每個字符一次 }) it('應該在按Enter時提交', async () => { const user = userEvent.setup() render( ) const input = screen.getByRole('textbox') await user.type(input, '{enter}') expect(mockOnSubmit).toHaveBeenCalledWith('test answer') }) it('應該在點擊提交按鈕時提交', async () => { const user = userEvent.setup() render( ) const submitButton = screen.getByText('提交') await user.click(submitButton) expect(mockOnSubmit).toHaveBeenCalledWith('test answer') }) }) describe('提交按鈕狀態', () => { it('應該在輸入為空時禁用提交按鈕', () => { render( ) const submitButton = screen.getByText('提交') expect(submitButton).toBeDisabled() }) it('應該在有輸入時啟用提交按鈕', () => { render( ) const submitButton = screen.getByText('提交') expect(submitButton).not.toBeDisabled() }) it('應該在顯示結果時隱藏提交按鈕', () => { render( ) expect(screen.queryByText('提交')).not.toBeInTheDocument() }) }) describe('結果顯示', () => { it('應該在答錯時顯示正確答案', () => { render( ) expect(screen.getByText('正確答案:')).toBeInTheDocument() expect(screen.getByText('correct')).toBeInTheDocument() }) it('應該在答對時不顯示正確答案', () => { render( ) expect(screen.queryByText('正確答案:')).not.toBeInTheDocument() }) }) describe('輸入樣式', () => { it('應該在正確時應用綠色樣式', () => { render( ) const input = screen.getByRole('textbox') expect(input).toHaveClass('border-green-500', 'bg-green-50') }) it('應該在錯誤時應用紅色樣式', () => { render( ) const input = screen.getByRole('textbox') expect(input).toHaveClass('border-red-500', 'bg-red-50') }) }) }) describe('ConfidenceLevel', () => { const mockOnSelect = vi.fn() beforeEach(() => { vi.clearAllMocks() }) describe('基礎渲染', () => { it('應該渲染所有信心等級按鈕', () => { render( ) expect(screen.getByText('完全不熟')).toBeInTheDocument() expect(screen.getByText('完全掌握')).toBeInTheDocument() // 檢查所有等級數字 for (let i = 1; i <= 5; i++) { expect(screen.getByText(i.toString())).toBeInTheDocument() } }) }) describe('信心等級選擇', () => { it('應該在點擊時調用 onSelect', async () => { const user = userEvent.setup() render( ) const level3Button = screen.getByText('還算熟悉').closest('button') await user.click(level3Button!) expect(mockOnSelect).toHaveBeenCalledWith(3) }) it('應該正確顯示選中狀態', () => { render( ) const selectedButton = screen.getByText('很熟悉').closest('button') expect(selectedButton).toHaveClass('ring-4', 'ring-opacity-50') }) }) }) describe('RecordingControl', () => { const mockOnStartRecording = vi.fn() const mockOnStopRecording = vi.fn() const mockOnPlayback = vi.fn() const mockOnSubmit = vi.fn() beforeEach(() => { vi.clearAllMocks() }) describe('錄音狀態管理', () => { it('應該在非錄音狀態顯示開始按鈕', () => { render( ) expect(screen.getByText('🎤')).toBeInTheDocument() expect(screen.getByText('點擊開始錄音')).toBeInTheDocument() }) it('應該在錄音狀態顯示停止按鈕', () => { render( ) expect(screen.getByText('⏹️')).toBeInTheDocument() expect(screen.getByText('錄音中... 點擊停止')).toBeInTheDocument() }) it('應該在有錄音時顯示播放和提交按鈕', () => { render( ) expect(screen.getByText('錄音完成')).toBeInTheDocument() expect(screen.getByTestId('blue-play-button')).toBeInTheDocument() expect(screen.getByText('提交錄音')).toBeInTheDocument() }) }) describe('錄音操作', () => { it('應該在點擊時開始錄音', async () => { const user = userEvent.setup() render( ) const recordButton = screen.getByRole('button') await user.click(recordButton) expect(mockOnStartRecording).toHaveBeenCalledTimes(1) }) it('應該在錄音時點擊停止錄音', async () => { const user = userEvent.setup() render( ) const recordButton = screen.getByRole('button') await user.click(recordButton) expect(mockOnStopRecording).toHaveBeenCalledTimes(1) }) it('應該在有錄音時能播放', async () => { const user = userEvent.setup() render( ) const playButton = screen.getByTestId('blue-play-button') await user.click(playButton) expect(mockOnPlayback).toHaveBeenCalledTimes(1) }) it('應該在有錄音時能提交', async () => { const user = userEvent.setup() render( ) const submitButton = screen.getByText('提交錄音') await user.click(submitButton) expect(mockOnSubmit).toHaveBeenCalledTimes(1) }) }) describe('禁用狀態', () => { it('應該在 disabled 時禁用所有按鈕', () => { render( ) // 錄音按鈕應該禁用 const recordButton = screen.getByText('🎤') expect(recordButton).toBeDisabled() // 播放和提交按鈕應該禁用 const playButton = screen.getByTestId('blue-play-button') const submitButton = screen.getByText('提交錄音') expect(playButton).toBeDisabled() expect(submitButton).toBeDisabled() }) }) })