import { describe, it, expect, beforeEach, vi } from 'vitest' import { useTestQueueStore, TestItem, ReviewMode } from '../useTestQueueStore' import { mockDueCards } from '@/lib/mock/reviewMockData' // Mock dependencies vi.mock('@/lib/utils/cefrUtils', () => ({ getReviewTypesByCEFR: vi.fn(() => ['flip-memory', 'vocab-choice']) })) vi.mock('@/lib/mock/reviewMockData', () => ({ isTestMode: vi.fn(), getTestModeReviewTypes: vi.fn(() => ['flip-memory', 'vocab-choice']), mockDueCards })) describe('useTestQueueStore', () => { beforeEach(() => { // 重置 store useTestQueueStore.getState().resetQueue() vi.clearAllMocks() // Mock localStorage vi.mocked(localStorage.getItem).mockReturnValue('A2') }) describe('初始狀態', () => { it('應該有正確的初始值', () => { const state = useTestQueueStore.getState() expect(state.testItems).toEqual([]) expect(state.currentTestIndex).toBe(0) expect(state.completedTests).toBe(0) expect(state.totalTests).toBe(0) expect(state.currentMode).toBe('flip-memory') expect(state.skippedTests).toEqual(new Set()) }) }) describe('initializeTestQueue', () => { it('應該正確生成測驗項目', () => { const store = useTestQueueStore.getState() const testCards = [mockDueCards[0]] // 只用一張卡片測試 store.initializeTestQueue(testCards, []) expect(store.testItems).toHaveLength(2) // 1卡 * 2測驗類型 expect(store.totalTests).toBe(2) expect(store.currentTestIndex).toBe(0) // 檢查第一個測驗項目 const firstTest = store.testItems[0] expect(firstTest.cardId).toBe('mock-1') expect(firstTest.word).toBe('hello') expect(firstTest.isCurrent).toBe(true) expect(firstTest.isCompleted).toBe(false) }) it('應該在測試模式下使用簡化邏輯', () => { vi.mocked(require('@/lib/mock/reviewMockData').isTestMode).mockReturnValue(true) const store = useTestQueueStore.getState() store.initializeTestQueue([mockDueCards[0]], []) // 驗證使用了測試模式的測驗類型 expect(store.testItems.map(item => item.testType)) .toEqual(['flip-memory', 'vocab-choice']) }) it('應該過濾已完成的測驗', () => { const store = useTestQueueStore.getState() const completedTests = [ { flashcardId: 'mock-1', testType: 'flip-memory' } ] store.initializeTestQueue([mockDueCards[0]], completedTests) expect(store.testItems).toHaveLength(1) // 只剩 vocab-choice expect(store.testItems[0].testType).toBe('vocab-choice') }) }) describe('goToNextTest', () => { beforeEach(() => { const store = useTestQueueStore.getState() store.initializeTestQueue([mockDueCards[0]], []) }) it('應該正確跳轉到下一個測驗', () => { const store = useTestQueueStore.getState() store.goToNextTest() expect(store.currentTestIndex).toBe(1) expect(store.currentMode).toBe('vocab-choice') expect(store.testItems[0].isCurrent).toBe(false) expect(store.testItems[1].isCurrent).toBe(true) }) it('應該在最後一個測驗時保持不變', () => { const store = useTestQueueStore.getState() // 跳到最後一個測驗 store.setCurrentTestIndex(1) store.goToNextTest() // 應該保持在最後一個 expect(store.currentTestIndex).toBe(1) }) }) describe('markTestCompleted', () => { beforeEach(() => { const store = useTestQueueStore.getState() store.initializeTestQueue([mockDueCards[0]], []) }) it('應該正確標記測驗為完成', () => { const store = useTestQueueStore.getState() store.markTestCompleted(0) expect(store.testItems[0].isCompleted).toBe(true) expect(store.testItems[0].isCurrent).toBe(false) expect(store.completedTests).toBe(1) }) it('應該從跳過列表移除完成的測驗', () => { const store = useTestQueueStore.getState() // 先跳過一個測驗 store.skipCurrentTest() const skippedId = store.testItems[0].id // 然後完成這個測驗 store.markTestCompleted(0) expect(store.skippedTests.has(skippedId)).toBe(false) }) }) describe('skipCurrentTest', () => { beforeEach(() => { const store = useTestQueueStore.getState() store.initializeTestQueue([mockDueCards[0], mockDueCards[1]], []) }) it('應該正確跳過當前測驗', () => { const store = useTestQueueStore.getState() const currentTestId = store.testItems[0].id store.skipCurrentTest() expect(store.testItems[0].isSkipped).toBe(true) expect(store.testItems[0].skippedAt).toBeDefined() expect(store.skippedTests.has(currentTestId)).toBe(true) }) it('應該正確切換到下一個可用測驗', () => { const store = useTestQueueStore.getState() store.skipCurrentTest() // 應該跳過被跳過的測驗,找到下一個 expect(store.testItems[store.currentTestIndex].isCompleted).toBe(false) expect(store.testItems[store.currentTestIndex].isCurrent).toBe(true) }) }) describe('工具方法', () => { beforeEach(() => { const store = useTestQueueStore.getState() store.initializeTestQueue(mockDueCards, []) }) it('getTestStats 應該返回正確的統計', () => { const store = useTestQueueStore.getState() // 完成一個測驗 store.markTestCompleted(0) // 跳過一個測驗 store.skipCurrentTest() const stats = store.getTestStats() expect(stats.total).toBe(6) // 3卡 * 2測驗 expect(stats.completed).toBe(1) expect(stats.skipped).toBe(1) expect(stats.remaining).toBe(4) }) it('isAllTestsCompleted 應該正確檢測完成狀態', () => { const store = useTestQueueStore.getState() expect(store.isAllTestsCompleted()).toBe(false) // 完成所有測驗 store.testItems.forEach((_, index) => { store.markTestCompleted(index) }) expect(store.isAllTestsCompleted()).toBe(true) }) }) describe('優先級演算法', () => { it('未嘗試的測驗應該有最高優先級', () => { const store = useTestQueueStore.getState() store.initializeTestQueue([mockDueCards[0]], []) const firstTest = store.testItems[0] expect(firstTest.priority).toBe(100) }) it('跳過的測驗應該有較低優先級', () => { const store = useTestQueueStore.getState() store.initializeTestQueue([mockDueCards[0]], []) store.skipCurrentTest() const skippedTest = store.testItems.find(item => item.isSkipped) expect(skippedTest?.priority).toBe(10) }) }) describe('resetQueue', () => { it('應該重置所有隊列狀態', () => { const store = useTestQueueStore.getState() // 設置一些狀態 store.initializeTestQueue(mockDueCards, []) store.markTestCompleted(0) store.skipCurrentTest() // 重置 store.resetQueue() expect(store.testItems).toEqual([]) expect(store.currentTestIndex).toBe(0) expect(store.completedTests).toBe(0) expect(store.totalTests).toBe(0) expect(store.skippedTests).toEqual(new Set()) }) }) })