# 機能設計書 36-jest-diff

## 概要

本ドキュメントは、jest-diffパッケージの機能設計を記載する。jest-diffは、2つの値の差分を計算し、GitHub風の差分表示（緑/赤でのハイライト）を生成する機能を提供する。

### 本機能の処理概要

jest-diffは、2つの任意のJavaScript値を比較し、その差分を人間が読みやすい形式で表示するライブラリである。

**業務上の目的・背景**：テスト実行時にアサーションが失敗した場合、期待値と実際の値の違いを開発者が素早く理解できることが重要である。単純に2つの値を表示するだけでなく、差分をハイライト表示することで、どこが異なるのかを一目で把握できるようにする必要がある。

**機能の利用シーン**：
- expect(actual).toEqual(expected) の失敗時
- expect(actual).toMatchSnapshot() の差分表示
- スナップショットの更新確認
- jest-matcherでのカスタムマッチャー作成

**主要な処理内容**：
1. diff関数: 2つの任意の値の差分を計算
2. diffLinesUnified: 行単位の差分表示（unified形式）
3. diffLinesRaw: 行単位の差分データ生成
4. diffStringsUnified: 文字列の差分表示
5. diffStringsRaw: 文字列の差分データ生成
6. 型の違いの検出と表示

**関連システム・外部連携**：
- @jest/diff-sequences: 最長共通部分列アルゴリズムの提供
- @jest/get-type: 値の型判定
- pretty-format: 値の文字列化
- chalk: 色付け出力

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | CLI出力 | 結果表示 | テスト失敗時の差分表示 |

## 機能種別

差分計算 / データ比較 / ユーティリティ

## 入力仕様

### 入力パラメータ

#### diff関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| a | any | Yes | 比較元の値（期待値） | - |
| b | any | Yes | 比較先の値（実際の値） | - |
| options | DiffOptions | No | 差分表示オプション | - |

#### DiffOptions

| パラメータ名 | 型 | 必須 | 説明 | デフォルト値 |
|-------------|-----|-----|------|-------------|
| aAnnotation | string | No | 期待値のラベル | 'Expected' |
| aColor | DiffOptionsColor | No | 期待値の色関数 | chalk.green |
| aIndicator | string | No | 期待値のインジケータ | '-' |
| bAnnotation | string | No | 実際値のラベル | 'Received' |
| bColor | DiffOptionsColor | No | 実際値の色関数 | chalk.red |
| bIndicator | string | No | 実際値のインジケータ | '+' |
| changeColor | DiffOptionsColor | No | 変更部分の色関数 | chalk.inverse |
| commonColor | DiffOptionsColor | No | 共通部分の色関数 | chalk.dim |
| contextLines | number | No | 差分周辺の表示行数 | 5 |
| expand | boolean | No | 全行を展開表示 | true |
| includeChangeCounts | boolean | No | 変更行数を表示 | false |
| omitAnnotationLines | boolean | No | 注釈行を省略 | false |
| compareKeys | CompareKeys | No | オブジェクトキーの比較関数 | undefined |

### 入力データソース

- expectマッチャー: アサーション失敗時の期待値と実際値
- jest-snapshot: スナップショットと実際の値

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | string \| null | 差分表示文字列、または差分がない場合のメッセージ/null |

### 出力先

関数の戻り値として返却（コンソール出力は呼び出し元で行う）

## 処理フロー

### 処理シーケンス

```
1. diff関数の呼び出し
   └─ 2つの値と差分オプションを受け取る
2. 同値チェック
   └─ Object.is(a, b)がtrueなら"no diff"メッセージを返す
3. 型チェック
   └─ @jest/get-typeで両値の型を取得
   └─ 型が異なる場合は型の違いを表示
4. 型に応じた差分処理
   └─ string: 行単位での差分比較
   └─ boolean/number: pretty-formatで文字列化して比較
   └─ map/set: ソート後にオブジェクトとして比較
   └─ object: pretty-formatで文字列化して行単位比較
5. 差分結果の整形
   └─ 色付けとインデントを適用
```

### フローチャート

```mermaid
flowchart TD
    A[diff開始] --> B{Object.is\(a, b\)?}
    B -->|Yes| C[NO_DIFF_MESSAGEを返却]
    B -->|No| D[getType\(a\), getType\(b\)]
    D --> E{型が一致?}
    E -->|No| F[型の違いを表示]
    E -->|Yes| G{aの型は?}
    G -->|string| H[diffLinesUnified]
    G -->|boolean/number| I[comparePrimitive]
    G -->|map| J[sortMap → compareObjects]
    G -->|set| K[sortSet → compareObjects]
    G -->|default| L[compareObjects]
    H --> M[結果を返却]
    I --> M
    J --> M
    K --> M
    L --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 同値判定 | Object.isで同値の場合、差分なしメッセージを返す | diff関数呼び出し時 |
| BR-02 | 型不一致処理 | 型が異なる場合、型の違いを表示 | getType(a) !== getType(b) |
| BR-03 | 非対称マッチャー | expect.any()等の非対称マッチャーは特別処理 | asymmetricMatchが関数の場合 |
| BR-04 | Map/Setソート | MapとSetは比較前にソートする | aTypeがmapまたはset |
| BR-05 | toJSONフォールバック | toJSON呼び出しで差分が出ない場合、toJSONなしで再比較 | オブジェクト比較時 |

### 計算ロジック

**差分タイプ定数**：
```typescript
const DIFF_DELETE = 0;  // 削除（期待値のみ）
const DIFF_INSERT = 1;  // 挿入（実際値のみ）
const DIFF_EQUAL = 2;   // 共通
```

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 例外 | pretty-format実行時のエラー | フォールバック処理（toJSONなし）を試行 |

### リトライ仕様

- toJSON呼び出しで差分が出ない/例外が発生した場合、callToJSON=falseで再試行

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

該当なし

## パフォーマンス要件

- 大きなオブジェクトでも適切な時間で差分計算を完了すること
- maxDepth/maxWidthオプションで出力量を制限可能

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

- 差分出力にセンシティブな情報が含まれる可能性がある

## 備考

- 差分アルゴリズムには@jest/diff-sequencesの最長共通部分列アルゴリズムを使用
- 出力形式はunified diff形式に準拠

---

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

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

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、型定義から理解を始める。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.ts | `packages/jest-diff/src/types.ts` | DiffOptions、DiffOptionsNormalized型の定義 |

**主要処理フロー**:
1. **11-30行目**: DiffOptions型（ユーザー入力オプション）
2. **32-51行目**: DiffOptionsNormalized型（正規化済みオプション）

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `packages/jest-diff/src/index.ts` | diff関数のメインロジック |

**主要処理フロー**:
1. **63-115行目**: diff関数
   - **64-66行目**: Object.isでの同値チェック
   - **68-84行目**: 型チェックと非対称マッチャー処理
   - **86-92行目**: 型不一致の場合のメッセージ生成
   - **98-114行目**: 型に応じた差分処理の分岐
2. **117-127行目**: comparePrimitive（プリミティブ値の比較）
3. **129-135行目**: sortMap、sortSet（Map/Setのソート）
4. **137-168行目**: compareObjects（オブジェクトの比較）
5. **182-206行目**: getObjectsDifference（オブジェクト差分の取得）

#### Step 3: 行単位差分処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | diffLines.ts | `packages/jest-diff/src/diffLines.ts` | 行単位の差分処理 |

**主要処理フロー**:
1. **88-95行目**: printDiffLines（差分の出力整形）
2. **98-109行目**: diffLinesUnified（行単位差分のunified表示）
3. **114-163行目**: diffLinesUnified2（表示用/比較用の分離処理）
4. **166-207行目**: diffLinesRaw（行単位の差分データ生成）
   - **196行目**: @jest/diff-sequencesのdiff関数呼び出し
   - **180-194行目**: foundSubsequenceコールバック（共通部分の処理）

#### Step 4: 定数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | constants.ts | `packages/jest-diff/src/constants.ts` | メッセージ定数 |

**主要処理フロー**:
1. **8行目**: NO_DIFF_MESSAGE（差分なしメッセージ）
2. **10-12行目**: SIMILAR_MESSAGE（構造同一メッセージ）

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

```
diff (index.ts)
    │
    ├─ getType (@jest/get-type)
    │
    ├─ diffLinesUnified (string型の場合)
    │      └─ diffLinesRaw
    │             └─ diff (@jest/diff-sequences)
    │
    ├─ comparePrimitive (boolean/number型の場合)
    │      ├─ prettyFormat (pretty-format)
    │      └─ diffLinesUnified
    │
    └─ compareObjects (object型の場合)
           ├─ prettyFormat (pretty-format)
           └─ diffLinesUnified2
                  └─ diffLinesRaw
                         └─ diff (@jest/diff-sequences)
```

### データフロー図

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

期待値(a) ─────────────────▶ diff()
                                  │
実際値(b) ─────────────────▶      │
                                  │
                                  ├─ Object.is? → NO_DIFF_MESSAGE
                                  │
                                  ├─ 型不一致? → "Comparing two different types..."
                                  │
                                  └─ 型一致
                                         │
                                         ├─ string → diffLinesUnified
                                         │              │
                                         │              └─ @jest/diff-sequences
                                         │
                                         ├─ boolean/number → comparePrimitive
                                         │                      │
                                         │                      └─ prettyFormat → diff
                                         │
                                         └─ object → compareObjects
                                                        │
                                                        ├─ prettyFormat
                                                        └─ diffLinesUnified2
                                                               │
                                                               ▼
                                                        差分表示文字列
                                                        - Expected
                                                        + Received

例：
"hello" vs "world"
         │
         ▼
- Expected
+ Received

- hello
+ world
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/jest-diff/src/index.ts` | ソース | メインのdiff関数とエクスポート |
| types.ts | `packages/jest-diff/src/types.ts` | ソース | 型定義 |
| diffLines.ts | `packages/jest-diff/src/diffLines.ts` | ソース | 行単位の差分処理 |
| printDiffs.ts | `packages/jest-diff/src/printDiffs.ts` | ソース | 文字列差分の表示 |
| cleanupSemantic.ts | `packages/jest-diff/src/cleanupSemantic.ts` | ソース | Diffクラスと差分タイプ定数 |
| constants.ts | `packages/jest-diff/src/constants.ts` | ソース | メッセージ定数 |
| normalizeDiffOptions.ts | `packages/jest-diff/src/normalizeDiffOptions.ts` | ソース | オプションの正規化 |
| getAlignedDiffs.ts | `packages/jest-diff/src/getAlignedDiffs.ts` | ソース | 差分の整列処理 |
| joinAlignedDiffs.ts | `packages/jest-diff/src/joinAlignedDiffs.ts` | ソース | 整列した差分の結合 |
| escapeControlCharacters.ts | `packages/jest-diff/src/escapeControlCharacters.ts` | ソース | 制御文字のエスケープ |
| package.json | `packages/jest-diff/package.json` | 設定 | パッケージ定義 |
