243 lines
7.4 KiB
TypeScript
243 lines
7.4 KiB
TypeScript
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())
|
|
})
|
|
})
|
|
}) |