Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions docs/dev-notes/2025-11-14/add_tests_for_contest_table_provider/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# ABC126ToABC211Provider テスト追加計画

**作成日**: 2025-11-14

**対象ブランチ**: #2830

**優先度**: High

---

## 参照ドキュメント

テストの書き方・スタイルについては、以下を参照:

📖 [`docs/dev-notes/2025-11-03/add_tests_for_contest_table_provider/plan.md`](../../2025-11-03/add_tests_for_contest_table_provider/plan.md)

---

## 実装チェックリスト

### 1. テスト設計 ✅

- [x] フィルタリングテスト(ABC126~211範囲内のみ抽出)
- [x] コンテストタイプ判別テスト(ABC型のみ)
- [x] メタデータ取得テスト
- [x] ディスプレイ設定テスト
- [x] ラウンドラベルフォーマットテスト
- [x] エッジケーステスト(空入力など)
- [x] 混合コンテストタイプ対応テスト

### 2. モックデータ準備

- [x] `src/test/lib/utils/test_cases/contest_table_provider.ts` に ABC126~211 データを追加
- [x] ABC126, ABC150, ABC211 の 3 コンテストでサンプルデータを作成
- [x] task_table_index は A, B, C, D, E, F に対応

### 3. テスト実装

- [x] 既存テスト(`ABC212ToABC318Provider` など)を参考に記述
- [x] `ABC126ToABC211Provider` をテストファイルにインポート
- [x] `describe.each()` に ABC126ToABC211Provider を追加(displayConfig 共通化)

### 4. テスト リファクタリング

- [x] `describe.each()` に ABC126ToABC211 を追加:displayConfig, label format, empty results テストを共通化
- [x] ABC126ToABC211 個別テストから重複テストを削除:5 個削除 → 4 個に削減

### 5. 実装後の検証

- [x] テスト実行: `pnpm test:unit src/test/lib/utils/contest_table_provider.test.ts` → **115 テスト全てパス**
- [x] Lint チェック: `pnpm format` → **フォーマット完了**
- [x] 全テスト合格確認 → **✅ PASS**

---

## テスト仕様

### 対象プロバイダー

`ABC126ToABC211Provider`

### フィルタ範囲

- **最小**: ABC 126
- **最大**: ABC 211
- **対象数**: 86 コンテスト

### 表示設定

| 項目 | 値 |
| --------------------- | ------------------------------------------------------- |
| `isShownHeader` | `true` |
| `isShownRoundLabel` | `true` |
| `roundLabelWidth` | `'xl:w-16'` |
| `tableBodyCellsWidth` | `'w-1/2 xs:w-1/3 sm:w-1/4 md:w-1/5 lg:w-1/6 px-1 py-1'` |
| `isShownTaskIndex` | `false` |

---

## 実装結果・教訓

### ✅ 実装完了

**実施時間**: 約 5 分(リファクタリング含む)

**実装内容**:

1. モックデータ追加: ABC126, ABC150, ABC211 の 3 コンテスト分(9 タスク)
2. `describe.each()` に ABC126ToABC211Provider を追加
3. ABC126ToABC211 個別テストから重複テストを削除(5→4)
4. テストコード削減: 39 行削除、DRY 原則に従う

### 📚 得られた教訓

1. **パラメータ化テストの活用**: `describe.each()` で同一構造のプロバイダーをシェアすることで、メンテナンス性向上とコード削減が実現。新規プロバイダー追加時は同じ表示設定を持つ場合、`describe.each()` への統合を検討すべき

2. **テスト重複の排除**: 共通テスト(displayConfig, label format, empty results)と固有テスト(フィルタリング範囲)の分離により、テストの意図が明確化され、保守が容易に

3. **プロバイダー設計の統一性**: ABC126~211 と ABC212~318 が同じ displayConfig を持つことは、プロバイダー実装の設計が一貫していることを示す。新規プロバイダー追加時は既存設計との整合性を確認することが重要

---

## 改善提案(実装完了)✅

### `describe.each()` パターンへの統合

**実施内容**:

`ABC126ToABC211Provider` を `describe.each()` に追加し、displayConfig などの共通テストをシェア。

**変更内容**:

```typescript
// 追加されたパラメータ
{
providerClass: ABC126ToABC211Provider,
label: '126 to 211',
displayConfig: {
roundLabelWidth: 'xl:w-16',
tableBodyCellsWidth: 'w-1/2 xs:w-1/3 sm:w-1/4 md:w-1/5 lg:w-1/6 px-1 py-1',
},
},
```

**ABC126ToABC211 個別テストから削除**:

- `test('expects to get correct display configuration', ...)`
- `test('expects to format contest round label correctly', ...)`
- `test('expects to handle empty task results', ...)`
- `test('expects to generate correct table structure', ...)`
- `test('expects to get contest round IDs correctly', ...)`

**結果**: テストコード 39 行削減、テスト数 115(変わらず)、DRY 原則に従う構造へ改善
245 changes: 245 additions & 0 deletions prisma/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3854,6 +3854,41 @@ export const tasks = [
title: 'A. New Generation ABC',
grade: 'Q8',
},
{
id: 'abc211_f',
contest_id: 'abc211',
problem_index: 'F',
name: 'Rectilinear Polygons',
title: 'F. Rectilinear Polygons',
},
{
id: 'abc211_e',
contest_id: 'abc211',
problem_index: 'E',
name: 'Red Polyomino',
title: 'E. Red Polyomino',
},
{
id: 'abc211_d',
contest_id: 'abc211',
problem_index: 'D',
name: 'Number of Shortest paths',
title: 'D. Number of Shortest paths',
},
{
id: 'abc211_c',
contest_id: 'abc211',
problem_index: 'C',
name: 'chokudai',
title: 'C. chokudai',
},
{
id: 'abc211_b',
contest_id: 'abc211',
problem_index: 'B',
name: 'Cycle Hit',
title: 'B. Cycle Hit',
},
{
id: 'abc211_a',
contest_id: 'abc211',
Expand All @@ -3862,6 +3897,90 @@ export const tasks = [
title: 'A. Blood Pressure',
grade: 'Q9',
},
{
id: 'abc210_f',
contest_id: 'abc210',
problem_index: 'F',
name: 'Coprime Solitaire',
title: 'F. Coprime Solitaire',
},
{
id: 'abc210_e',
contest_id: 'abc210',
problem_index: 'E',
name: 'Ring MST',
title: 'E. Ring MST',
},
{
id: 'abc210_d',
contest_id: 'abc210',
problem_index: 'D',
name: 'National Railway',
title: 'D. National Railway',
},
{
id: 'abc210_c',
contest_id: 'abc210',
problem_index: 'C',
name: 'Colorful Candies',
title: 'C. Colorful Candies',
},
{
id: 'abc210_b',
contest_id: 'abc210',
problem_index: 'B',
name: 'Bouzu Mekuri',
title: 'B. Bouzu Mekuri',
},
{
id: 'abc210_a',
contest_id: 'abc210',
problem_index: 'A',
name: 'Cabbages',
title: 'A. Cabbages',
},
{
id: 'abc209_f',
contest_id: 'abc209',
problem_index: 'F',
name: 'Deforestation',
title: 'F. Deforestation',
},
{
id: 'abc209_e',
contest_id: 'abc209',
problem_index: 'E',
name: 'Shiritori',
title: 'E. Shiritori',
},
{
id: 'abc209_d',
contest_id: 'abc209',
problem_index: 'D',
name: 'Collision',
title: 'D. Collision',
},
{
id: 'abc209_c',
contest_id: 'abc209',
problem_index: 'C',
name: 'Not Equal',
title: 'C. Not Equal',
},
{
id: 'abc209_b',
contest_id: 'abc209',
problem_index: 'B',
name: 'Can you buy them all?',
title: 'B. Can you buy them all?',
},
{
id: 'abc209_a',
contest_id: 'abc209',
problem_index: 'A',
name: 'Counting',
title: 'A. Counting',
},
{
id: 'abc205_a',
contest_id: 'abc205',
Expand Down Expand Up @@ -4060,6 +4179,132 @@ export const tasks = [
name: 'ModSum',
title: 'D. ModSum',
},
{
id: 'abc128_f',
contest_id: 'abc128',
problem_index: 'F',
name: 'Frog Jump',
title: 'F. Frog Jump',
},
{
id: 'abc128_e',
contest_id: 'abc128',
problem_index: 'E',
name: 'Roadwork',
title: 'E. Roadwork',
},
{
id: 'abc128_d',
contest_id: 'abc128',
problem_index: 'D',
name: 'equeue',
title: 'D. equeue',
},
{
id: 'abc128_c',
contest_id: 'abc128',
problem_index: 'C',
name: 'Switches',
title: 'C. Switches',
},
{
id: 'abc128_b',
contest_id: 'abc128',
problem_index: 'B',
name: 'Guidebook',
title: 'B. Guidebook',
},
{
id: 'abc128_a',
contest_id: 'abc128',
problem_index: 'A',
name: 'Apple Pie',
title: 'A. Apple Pie',
},
{
id: 'abc127_f',
contest_id: 'abc127',
problem_index: 'F',
name: 'Absolute Minima',
title: 'F. Absolute Minima',
},
{
id: 'abc127_e',
contest_id: 'abc127',
problem_index: 'E',
name: 'Cell Distance',
title: 'E. Cell Distance',
},
{
id: 'abc127_d',
contest_id: 'abc127',
problem_index: 'D',
name: 'Integer Cards',
title: 'D. Integer Cards',
},
{
id: 'abc127_c',
contest_id: 'abc127',
problem_index: 'C',
name: 'Prison',
title: 'C. Prison',
},
{
id: 'abc127_b',
contest_id: 'abc127',
problem_index: 'B',
name: 'Algae',
title: 'B. Algae',
},
{
id: 'abc127_a',
contest_id: 'abc127',
problem_index: 'A',
name: 'Ferris Wheel',
title: 'A. Ferris Wheel',
},
{
id: 'abc126_f',
contest_id: 'abc126',
problem_index: 'F',
name: 'XOR Matching',
title: 'F. XOR Matching',
},
{
id: 'abc126_e',
contest_id: 'abc126',
problem_index: 'E',
name: '1 or 2',
title: 'E. 1 or 2',
},
{
id: 'abc126_d',
contest_id: 'abc126',
problem_index: 'D',
name: 'Even Relation',
title: 'D. Even Relation',
},
{
id: 'abc126_c',
contest_id: 'abc126',
problem_index: 'C',
name: 'Dice and Coin',
title: 'C. Dice and Coin',
},
{
id: 'abc126_b',
contest_id: 'abc126',
problem_index: 'B',
name: 'YYMM or MMYY',
title: 'B. YYMM or MMYY',
},
{
id: 'abc126_a',
contest_id: 'abc126',
problem_index: 'A',
name: 'Changing a Character',
title: 'A. Changing a Character',
},
{
id: 'abc123_d',
contest_id: 'abc123',
Expand Down
Loading
Loading