# 機能設計書 41-jest-watcher

## 概要

本ドキュメントは、jest-watcherパッケージの機能設計について記載する。jest-watcherは、Jestのウォッチモードにおけるファイル監視とインタラクティブなUIを提供するパッケージである。

### 本機能の処理概要

jest-watcherは、テスト実行のウォッチモードを支援するコンポーネント群を提供する。ウォッチプラグインの基盤クラス、フック機構、テスト実行状態の監視、インタラクティブなプロンプト機能を含む。

**業務上の目的・背景**：開発中にソースコードを変更するたびに手動でテストを再実行するのは非効率である。jest-watcherは、ファイルの変更を検知して自動的にテストを再実行し、開発者がインタラクティブにテストのフィルタリングや実行を制御できるようにすることで、開発効率を大幅に向上させる。これにより、TDD（テスト駆動開発）やリファクタリング作業が円滑になる。

**機能の利用シーン**：開発者が`jest --watch`または`jest --watchAll`コマンドでJestをウォッチモードで起動した際に利用される。ファイル変更時の自動テスト再実行、テスト名やファイル名でのフィルタリング、失敗したテストのみの再実行など、インタラクティブな操作が必要な場面で活用される。

**主要な処理内容**：
1. BaseWatchPlugin: ウォッチプラグインの基底クラスを提供し、カスタムプラグイン開発の基盤となる
2. JestHooks: ファイル変更、テスト完了などのイベントを購読・発行するフック機構を提供
3. TestWatcher: テスト実行の状態（中断状態など）を管理し、状態変更イベントを発行
4. PatternPrompt/Prompt: テスト名やファイル名のフィルタリングのためのインタラクティブなプロンプトUIを提供
5. キー入力ハンドリング: 矢印キー、Enter、Escapeなどのキー入力を処理

**関連システム・外部連携**：@jest/coreパッケージと連携し、ウォッチモードの中核機能を提供する。ttyモジュールを使用した標準入出力の制御、ansi-escapesによるターミナル制御を行う。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | CLIのみで使用される機能のため関連画面なし |

## 機能種別

イベント処理 / インタラクティブUI / 状態管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| stdin | ReadStream | Yes | 標準入力ストリーム | tty.ReadStream型である必要がある |
| stdout | WriteStream | Yes | 標準出力ストリーム | tty.WriteStream型である必要がある |
| isWatchMode | boolean | Yes | ウォッチモードかどうか | - |
| globalConfig | Config.GlobalConfig | No | Jest全体設定 | - |

### 入力データソース

- 標準入力（キー入力）
- ファイルシステムの変更イベント（jest-haste-map経由）
- テスト実行結果（@jest/test-result経由）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| state | State | テスト実行状態（interrupted: boolean） |
| usageInfo | UsageData | プラグインの使用方法情報（key, prompt） |

### 出力先

- 標準出力（ターミナルUI表示）
- イベントリスナー（フック購読者への通知）

## 処理フロー

### 処理シーケンス

```
1. ウォッチモード起動
   └─ TestWatcherインスタンス作成、JestHooksインスタンス作成
2. ウォッチプラグインの初期化
   └─ BaseWatchPluginを継承したプラグインのapply()でフック購読
3. ファイル変更検知
   └─ onFileChangeフックが発火し、購読者に通知
4. テスト実行
   └─ TestWatcherで実行状態を監視
5. テスト完了
   └─ onTestRunCompleteフックが発火し、結果を通知
6. ユーザー入力待機
   └─ Promptクラスでキー入力を処理
7. フィルタリング操作
   └─ PatternPromptでパターン入力UI表示
```

### フローチャート

```mermaid
flowchart TD
    A[ウォッチモード開始] --> B[JestHooks初期化]
    B --> C[プラグイン適用]
    C --> D{ファイル変更検知?}
    D -->|Yes| E[onFileChangeフック発火]
    E --> F[テスト実行]
    D -->|No| G{キー入力?}
    G -->|Yes| H[キー処理]
    H --> I{特殊キー?}
    I -->|Enter| J[フィルタ適用]
    I -->|Escape| K[パターンモード終了]
    I -->|矢印| L[選択変更]
    I -->|その他| M[パターン入力]
    F --> N[onTestRunCompleteフック発火]
    N --> D
    J --> D
    K --> D
    L --> G
    M --> G
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-41-01 | フック購読 | 同一フックに複数のリスナーを登録可能 | 常時 |
| BR-41-02 | shouldRunTestSuite | すべてのリスナーがtrueを返した場合のみテスト実行 | テストスイート実行前 |
| BR-41-03 | キー入力処理 | BackspaceでWindowsは0x08、それ以外は0x7f | プラットフォーム依存 |
| BR-41-04 | スクロール計算 | 表示領域の半分を基準にスクロール位置を決定 | リスト表示時 |

### 計算ロジック

スクロール位置計算（scroll.ts）:
```
halfScreen = max / 2
if (index <= halfScreen) {
  start = 0
} else {
  start = min(index - halfScreen - 1, size - max)
  index = min(index - start, size)
}
end = min(size, start + max)
```

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

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | TypeError | プラグインに予約済みメソッド名を使用 | 別のメソッド名を使用 |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

- キー入力に対するレスポンス: 即時
- ファイル変更検知からテスト開始まで: 数百ミリ秒以内

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

- 標準入出力へのアクセスのみで、外部ネットワークやファイルシステムへの直接アクセスは行わない

## 備考

- Emitteryライブラリを使用したイベント管理
- WindowsとUnix系でBackspaceキーのコードが異なる点に注意

---

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

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

### 推奨読解順序

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

まず、ウォッチャーで使用される型定義を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.ts | `packages/jest-watcher/src/types.ts` | JestHookSubscriber、JestHookEmitter、WatchPlugin、UsageData等の型定義を把握 |
| 1-2 | constants.ts | `packages/jest-watcher/src/constants.ts` | KEYSオブジェクトでキー入力のコード定義を理解 |

**読解のコツ**: TypeScriptの型定義を通じて、各コンポーネント間のインターフェースを理解する。

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

エクスポートされているモジュールの全体像を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `packages/jest-watcher/src/index.ts` | パッケージ全体のエクスポート構造を理解 |

**主要処理フロー**:
1. **8-11行目**: 主要クラス（BaseWatchPlugin、JestHook、PatternPrompt、TestWatcher）のエクスポート
2. **12行目**: 定数のエクスポート
3. **23-24行目**: ライブラリユーティリティのエクスポート

#### Step 3: JestHooksフック機構を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | JestHooks.ts | `packages/jest-watcher/src/JestHooks.ts` | フックの購読・発行パターンを理解 |

**主要処理フロー**:
- **21-36行目**: 3種類のフック（onFileChange、onTestRunComplete、shouldRunTestSuite）のリスナー配列定義
- **38-48行目**: _subscriber - フック購読用インターフェース
- **50-67行目**: _emitter - フック発行用インターフェース
- **58-66行目**: shouldRunTestSuiteは全リスナーの結果をPromise.allで収集し、every(Boolean)で判定

#### Step 4: TestWatcher状態管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | TestWatcher.ts | `packages/jest-watcher/src/TestWatcher.ts` | テスト実行状態の管理方法を理解 |

**主要処理フロー**:
- **14行目**: Emitteryを継承したイベントエミッター
- **18-22行目**: コンストラクタでisWatchModeフラグと初期状態を設定
- **24-27行目**: setState()で状態更新とイベント発火

#### Step 5: ウォッチプラグイン基底クラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | BaseWatchPlugin.ts | `packages/jest-watcher/src/BaseWatchPlugin.ts` | プラグイン拡張の基盤を理解 |

**主要処理フロー**:
- **17行目**: WatchPluginインターフェースを実装
- **21-24行目**: stdin/stdoutをコンストラクタで受け取り保持
- **27-41行目**: apply()、getUsageInfo()、onKey()、run()のデフォルト実装

#### Step 6: プロンプトUIを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | Prompt.ts | `packages/jest-watcher/src/lib/Prompt.ts` | キー入力処理とプロンプト状態管理 |
| 6-2 | PatternPrompt.ts | `packages/jest-watcher/src/PatternPrompt.ts` | パターン入力UIの基底クラス |

**主要処理フロー**:
- **Prompt.ts 71-108行目**: put()メソッドでキー入力を処理（Enter、Escape、矢印キー等）
- **PatternPrompt.ts 36-55行目**: run()メソッドでプロンプトUIを表示

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

```
@jest/core (watchモード)
    │
    ├─ JestHooks
    │      ├─ getSubscriber() → ウォッチプラグインへ
    │      └─ getEmitter() → @jest/coreへ
    │
    ├─ TestWatcher
    │      └─ setState() → 状態変更イベント発火
    │
    └─ WatchPlugin (BaseWatchPlugin継承)
           ├─ apply(hooks) → フック購読
           ├─ getUsageInfo() → ヘルプ表示情報
           ├─ onKey(key) → キー入力処理
           └─ run() → プラグイン実行
                  └─ PatternPrompt
                         └─ Prompt
                                └─ put(key) → キー処理
```

### データフロー図

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

ファイル変更 ────────▶ JestHooks._emitter.onFileChange() ──▶ リスナーへ通知

テスト結果 ────────────▶ JestHooks._emitter.onTestRunComplete() ──▶ リスナーへ通知

キー入力 ──────────────▶ Prompt.put() ──────────────────────▶ コールバック実行
                            │
                            ├─ Enter → onSuccess
                            ├─ Escape → onCancel
                            └─ 文字 → onChange

状態変更 ──────────────▶ TestWatcher.setState() ────────────▶ 'change'イベント発火
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/jest-watcher/src/index.ts` | ソース | パッケージエントリーポイント |
| types.ts | `packages/jest-watcher/src/types.ts` | ソース | 型定義 |
| constants.ts | `packages/jest-watcher/src/constants.ts` | ソース | キーコード定数 |
| BaseWatchPlugin.ts | `packages/jest-watcher/src/BaseWatchPlugin.ts` | ソース | プラグイン基底クラス |
| JestHooks.ts | `packages/jest-watcher/src/JestHooks.ts` | ソース | フック機構 |
| TestWatcher.ts | `packages/jest-watcher/src/TestWatcher.ts` | ソース | テスト状態監視 |
| PatternPrompt.ts | `packages/jest-watcher/src/PatternPrompt.ts` | ソース | パターン入力UI基底 |
| Prompt.ts | `packages/jest-watcher/src/lib/Prompt.ts` | ソース | プロンプト入力処理 |
| scroll.ts | `packages/jest-watcher/src/lib/scroll.ts` | ソース | スクロール計算 |
| colorize.ts | `packages/jest-watcher/src/lib/colorize.ts` | ソース | テキスト色付け |
| formatTestNameByPattern.ts | `packages/jest-watcher/src/lib/formatTestNameByPattern.ts` | ソース | テスト名フォーマット |
| patternModeHelpers.ts | `packages/jest-watcher/src/lib/patternModeHelpers.ts` | ソース | パターンモードヘルパー |
