# 延遲計數系統測試規格

**目的**: 將您的延遲計數需求轉換為可執行的測試案例
**測試範圍**: 跳過功能、排序邏輯、答錯處理、優先級管理
**測試方式**: Jest + React Testing Library
**最後更新**: 2025-10-03

---

## 🧪 **核心需求測試**

### **測試1: 跳過功能基礎邏輯**
```typescript
describe('延遲計數系統 - 跳過功能', () => {
  test('當使用者在答題時點擊跳過，該題目會跳過', () => {
    // 準備測試數據
    const initialCards = [
      {
        id: '1',
        word: 'evidence',
        skipCount: 0,
        wrongCount: 0,
        isCompleted: false
      }
    ]
    const currentIndex = 0

    // 執行跳過操作
    const result = handleSkip(initialCards, currentIndex)

    // 驗證結果
    expect(result.updatedCards[0].skipCount).toBe(1)
    expect(result.updatedCards[0].wrongCount).toBe(0) // 不影響答錯次數
    expect(result.nextIndex).toBe(1) // 移動到下一題
    expect(result.updatedCards[0].isCompleted).toBe(false) // 未完成狀態
  })

  test('同一題目可以多次跳過，次數累加', () => {
    const cards = [
      { id: '1', word: 'test', skipCount: 2, wrongCount: 1, isCompleted: false }
    ]

    const result = handleSkip(cards, 0)

    expect(result.updatedCards[0].skipCount).toBe(3) // 從2增加到3
    expect(result.updatedCards[0].wrongCount).toBe(1) // 保持不變
  })
})
```

### **測試2: 排序優先級邏輯**
```typescript
describe('延遲計數系統 - 排序邏輯', () => {
  test('題目排序應該是被跳過次數越少越前面', () => {
    // 準備不同延遲分數的卡片
    const cards = [
      { id: '1', word: 'card1', skipCount: 3, wrongCount: 1, originalOrder: 1, isCompleted: false }, // 延遲分數: 4
      { id: '2', word: 'card2', skipCount: 1, wrongCount: 0, originalOrder: 2, isCompleted: false }, // 延遲分數: 1
      { id: '3', word: 'card3', skipCount: 0, wrongCount: 0, originalOrder: 3, isCompleted: false }, // 延遲分數: 0
      { id: '4', word: 'card4', skipCount: 2, wrongCount: 0, originalOrder: 4, isCompleted: false }  // 延遲分數: 2
    ]

    // 執行排序
    const sorted = sortCardsByPriority(cards)

    // 驗證排序結果 (延遲分數由小到大)
    expect(sorted[0].id).toBe('3') // 延遲分數 0
    expect(sorted[1].id).toBe('2') // 延遲分數 1
    expect(sorted[2].id).toBe('4') // 延遲分數 2
    expect(sorted[3].id).toBe('1') // 延遲分數 4
  })

  test('延遲分數相同時按原始順序排列', () => {
    const cards = [
      { id: '1', skipCount: 1, wrongCount: 0, originalOrder: 3, isCompleted: false }, // 延遲分數: 1
      { id: '2', skipCount: 0, wrongCount: 1, originalOrder: 1, isCompleted: false }, // 延遲分數: 1
      { id: '3', skipCount: 1, wrongCount: 0, originalOrder: 2, isCompleted: false }  // 延遲分數: 1
    ]

    const sorted = sortCardsByPriority(cards)

    // 相同延遲分數按originalOrder排序
    expect(sorted[0].originalOrder).toBe(1)
    expect(sorted[1].originalOrder).toBe(2)
    expect(sorted[2].originalOrder).toBe(3)
  })
})
```

### **測試3: 答錯效果等同跳過**
```typescript
describe('延遲計數系統 - 答錯處理', () => {
  test('答錯的效果和跳過一樣都會被註記一次', () => {
    const initialCard = {
      id: '1',
      word: 'test',
      skipCount: 0,
      wrongCount: 0,
      isCompleted: false
    }

    // 測試跳過效果
    const skippedResult = handleSkip([initialCard], 0)
    const skippedScore = skippedResult.updatedCards[0].skipCount + skippedResult.updatedCards[0].wrongCount

    // 測試答錯效果
    const wrongResult = handleWrongAnswer([initialCard], 0)
    const wrongScore = wrongResult.updatedCards[0].skipCount + wrongResult.updatedCards[0].wrongCount

    // 兩者都應該增加1分延遲分數
    expect(skippedScore).toBe(1)
    expect(wrongScore).toBe(1)
  })

  test('信心度1-2算答錯，3算答對', () => {
    const card = { id: '1', word: 'test', skipCount: 0, wrongCount: 0, isCompleted: false }

    // 信心度1 (模糊) → 答錯
    const result1 = handleConfidenceAnswer([card], 0, 1)
    expect(result1.updatedCards[0].wrongCount).toBe(1)
    expect(result1.updatedCards[0].isCompleted).toBe(false)

    // 信心度2 (一般) → 答對
    const result2 = handleConfidenceAnswer([card], 0, 2)
    expect(result2.updatedCards[0].isCompleted).toBe(true)
    expect(result2.updatedCards[0].wrongCount).toBe(0) // 不增加

    // 信心度3 (熟悉) → 答對
    const result3 = handleConfidenceAnswer([card], 0, 3)
    expect(result3.updatedCards[0].isCompleted).toBe(true)
  })
})
```

### **測試4: 排序不排除邏輯**
```typescript
describe('延遲計數系統 - 不排除原則', () => {
  test('被跳過只是排序問題，不是直接排除', () => {
    const cards = [
      { id: '1', word: 'high-delay', skipCount: 10, wrongCount: 5, isCompleted: false }, // 高延遲
      { id: '2', word: 'normal', skipCount: 0, wrongCount: 0, isCompleted: false },       // 正常
      { id: '3', word: 'completed', skipCount: 2, wrongCount: 1, isCompleted: true }      // 已完成
    ]

    const sorted = sortCardsByPriority(cards)

    // 驗證沒有卡片被排除
    expect(sorted).toHaveLength(3)
    expect(sorted.find(c => c.id === '1')).toBeDefined() // 高延遲卡片仍存在

    // 驗證排序正確
    expect(sorted[0].id).toBe('2') // 正常卡片優先
    expect(sorted[1].id).toBe('1') // 延遲卡片其次
    expect(sorted[2].id).toBe('3') // 已完成卡片最後
  })

  test('即使跳過很多次的卡片仍會被練習', () => {
    const cards = [
      { id: '1', word: 'difficult', skipCount: 20, wrongCount: 15, isCompleted: false }
    ]

    const sorted = sortCardsByPriority(cards)

    expect(sorted).toHaveLength(1)
    expect(sorted[0].id).toBe('1') // 即使延遲分數很高，仍然存在
  })
})
```

### **測試5: 完整流程集成**
```typescript
describe('延遲計數系統 - 完整流程', () => {
  test('完整的學習會話流程', () => {
    // 初始狀態: 3張卡片
    let cards = [
      { id: '1', word: 'easy', skipCount: 0, wrongCount: 0, originalOrder: 1, isCompleted: false },
      { id: '2', word: 'medium', skipCount: 0, wrongCount: 0, originalOrder: 2, isCompleted: false },
      { id: '3', word: 'hard', skipCount: 0, wrongCount: 0, originalOrder: 3, isCompleted: false }
    ]

    // 第1輪: easy答對, medium跳過, hard答錯
    cards = handleConfidenceAnswer(cards, 0, 3).updatedCards // easy完成
    cards = handleSkip(cards, 1).updatedCards                // medium跳過+1
    cards = handleWrongAnswer(cards, 2).updatedCards         // hard答錯+1

    // 排序後應該是: medium, hard (延遲分數都是1，按原順序)
    const sorted1 = sortCardsByPriority(cards.filter(c => !c.isCompleted))
    expect(sorted1[0].word).toBe('medium')
    expect(sorted1[1].word).toBe('hard')

    // 第2輪: medium再次跳過, hard答對完成
    cards = handleSkip(cards, 1).updatedCards                // medium跳過+1 (總共2次)
    cards = handleConfidenceAnswer(cards, 2, 2).updatedCards // hard完成

    // 最終只剩medium未完成，延遲分數為2
    const remaining = cards.filter(c => !c.isCompleted)
    expect(remaining).toHaveLength(1)
    expect(remaining[0].word).toBe('medium')
    expect(remaining[0].skipCount).toBe(2)
  })
})
```

---

## 🎯 **測試實作函數**

### **需要實作的核心函數**
```typescript
// 這些是測試中使用的函數，需要在實際代碼中實作

interface TestResult {
  updatedCards: CardState[]
  nextIndex: number
}

// 跳過處理函數
function handleSkip(cards: CardState[], currentIndex: number): TestResult

// 答錯處理函數
function handleWrongAnswer(cards: CardState[], currentIndex: number): TestResult

// 信心度答題函數
function handleConfidenceAnswer(cards: CardState[], currentIndex: number, confidence: 1|2|3): TestResult

// 排序函數
function sortCardsByPriority(cards: CardState[]): CardState[]
```

---

## 📊 **測試覆蓋範圍**

### **功能覆蓋**
- ✅ 跳過次數計數
- ✅ 答錯次數計數
- ✅ 優先級排序邏輯
- ✅ 完成狀態管理
- ✅ 多次操作累計效果

### **邊界條件測試**
- ✅ 極高延遲分數的卡片
- ✅ 相同延遲分數的排序
- ✅ 全部完成的情況
- ✅ 單卡片情況

### **集成測試**
- ✅ 完整學習會話流程
- ✅ 多輪練習的狀態變化
- ✅ 不同信心度的處理

---

## 🚀 **TDD開發流程**

### **第1步: Red (測試失敗)**
```bash
npm test delay-counting-system.test.ts
# 所有測試應該失敗，因為函數還沒實作
```

### **第2步: Green (最小實作)**
```typescript
// 實作最簡單的版本讓測試通過
const handleSkip = (cards, index) => {
  const newCards = [...cards]
  newCards[index] = { ...newCards[index], skipCount: newCards[index].skipCount + 1 }
  return { updatedCards: newCards, nextIndex: index + 1 }
}
```

### **第3步: Refactor (重構優化)**
```typescript
// 重構為更優雅的版本，保持測試通過
const handleSkip = useCallback((cards: CardState[], currentIndex: number) => {
  const updatedCards = cards.map((card, index) =>
    index === currentIndex
      ? { ...card, skipCount: card.skipCount + 1 }
      : card
  )

  return {
    updatedCards,
    nextIndex: getNextIndex(updatedCards, currentIndex)
  }
}, [])
```

---

## 🎯 **測試的業務價值**

### **確保需求實現**
- ✅ 您的4個核心需求都有對應測試
- ✅ 邊界情況都有覆蓋
- ✅ 集成場景完整測試

### **回歸保護**
- ✅ 未來修改不會破壞核心邏輯
- ✅ 重構時有安全保障
- ✅ 新功能不會影響現有行為

### **需求文檔化**
- ✅ 測試即是可執行的需求規格
- ✅ 開發者可以直接理解期望行為
- ✅ 產品經理可以驗證實作正確性

---

## 📋 **測試執行清單**

### **開發前**
- [ ] 所有測試都失敗 (因為還沒實作)
- [ ] 測試準確描述了您的需求

### **開發中**
- [ ] 逐個實作函數讓測試通過
- [ ] 保持測試綠燈狀態
- [ ] 重構時確保測試不破壞

### **完成後**
- [ ] 所有測試通過
- [ ] 覆蓋率達到90%+
- [ ] 邊界條件都正確處理

---

**這些測試完美地捕捉了您的延遲計數需求，可以指導TDD開發，確保實作完全符合您的預期！** 🎯

*測試規格制定: 2025-10-03*
*基於用戶明確需求*
*TDD開發就緒*