# 通知設計書 1-テスト成功通知

## 概要

本ドキュメントは、Jestテストフレームワークにおけるデスクトップ通知機能のうち、テスト実行完了時に全テストが成功した場合に送信される「テスト成功通知」の設計を記載する。

### 本通知の処理概要

テスト成功通知は、Jestのテスト実行が完了し、全てのテストが成功した場合にデスクトップ通知としてユーザーに成功を通知する機能である。node-notifierパッケージを利用してOS標準の通知システムを通じて視覚的なフィードバックを提供する。

**業務上の目的・背景**：開発者がテスト実行中に他の作業を行っている場合でも、テストが成功したことを即座に把握できるようにすることで、開発効率を向上させる。特にwatchモードで継続的にテストを実行している場合に有用であり、テスト完了を待つために画面を監視し続ける必要がなくなる。

**通知の送信タイミング**：テスト実行が完了し（onRunComplete イベント）、かつ全てのテストが成功した場合（numFailedTests === 0 かつ numRuntimeErrorTestSuites === 0）に送信される。ただし、notifyMode設定により通知条件が制御される。

**通知の受信者**：テストを実行した開発者本人のデスクトップに通知が表示される。node-notifierが利用するOS標準の通知システムを通じて配信される。

**通知内容の概要**：タイトルにはパッケージ名と「100% Passed」が表示され、メッセージには合格したテスト数が「X tests passed」の形式で表示される。macOSではチェックマーク絵文字が付加される。

**期待されるアクション**：開発者はテストが成功したことを確認し、次のタスク（コミット、プルリクエスト作成、次の機能開発など）に進むことができる。

## 通知種別

デスクトップ通知（OS標準通知システム経由）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（onRunComplete内で直接呼び出し） |
| 優先度 | 中 |
| リトライ | なし |

### 送信先決定ロジック

テストを実行した開発者のローカルマシンに対して、OS標準の通知システム（macOS: Notification Center、Windows: Toast Notifications、Linux: libnotify等）を通じて通知を表示する。node-notifierパッケージがOSに応じた適切な通知メカニズムを自動選択する。

## 通知テンプレート

### デスクトップ通知の場合

| 項目 | 内容 |
|-----|------|
| アイコン | Jestロゴ（`jest_logo.png`） |
| タイトル | `{packageName} - 100% Passed` |
| メッセージ | macOS: `✅ X tests passed` / 他OS: `X tests passed` |

### 本文テンプレート

```
タイトル: {packageName} - 100% Passed
メッセージ: ✅ {numPassedTests} tests passed
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| jest_logo.png | PNG | 常に | 通知アイコンとして使用 |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| packageName | プロジェクト名またはルートディレクトリ | hasteFS.getModuleName() または globalConfig.rootDir | Yes |
| numPassedTests | 合格したテスト数 | AggregatedResult.numPassedTests | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| イベント | onRunComplete | テスト成功 かつ notifyMode条件合致 | テスト実行完了時に発火 |

### 送信条件詳細

以下の全ての条件を満たす場合に通知が送信される：

1. `testsHaveRun = true`（numTotalTests !== 0）
2. `success = true`（numFailedTests === 0 かつ numRuntimeErrorTestSuites === 0）
3. notifyModeが以下のいずれかに該当：
   - `always`: 常に通知
   - `success`: 成功時は常に通知
   - `success-change`: 成功時は常に通知
   - `change`: ステータスが変化した場合のみ通知
   - `failure-change`: ステータスが変化した場合のみ通知

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| numTotalTests === 0 | テストが1件も実行されていない場合 |
| notifyMode === 'failure' | 失敗時のみ通知の設定 |
| notifyMode === 'change' かつ statusChanged === false | ステータス変化なしの場合 |
| node-notifier未インストール | 必須の依存パッケージがない場合（エラーがスローされる） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[onRunComplete発火] --> B{テスト実行あり?}
    B -->|No| Z[終了]
    B -->|Yes| C{全テスト成功?}
    C -->|No| Z
    C -->|Yes| D{notifyMode条件合致?}
    D -->|No| Z
    D -->|Yes| E[パッケージ名取得]
    E --> F[タイトル生成: packageName - 100% Passed]
    F --> G[メッセージ生成: X tests passed]
    G --> H[node-notifier.notify呼び出し]
    H --> I[コンテキスト更新]
    I --> Z
```

## データベース参照・更新仕様

### 参照テーブル一覧

本通知はデータベースを使用しない。メモリ上のAggregatedResult構造体からデータを取得する。

| データソース | 用途 | 備考 |
|-----------|------|------|
| AggregatedResult | テスト実行結果の取得 | onRunComplete引数として渡される |
| ReporterContext | 前回の成功状態・初回実行フラグ | ステータス変化判定に使用 |
| hasteFS | パッケージ名の取得 | TestContextから取得 |

### 参照項目詳細

#### AggregatedResult

| 参照項目 | 用途 | 取得条件 |
|--------|------|---------|
| numFailedTests | 成功判定 | 0であること |
| numRuntimeErrorTestSuites | 成功判定 | 0であること |
| numTotalTests | 実行有無判定 | 0より大きいこと |
| numPassedTests | メッセージ表示 | なし |

#### ReporterContext

| 参照項目 | 用途 | 取得条件 |
|--------|------|---------|
| previousSuccess | ステータス変化判定 | なし |
| firstRun | 初回実行判定 | なし |

### 更新テーブル一覧

| データソース | 操作 | 概要 |
|-----------|------|------|
| ReporterContext | UPDATE | 成功状態とfirstRunフラグを更新 |

#### ReporterContext更新

| 操作 | 項目 | 更新値 | 備考 |
|-----|------|-------|------|
| UPDATE | previousSuccess | success（true/false） | 次回判定用 |
| UPDATE | firstRun | false | 初回実行完了を記録 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| MODULE_NOT_FOUND | node-notifierがインストールされていない | エラーメッセージを表示してスロー |
| 通知システムエラー | OS通知システムが利用不可 | node-notifierがハンドリング（サイレント失敗） |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（テスト実行完了時に即座に通知）

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

- 通知内容にはパッケージ名とテスト数のみが含まれ、機密情報は含まれない
- ローカルマシン内でのみ通知が表示されるため、外部への情報漏洩リスクはない
- node-notifierは外部サービスとの通信を行わない

## 備考

- node-notifierパッケージはオプショナルなピア依存関係として定義されている
- macOSでは通知メッセージの先頭にチェックマーク絵文字（✅）が付加される（isDarwin判定）
- hint: 'int:transient:1' オプションにより、一時的な通知として扱われる
- timeout: false により、通知はユーザーが閉じるまで表示される

---

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

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

### 推奨読解順序

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

まず、通知処理で使用されるデータ構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.ts | `packages/jest-reporters/src/types.ts` | ReporterContext型の定義を確認（firstRun, previousSuccess） |
| 1-2 | Config.ts | `packages/jest-types/src/Config.ts` | notifyMode等の設定値の型定義を確認 |

**読解のコツ**: TypeScriptの型定義ファイルを先に読むことで、後続のコードで使用される変数の意味が明確になる。

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

処理の起点となるファイル・関数を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | NotifyReporter.ts | `packages/jest-reporters/src/NotifyReporter.ts` | onRunCompleteメソッドが通知の起点 |

**主要処理フロー**:
1. **34行目**: `onRunComplete`メソッドの開始
2. **38-39行目**: 成功判定（numFailedTests === 0 かつ numRuntimeErrorTestSuites === 0）
3. **61-73行目**: notifyMode条件の評価
4. **74-87行目**: 成功通知の送信処理

#### Step 3: 通知送信処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | NotifyReporter.ts | `packages/jest-reporters/src/NotifyReporter.ts` | 74-87行目で通知パラメータの構築と送信を実行 |

**主要処理フロー**:
- **75行目**: タイトル生成（`util.format('%s%d%% Passed', packageName, 100)`）
- **76-79行目**: メッセージ生成（macOSでは絵文字付加）
- **81-87行目**: node-notifier.notify呼び出し

#### Step 4: 依存パッケージのロードを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | NotifyReporter.ts | `packages/jest-reporters/src/NotifyReporter.ts` | 158-170行目のloadNotifier関数 |

**主要処理フロー**:
- **160行目**: require('node-notifier')でパッケージをロード
- **161-168行目**: MODULE_NOT_FOUNDエラー時のハンドリング

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

```
jest-core/runJest
    │
    ├─ ReporterDispatcher.onRunComplete()
    │      │
    │      └─ NotifyReporter.onRunComplete()
    │             │
    │             ├─ 成功判定処理
    │             │
    │             ├─ notifyMode条件評価
    │             │
    │             ├─ パッケージ名取得
    │             │      └─ hasteFS.getModuleName()
    │             │
    │             └─ node-notifier.notify()
    │                    └─ OS標準通知システム
    │
    └─ コンテキスト更新
           ├─ previousSuccess = success
           └─ firstRun = false
```

### データフロー図

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

AggregatedResult ───────▶ NotifyReporter
  numFailedTests              │
  numRuntimeErrorTestSuites   ├─▶ 成功判定
  numTotalTests               │
  numPassedTests              │
                              │
GlobalConfig ───────────────▶ │
  notifyMode                  ├─▶ 通知条件判定
  rootDir                     │
                              │
TestContext ────────────────▶ │
  hasteFS                     ├─▶ パッケージ名取得
                              │
ReporterContext ────────────▶ │
  previousSuccess             ├─▶ ステータス変化判定  ───▶ デスクトップ通知
  firstRun                    │                            - title
                              │                            - message
                              └─▶ コンテキスト更新         - icon
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| NotifyReporter.ts | `packages/jest-reporters/src/NotifyReporter.ts` | ソース | メイン通知処理 |
| BaseReporter.ts | `packages/jest-reporters/src/BaseReporter.ts` | ソース | 基底クラス |
| types.ts | `packages/jest-reporters/src/types.ts` | ソース | ReporterContext型定義 |
| Config.ts | `packages/jest-types/src/Config.ts` | ソース | GlobalConfig型定義（notifyMode含む） |
| jest_logo.png | `packages/jest-reporters/assets/jest_logo.png` | アセット | 通知アイコン |
| package.json | `packages/jest-reporters/package.json` | 設定 | node-notifierの依存定義 |
