# 機能設計書 55-@jest/test-utils

## 概要

本ドキュメントは、Jestテスティングフレームワークにおける `@jest/test-utils` パッケージの機能設計について記述する。このパッケージは、Jest内部のテストコードで使用するユーティリティ関数を提供するプライベートパッケージである。

### 本機能の処理概要

`@jest/test-utils` は、Jest自身のテストコードをより効率的かつ一貫性を持って記述するためのユーティリティ関数群を提供する。条件付きテスト実行、設定ファクトリ、出力正規化などの機能を含む。

**業務上の目的・背景**：Jestは自身のコードベースをJestでテストしている（セルフホスティング）。多くのテストで共通して必要となるユーティリティ（テスト環境の判定、設定オブジェクトの生成、出力の正規化等）を一箇所に集約することで、テストコードの重複を削減し、保守性を向上させる。

**機能の利用シーン**：
- Jest内部パッケージのテストコード作成時
- jest-jasmine2 / jest-circus 環境での条件付きテスト
- Node.jsバージョン依存のテスト実行
- スナップショットテストでのANSIエスケープシーケンス正規化
- GlobalConfig / ProjectConfig のモックオブジェクト生成

**主要な処理内容**：
1. 条件付きテスト実行（skipSuiteOnJasmine、skipSuiteOnJestCircus、onNodeVersions）
2. 設定ファクトリ関数（makeGlobalConfig、makeProjectConfig）
3. ANSIスタイルシリアライザ（alignedAnsiStyleSerializer）
4. アイコン正規化（normalizeIcons）

**関連システム・外部連携**：
- `@jest/globals`：describe、testのインポート
- `@jest/pattern`：TestPathPatternsの利用
- `@jest/types`：Config型の参照
- `pretty-format`：スナップショットシリアライザのNewPlugin型
- `semver`：Node.jsバージョン判定

**権限による制御**：プライベートパッケージであり、Jest内部でのみ使用される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 内部ユーティリティパッケージのため画面なし |

## 機能種別

テストユーティリティ / ヘルパー関数

## 入力仕様

### 入力パラメータ

#### makeGlobalConfig / makeProjectConfig

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| overrides | Partial<Config.GlobalConfig> / Partial<Config.ProjectConfig> | No | 上書きする設定値 | 存在しないキーはエラー |

#### onNodeVersions

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| versionRange | string | Yes | semverバージョン範囲 | semver形式 |
| testBody | () => void | Yes | テスト本体 | - |

#### normalizeIcons

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| str | string | Yes | 正規化対象文字列 | - |

### 入力データソース

- テストコードからの直接呼び出し
- 環境変数 `JEST_JASMINE` によるテストランナー判定

## 出力仕様

### 出力データ

| 関数名 | 戻り値型 | 説明 |
|--------|---------|------|
| isJestJasmineRun | boolean | Jasmineランナーで実行中かどうか |
| skipSuiteOnJasmine | void | Jasmine環境でテストスイートをスキップ |
| skipSuiteOnJestCircus | void | jest-circus環境でテストスイートをスキップ |
| onNodeVersions | void | 指定バージョンでのみテスト実行 |
| makeGlobalConfig | Config.GlobalConfig | グローバル設定オブジェクト |
| makeProjectConfig | Config.ProjectConfig | プロジェクト設定オブジェクト |
| alignedAnsiStyleSerializer | NewPlugin | pretty-format用シリアライザ |
| normalizeIcons | string | 正規化された文字列 |

### 出力先

- テスト実行制御
- 設定オブジェクト
- スナップショットシリアライザ

## 処理フロー

### 処理シーケンス

#### 条件付きテスト実行

```
1. isJestJasmineRun()
   └─ process.env.JEST_JASMINE === '1' を判定

2. skipSuiteOnJasmine()
   └─ Jasmine環境の場合、test.only()でスキップメッセージを出力
   └─ jest-circus環境では何もしない

3. skipSuiteOnJestCircus()
   └─ jest-circus環境の場合、test.only()でスキップメッセージを出力
   └─ Jasmine環境では何もしない

4. onNodeVersions(versionRange, testBody)
   └─ semver.satisfiesでバージョン判定
   └─ 一致: describe()でテスト実行
   └─ 不一致: describe.skip()でスキップ
```

#### 設定ファクトリ

```
1. makeGlobalConfig(overrides)
   └─ overridesのキーを検証
   └─ DEFAULT_GLOBAL_CONFIGにマージして返却

2. makeProjectConfig(overrides)
   └─ overridesのキーを検証
   └─ DEFAULT_PROJECT_CONFIGにマージして返却
```

### フローチャート

```mermaid
flowchart TD
    subgraph 条件付きテスト
        A[skipSuiteOnJasmine] --> B{JEST_JASMINE=1?}
        B -->|Yes| C[test.only スキップ]
        B -->|No| D[何もしない]

        E[onNodeVersions] --> F{バージョン一致?}
        F -->|Yes| G[describe でテスト実行]
        F -->|No| H[describe.skip]
    end

    subgraph 設定ファクトリ
        I[makeGlobalConfig] --> J{キー検証}
        J -->|OK| K[DEFAULTにマージ]
        J -->|NG| L[Errorスロー]
    end
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-55-01 | プライベートパッケージ | package.jsonにprivate: trueが設定されている | 常時 |
| BR-55-02 | 厳格なキー検証 | makeGlobalConfig/makeProjectConfigは存在しないキーでエラー | オーバーライド指定時 |
| BR-55-03 | 環境変数による判定 | JEST_JASMINE=1でJasmine環境と判定 | テストランナー判定時 |
| BR-55-04 | アイコン正規化 | \u00D7→\u2715、\u221A→\u2713に変換 | normalizeIcons呼び出し時 |

### 計算ロジック

#### アイコン正規化

```typescript
// Windows/Unix間でのアイコン表示差異を吸収
str.replaceAll(new RegExp('\u00D7', 'gu'), '\u2715')  // × → ✕
   .replaceAll(new RegExp('\u221A', 'gu'), '\u2713')  // √ → ✓
```

#### ANSIスタイル変換

```typescript
// ANSIエスケープシーケンスをタグ形式に変換
switch (match) {
  case style.inverse.open: return '<i>';
  case style.bold.open: return '<b>';
  case style.dim.open: return '<d>';
  case style.green.open: return '<g>';
  case style.red.open: return '<r>';
  case style.yellow.open: return '<y>';
  case style.bgYellow.open: return '<Y>';
  // closeタグは全て '</>' に統一
}
```

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

本パッケージはデータベース操作を行わない。

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

該当なし

### テーブル別操作詳細

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| Error | 設定キーエラー | GlobalConfig/ProjectConfigに存在しないキーを指定 | 正しいキー名を使用 |

### リトライ仕様

該当なし（同期処理）

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

該当なし

## パフォーマンス要件

- 全て同期処理であり即座に完了
- 正規表現によるアイコン正規化は文字列長に比例

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

- プライベートパッケージであり外部公開されない
- 環境変数 JEST_JASMINE の読み取りのみ

## 備考

- Jest内部使用のみ（private: true）
- Node.js ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 が必要
- バージョン 30.2.0 時点の情報

---

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

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

### 推奨読解順序

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

パッケージの公開APIを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.ts | `packages/test-utils/src/index.ts` | エクスポートされる全関数 |

**主要処理フロー**:
- **8行目**: alignedAnsiStyleSerializerをエクスポート
- **10-15行目**: ConditionalTestから4関数をエクスポート
- **17行目**: configからmakeGlobalConfig、makeProjectConfigをエクスポート
- **19行目**: normalizeIconsをエクスポート

**読解のコツ**: 各モジュールは単一責任を持つ小さなファイルとして分割されている。

#### Step 2: 条件付きテスト機能を理解する

テスト環境に応じたスキップ機能を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ConditionalTest.ts | `packages/test-utils/src/ConditionalTest.ts` | 条件付きテスト実行関数 |

**主要処理フロー**:
- **13-15行目**: isJestJasmineRun - JEST_JASMINE環境変数チェック
- **17-23行目**: skipSuiteOnJasmine - Jasmine環境でtest.onlyによるスキップ
- **25-31行目**: skipSuiteOnJestCircus - jest-circus環境でスキップ
- **33-47行目**: onNodeVersions - semverでバージョン判定し条件付き実行

**読解のコツ**: `test.only` を使うことで、他のテストを実行せずにスキップメッセージのみ出力する。eslint-disable-next-line jest/no-focused-tests でlint警告を抑制。

#### Step 3: 設定ファクトリを理解する

テスト用設定オブジェクトの生成を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | config.ts | `packages/test-utils/src/config.ts` | 設定ファクトリ関数 |

**主要処理フロー**:
- **11-71行目**: DEFAULT_GLOBAL_CONFIG - グローバル設定のデフォルト値
- **73-139行目**: DEFAULT_PROJECT_CONFIG - プロジェクト設定のデフォルト値
- **141-157行目**: makeGlobalConfig - オーバーライドとマージ、キー検証
- **159-175行目**: makeProjectConfig - オーバーライドとマージ、キー検証

**読解のコツ**: 存在しないキーが渡された場合はエラーをスローし、テストのミスを早期発見できるようにしている。

#### Step 4: ANSIスタイルシリアライザを理解する

スナップショット用のANSI変換を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | alignedAnsiStyleSerializer.ts | `packages/test-utils/src/alignedAnsiStyleSerializer.ts` | ANSIスタイルシリアライザ |

**主要処理フロー**:
- **12-51行目**: alignedAnsiStyleSerializer - pretty-format用NewPlugin
- **13-46行目**: serialize - ANSIエスケープをタグに変換
- **48-50行目**: test - 文字列型かどうかの判定

**読解のコツ**: ansi-stylesパッケージの定数（style.bold.open等）とマッチングしてタグに変換。closeタグは全て `</>` に統一することで、スナップショットの可読性を向上。

#### Step 5: アイコン正規化を理解する

プラットフォーム間の差異吸収を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | normalizeIcons.ts | `packages/test-utils/src/normalizeIcons.ts` | アイコン正規化関数 |

**主要処理フロー**:
- **8-17行目**: normalizeIcons - Unicode文字の正規化
- **14行目**: \u00D7（乗算記号）→ \u2715（X記号）
- **16行目**: \u221A（ルート記号）→ \u2713（チェックマーク）

**読解のコツ**: jest-utilのspecialCharsと同期を保つ必要があるとコメントに記載。Windows/macOS/Linux間でのアイコン表示差異を吸収。

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

```
@jest/test-utils/src/index.ts
    │
    ├─ alignedAnsiStyleSerializer.ts
    │      ├─ ansi-regex (ANSIエスケープ検出)
    │      └─ ansi-styles (スタイル定数)
    │
    ├─ ConditionalTest.ts
    │      ├─ semver (バージョン比較)
    │      └─ @jest/globals (describe, test)
    │
    ├─ config.ts
    │      ├─ @jest/pattern (TestPathPatterns)
    │      └─ @jest/types (Config型)
    │
    └─ normalizeIcons.ts
           └─ (外部依存なし)
```

### データフロー図

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

環境変数 ─────────────────▶ isJestJasmineRun ──────────────▶ boolean
JEST_JASMINE                     │
                                 ▼
                        skipSuiteOnJasmine ──────────────▶ テストスキップ

Node.js version ─────────▶ onNodeVersions ────────────────▶ 条件付きテスト
                                 │
                                 └─ semver.satisfies

Config overrides ─────────▶ makeGlobalConfig ─────────────▶ Config.GlobalConfig
                                 │
                                 └─ キー検証 → マージ

ANSI文字列 ───────────────▶ alignedAnsiStyleSerializer ───▶ タグ付き文字列
                                 │
                                 └─ ansi-regex → 置換

アイコン文字列 ───────────▶ normalizeIcons ───────────────▶ 正規化文字列
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/test-utils/src/index.ts` | ソース | エントリポイント、エクスポート |
| ConditionalTest.ts | `packages/test-utils/src/ConditionalTest.ts` | ソース | 条件付きテスト実行 |
| config.ts | `packages/test-utils/src/config.ts` | ソース | 設定ファクトリ関数 |
| alignedAnsiStyleSerializer.ts | `packages/test-utils/src/alignedAnsiStyleSerializer.ts` | ソース | ANSIスタイルシリアライザ |
| normalizeIcons.ts | `packages/test-utils/src/normalizeIcons.ts` | ソース | アイコン正規化 |
| package.json | `packages/test-utils/package.json` | 設定 | パッケージメタデータ（private: true） |
