# 機能設計書 48-jest-each

## 概要

本ドキュメントは、jest-eachパッケージの機能設計について記載する。jest-eachは、パラメータ化テスト（データ駆動テスト）の実装を提供し、test.eachやdescribe.eachをサポートするパッケージである。

### 本機能の処理概要

jest-eachは、同じテストロジックを異なるデータセットで繰り返し実行するためのAPIを提供する。配列形式またはタグ付きテンプレートリテラル形式でテストデータを定義し、各データセットに対してテストケースを自動生成する。

**業務上の目的・背景**：多くのテストシナリオでは、同じロジックを異なる入力値で検証する必要がある。従来は各ケースを個別に記述する必要があったが、jest-eachを使用することで、データとテストロジックを分離し、コードの重複を削減できる。これにより、テストの保守性が向上し、新しいテストケースの追加も容易になる。

**機能の利用シーン**：境界値テスト、複数の入力パターンのテスト、正常系/異常系の組み合わせテストなど、同一ロジックを異なるデータで検証する場面で使用される。

**主要な処理内容**：
1. each関数: テストデータを受け取り、test/it/describe関数を返却
2. bind関数: テスト関数とデータをバインド
3. array変換: 配列形式のデータをテストケースに変換
4. template変換: テンプレートリテラル形式のデータをテストケースに変換
5. interpolation: タイトル内のプレースホルダーを値で置換

**関連システム・外部連携**：@jest/types（Global型定義）、jest-util（convertDescriptorToString、ErrorWithStack）、pretty-format（値の整形）を使用。

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 18 | API Referenceページ | 補助機能 | test.each、describe.eachのAPI説明 |

## 機能種別

テスト生成 / データバインディング

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| table | EachTable | Yes | テストデータ（配列またはテンプレート） | 配列またはTemplateStringsArray |
| data | TemplateData | No | テンプレートリテラルのデータ部分 | - |
| title | string | Yes | テストタイトル（プレースホルダー含む） | - |
| test | EachTestFn | Yes | テスト関数 | - |
| timeout | number | No | タイムアウト値 | - |

### 入力データソース

- テストコード内のデータ定義

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| tests | EachTests | 生成されたテストケース配列（title, arguments） |

### 出力先

- Jestのテスト実行エンジン

## 処理フロー

### 処理シーケンス

```
1. each呼び出し
   └─ テーブルデータを受け取る
2. データ形式判定
   └─ 配列形式かテンプレート形式かを判定
3. テスト生成
   └─ 配列: convertArrayTable
   └─ テンプレート: convertTemplateTable
4. タイトル補間
   └─ %s、%d、%p等のプレースホルダーを値で置換
5. テスト登録
   └─ 生成された各テストケースをJestに登録
```

### フローチャート

```mermaid
flowchart TD
    A[each呼び出し] --> B[install関数]
    B --> C[bind関数呼び出し]
    C --> D{データ形式?}
    D -->|配列| E[validateArrayTable]
    D -->|テンプレート| F[validateTemplateTableArguments]
    E --> G[convertArrayTable]
    F --> H[getHeadingKeys]
    H --> I[convertTemplateTable]
    G --> J[タイトル補間]
    I --> J
    J --> K[テスト関数呼び出し]
    K --> L[Jestにテスト登録]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-48-01 | 形式制限 | .eachは配列またはタグ付きテンプレートリテラルのみ使用可能 | 常時 |
| BR-48-02 | doneサポート | テスト関数のarity（引数数）がパラメータ数より大きい場合、done引数を渡す | 非同期テスト |
| BR-48-03 | プレースホルダー | %s(文字列)、%d(数値)、%i(整数)、%f(小数)、%j(JSON)、%o(オブジェクト)、%p(pretty)、%#(インデックス)、%$(番号) | タイトル補間時 |
| BR-48-04 | エスケープ | %%でパーセント記号をエスケープ | タイトル補間時 |

### 計算ロジック

テンプレートからテーブル変換:
```
行数 = row.length / headings.length
各行 = row.slice(index * headings.length, (index + 1) * headings.length)
テンプレート = Object.fromEntries(行.map((value, i) => [headings[i], value]))
```

## データベース操作仕様

### 操作別データベース影響一覧

該当なし（データベース操作は行わない）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Error | 配列でもテンプレートリテラルでもない | 正しい形式で呼び出す |
| - | Error | テンプレートヘッダーの形式が不正 | パイプ区切りのヘッダーを使用 |

### リトライ仕様

該当なし

## トランザクション仕様

該当なし

## パフォーマンス要件

- テスト生成: ミリ秒以下

## セキュリティ考慮事項

- テストコード内でのみ使用され、外部への影響はない

## 備考

- test.each、it.each、describe.each、test.concurrent.each等をサポート
- skip、onlyバリアントもサポート

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.ts | `packages/jest-each/src/index.ts` | each関数とinstall関数 |

**主要処理フロー**:
1. **13-67行目**: install関数 - グローバルにtest/it/describeをバインド
2. **25-31行目**: test関数の生成とskip/only/concurrentの設定
3. **56-64行目**: describe関数の生成
4. **69-78行目**: each関数 - globalThisを使用してinstall呼び出し

#### Step 2: bind関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | bind.ts | `packages/jest-each/src/bind.ts` | テストデータとテスト関数のバインド |

**主要処理フロー**:
- **32-86行目**: bind関数 - メインのバインディングロジック
- **50-52行目**: 配列/テンプレート形式の判定
- **54-69行目**: テストケースのループと登録
- **108-115行目**: applyArguments - done引数の処理

#### Step 3: 配列テーブル変換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | array.ts | `packages/jest-each/src/table/array.ts` | 配列形式のテーブル変換 |

**主要処理フロー**:
- **23-40行目**: array関数 - メイン変換処理
- **42-48行目**: isTemplates - オブジェクト配列かどうかの判定
- **50-51行目**: normaliseTable - テーブル正規化
- **58-80行目**: formatTitle - タイトルのプレースホルダー置換

#### Step 4: テンプレートテーブル変換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | template.ts | `packages/jest-each/src/table/template.ts` | テンプレート形式のテーブル変換 |

**主要処理フロー**:
- **17-28行目**: template関数 - メイン変換処理
- **30-36行目**: convertRowToTable - 行データをテーブルに変換
- **38-44行目**: convertTableToTemplates - テーブルをテンプレートオブジェクトに変換

### プログラム呼び出し階層図

```
each(table, ...data)
    │
    └─ install(globalThis, table, ...data)
           │
           ├─ bind(g.test)(table, ...data)
           │      │
           │      └─ eachBind(title, test, timeout)
           │             │
           │             ├─ isArrayTable(data) → buildArrayTests()
           │             │      └─ validateArrayTable()
           │             │      └─ convertArrayTable()
           │             │             └─ formatTitle()
           │             │
           │             └─ buildTemplateTests()
           │                    └─ getHeadingKeys()
           │                    └─ validateTemplateTableArguments()
           │                    └─ convertTemplateTable()
           │                           └─ interpolateVariables()
           │
           └─ 各テストケースでcb(row.title, applyArguments(...))
```

### データフロー図

```
[入力]                    [処理]                         [出力]

配列形式:
[[1,2,3], [4,5,6]] ─────▶ convertArrayTable ────────────▶ EachTests
                              │
                              └─ formatTitle('%s + %s = %s')
                                     └─ '1 + 2 = 3', '4 + 5 = 6'

テンプレート形式:
`a | b | sum               convertTemplateTable
  1 | 2 | 3    ────────────▶ │ ──────────────────────▶ EachTests
  4 | 5 | 9`                 └─ [{a:1,b:2,sum:3}, {a:4,b:5,sum:9}]
                                     └─ interpolateVariables('$a + $b')
                                            └─ '1 + 2', '4 + 5'
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/jest-each/src/index.ts` | ソース | エントリーポイント |
| bind.ts | `packages/jest-each/src/bind.ts` | ソース | バインディング処理 |
| validation.ts | `packages/jest-each/src/validation.ts` | ソース | バリデーション |
| array.ts | `packages/jest-each/src/table/array.ts` | ソース | 配列テーブル変換 |
| template.ts | `packages/jest-each/src/table/template.ts` | ソース | テンプレート変換 |
| interpolation.ts | `packages/jest-each/src/table/interpolation.ts` | ソース | 変数補間 |
| __tests__/index.test.ts | `packages/jest-each/src/__tests__/index.test.ts` | テスト | ユニットテスト |
| __tests__/array.test.ts | `packages/jest-each/src/__tests__/array.test.ts` | テスト | 配列テスト |
| __tests__/template.test.ts | `packages/jest-each/src/__tests__/template.test.ts` | テスト | テンプレートテスト |
