dramaling-vocab-learning/frontend/store/review/__tests__/useTestResultStore.test.ts

225 lines
6.3 KiB
TypeScript

import { describe, it, expect, beforeEach, vi } from 'vitest'
import { useTestResultStore } from '../useTestResultStore'
// Mock flashcardsService
const mockFlashcardsService = {
recordTestCompletion: vi.fn()
}
vi.mock('@/lib/services/flashcards', () => ({
flashcardsService: mockFlashcardsService
}))
// Mock isTestMode
vi.mock('@/lib/mock/reviewMockData', () => ({
isTestMode: vi.fn()
}))
describe('useTestResultStore', () => {
beforeEach(() => {
// 重置 store
useTestResultStore.getState().resetScore()
vi.clearAllMocks()
})
describe('初始狀態', () => {
it('應該有正確的初始值', () => {
const state = useTestResultStore.getState()
expect(state.score).toEqual({ correct: 0, total: 0 })
expect(state.isRecordingResult).toBe(false)
expect(state.recordingError).toBe(null)
})
})
describe('updateScore', () => {
it('應該正確更新正確答案分數', () => {
const store = useTestResultStore.getState()
store.updateScore(true)
expect(store.score).toEqual({ correct: 1, total: 1 })
store.updateScore(true)
expect(store.score).toEqual({ correct: 2, total: 2 })
})
it('應該正確更新錯誤答案分數', () => {
const store = useTestResultStore.getState()
store.updateScore(false)
expect(store.score).toEqual({ correct: 0, total: 1 })
store.updateScore(true)
expect(store.score).toEqual({ correct: 1, total: 2 })
})
})
describe('recordTestResult 測試模式', () => {
beforeEach(() => {
vi.mocked(require('@/lib/mock/reviewMockData').isTestMode).mockReturnValue(true)
})
it('應該在測試模式下跳過 API 呼叫', async () => {
const store = useTestResultStore.getState()
const result = await store.recordTestResult({
flashcardId: 'mock-1',
testType: 'flip-memory',
isCorrect: true,
userAnswer: 'test',
confidenceLevel: 4
})
expect(result).toBe(true)
expect(mockFlashcardsService.recordTestCompletion).not.toHaveBeenCalled()
})
it('應該正確設置錄製狀態', async () => {
const store = useTestResultStore.getState()
// 開始錄製時檢查狀態
const recordPromise = store.recordTestResult({
flashcardId: 'mock-1',
testType: 'flip-memory',
isCorrect: true
})
expect(store.isRecordingResult).toBe(true)
// 等待完成
await recordPromise
expect(store.isRecordingResult).toBe(false)
})
})
describe('recordTestResult 正常模式', () => {
beforeEach(() => {
vi.mocked(require('@/lib/mock/reviewMockData').isTestMode).mockReturnValue(false)
})
it('應該成功記錄測驗結果', async () => {
mockFlashcardsService.recordTestCompletion.mockResolvedValue({
success: true
})
const store = useTestResultStore.getState()
const testParams = {
flashcardId: 'mock-1',
testType: 'flip-memory' as any,
isCorrect: true,
userAnswer: 'hello',
confidenceLevel: 4,
responseTimeMs: 2000
}
const result = await store.recordTestResult(testParams)
expect(result).toBe(true)
expect(mockFlashcardsService.recordTestCompletion).toHaveBeenCalledWith(testParams)
})
it('應該處理 API 失敗', async () => {
mockFlashcardsService.recordTestCompletion.mockResolvedValue({
success: false,
error: 'API錯誤'
})
const store = useTestResultStore.getState()
const result = await store.recordTestResult({
flashcardId: 'mock-1',
testType: 'flip-memory',
isCorrect: true
})
expect(result).toBe(false)
expect(store.recordingError).toBe('記錄測驗結果失敗')
})
it('應該處理網路異常', async () => {
mockFlashcardsService.recordTestCompletion.mockRejectedValue(
new Error('Network error')
)
const store = useTestResultStore.getState()
const result = await store.recordTestResult({
flashcardId: 'mock-1',
testType: 'flip-memory',
isCorrect: true
})
expect(result).toBe(false)
expect(store.recordingError).toBe('記錄測驗結果異常')
})
it('應該設置預設 responseTimeMs', async () => {
mockFlashcardsService.recordTestCompletion.mockResolvedValue({
success: true
})
const store = useTestResultStore.getState()
await store.recordTestResult({
flashcardId: 'mock-1',
testType: 'flip-memory',
isCorrect: true
})
expect(mockFlashcardsService.recordTestCompletion).toHaveBeenCalledWith(
expect.objectContaining({
responseTimeMs: 2000
})
)
})
})
describe('統計方法', () => {
beforeEach(() => {
const store = useTestResultStore.getState()
// 設置一些分數數據
store.updateScore(true) // 1/1
store.updateScore(true) // 2/2
store.updateScore(false) // 2/3
store.updateScore(true) // 3/4
})
it('getAccuracyPercentage 應該計算正確的準確率', () => {
const store = useTestResultStore.getState()
const accuracy = store.getAccuracyPercentage()
expect(accuracy).toBe(75) // 3/4 = 75%
})
it('getTotalAttempts 應該返回總嘗試次數', () => {
const store = useTestResultStore.getState()
const total = store.getTotalAttempts()
expect(total).toBe(4)
})
it('應該在無嘗試時返回 0%', () => {
const store = useTestResultStore.getState()
store.resetScore()
expect(store.getAccuracyPercentage()).toBe(0)
expect(store.getTotalAttempts()).toBe(0)
})
})
describe('resetScore', () => {
it('應該重置分數和錯誤狀態', () => {
const store = useTestResultStore.getState()
// 設置一些狀態
store.updateScore(true)
store.setRecordingError('某個錯誤')
// 重置
store.resetScore()
expect(store.score).toEqual({ correct: 0, total: 0 })
expect(store.recordingError).toBe(null)
})
})
})