# バッチ設計書 26-cypress/run.mjs

## 概要

本ドキュメントは、Decap CMSプロジェクトにおけるCI環境でのCypress E2Eテスト並列実行ランナースクリプト `cypress/run.mjs` の設計を記述する。

### 本バッチの処理概要

本スクリプトは、CI環境でCypress E2Eテストをマシン分割で並列実行するためのランナースクリプトである。テストスペックファイルを環境変数に基づいてシャーディング（分散）し、複数のCIマシンで効率的にE2Eテストを実行する。

**業務上の目的・背景**：Decap CMSのE2Eテストスイートは多数のスペックファイルで構成されており、全テストを単一マシンで順次実行すると長時間を要する。CI環境（GitHub Actions等）で複数マシンに分割して並列実行することで、テスト実行時間を大幅に短縮し、開発者へのフィードバックループを高速化する。

**バッチの実行タイミング**：CI環境でのE2Eテスト実行時。`npm run test:e2e:exec-ci` から呼び出される。具体的には、mainブランチへのpushまたはPR作成時のGitHub Actionsワークフローで実行される。

**主要な処理内容**：
1. `cypress/e2e/*spec*.js` パターンに一致するテストスペックファイルの一覧をglobbyで取得
2. 環境変数 `MACHINE_INDEX` と `MACHINE_COUNT` の有無を確認
3. シャーディングモード（環境変数あり）：スペックを均等分割し、該当マシンの担当分のみを実行
4. 並列記録モード（環境変数なし）：Cypress Dashboardの `--record --parallel` オプションで実行
5. execaを使用してCypressをChromeブラウザ・ヘッドレスモードで実行

**前後の処理との関連**：本スクリプトは `npm run test:e2e:ci` の一部として実行される。前段として `npm run build:demo` によるデモサイトビルドが行われ、`start-server-and-test` でHTTPサーバーが起動された後に実行される。テスト対象のデモサイトがポート8080で稼働していることが前提。

**影響範囲**：CIパイプラインのE2Eテスト結果。テストの成否がPRマージ可否に直接影響する。

## バッチ種別

テストユーティリティ（E2Eテスト並列実行ランナー）

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | CI/CDパイプライン実行時（push/PR作成時） |
| 実行時刻 | N/A（イベント駆動） |
| 実行曜日 | N/A |
| 実行日 | N/A |
| トリガー | `npm run test:e2e:exec-ci` からの呼び出し（Node.jsスクリプト実行） |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| Node.js実行環境 | Node.jsが利用可能であること |
| Cypressインストール | Cypressバイナリがインストール済みであること |
| Chromeブラウザ | Chrome（またはChromium）がインストール済みであること |
| テスト対象サーバー | デモサイトがHTTPサーバー上で起動していること |
| テストスペックファイル | `cypress/e2e/*spec*.js` に一致するファイルが1つ以上存在すること |

### 実行可否判定

テストスペックファイルが0件の場合、エラーメッセージを出力して `process.exit(1)` で終了する。

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| MACHINE_INDEX | 数値（環境変数） | No | 0 | 現在のマシンのインデックス（1始まり） |
| MACHINE_COUNT | 数値（環境変数） | No | 0 | 並列実行マシンの総数 |
| CI_BUILD_ID | 文字列（環境変数） | No | GITHUB_RUN_ID or GITHUB_SHA | Cypress並列実行のビルドID |
| GITHUB_RUN_ID | 文字列（環境変数） | No | なし | GitHub ActionsのランID |
| GITHUB_SHA | 文字列（環境変数） | No | なし | GitHubコミットSHA |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| cypress/e2e/*spec*.js | JavaScriptファイル | Cypress E2Eテストスペックファイル |
| 環境変数 | 文字列/数値 | シャーディング制御パラメータ |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| 標準出力 | テキスト | Cypressテスト実行結果 |
| Cypress Dashboard | API | `--record` オプション使用時のテスト結果記録 |
| プロセス終了コード | 数値 | テスト成功時0、失敗時非0 |

### 出力ファイル仕様

本スクリプト自体はファイル出力を行わない。Cypressがスクリーンショットやビデオを出力する場合がある。

| 項目 | 内容 |
|-----|------|
| ファイル名 | Cypress設定に依存 |
| 出力先 | Cypress設定に依存 |
| 文字コード | N/A |
| 区切り文字 | N/A |

## 処理フロー

### 処理シーケンス

```
1. Cypress実行引数の初期化
   └─ ['run', '--browser', 'chrome', '--headless'] を基本引数として設定
2. テストスペックファイルの検索
   └─ globby で 'cypress/e2e/*spec*.js' パターンに一致するファイルを取得
3. スペックファイルの存在確認
   └─ 0件の場合、エラーメッセージを出力して process.exit(1)
4. 実行モードの判定
   ├─ MACHINE_INDEX && MACHINE_COUNT が設定されている場合 → シャーディングモード
   └─ それ以外 → 並列記録モード
5a. シャーディングモード
   ├─ specsPerMachine = Math.floor(specs.length / machineCount) で1マシンあたりのスペック数を計算
   ├─ start = (machineIndex - 1) * specsPerMachine で開始インデックスを計算
   ├─ 最後のマシンは残りの全スペックを担当
   └─ --spec オプションで担当スペックをカンマ区切りで指定
5b. 並列記録モード
   ├─ ciBuildIdを CI_BUILD_ID || GITHUB_RUN_ID || GITHUB_SHA から取得
   └─ --record --parallel --ci-build-id --group 'GitHub CI' --spec オプションを追加
6. Cypress実行
   └─ execa('cypress', args) で実行（stdio: 'inherit', timeout: 1時間）
```

### フローチャート

```mermaid
flowchart TD
    A[スクリプト開始] --> B["globbyでスペックファイル検索"]
    B --> C{スペックファイル存在?}
    C -->|0件| D["エラー出力・exit(1)"]
    C -->|1件以上| E{MACHINE_INDEX && MACHINE_COUNT?}
    E -->|あり| F[シャーディングモード]
    F --> G[担当スペック範囲を計算]
    G --> H["--spec で担当分を指定"]
    E -->|なし| I[並列記録モード]
    I --> J[ciBuildId取得]
    J --> K["--record --parallel オプション追加"]
    H --> L["execa('cypress', args) 実行"]
    K --> L
    L --> M[テスト結果出力]
```

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

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

本スクリプトはデータベース操作を行わない。

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| N/A | N/A | N/A | データベース操作なし |

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

N/A

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| exit 1 | テストファイル不在 | `cypress/e2e/*spec*.js` に一致するファイルが存在しない | テストスペックファイルの配置を確認する |
| 非0終了 | テスト失敗 | Cypressテストケースが失敗 | テスト失敗の内容を確認して修正する |
| タイムアウト | 実行タイムアウト | Cypress実行が1時間を超過 | テストの最適化またはタイムアウト値の調整 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | N/A（スクリプトレベルでのリトライなし。CIワークフローでのリトライは別途設定） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

### 障害時対応

テスト失敗時は、Cypressのスクリーンショット・ビデオ（設定されている場合）およびCIログを確認する。タイムアウトの場合は、テストスペックの実行時間を確認し、シャーディング数の増加を検討する。

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | N/A（テスト実行のため該当なし） |
| コミットタイミング | N/A |
| ロールバック条件 | N/A |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | テストスペックファイル数（全体を均等分割） |
| 目標処理時間 | 1時間以内（execaのtimeout設定） |
| メモリ使用量上限 | CI環境のマシンスペックに依存 |

## 排他制御

排他制御は不要。各CIマシンは独立したスペックセットを担当するため、マシン間でのテスト競合は発生しない。ただし、テスト対象のデモサイトサーバーは各マシンのローカルで個別に起動される。

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| シャーディング情報 | シャーディングモード時 | `Sharding specs manually: machine {index}/{count} running {n} specs` |
| 実行引数 | Cypress実行直前 | `Running Cypress with args: {args}` |
| テスト結果 | Cypress実行完了後 | Cypressの標準出力がそのまま継承される（stdio: 'inherit'） |
| エラーログ | スペック不在時 | `No test files found in cypress/e2e/*spec*.js` |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| テスト成否 | 失敗時 | GitHub Actions CI結果（PR Checks） |
| 実行時間 | 1時間超過（タイムアウト） | GitHub Actions CI結果 |

## 備考

- execa v5（CommonJS互換）を使用してCypressプロセスを起動している
- `preferLocal: true` により、ローカルにインストールされたCypressバイナリが優先使用される
- シャーディングモードでは最後のマシン（machineIndex === machineCount）が残りの全スペックを担当するため、スペック数がマシン数で割り切れない場合に最後のマシンの負荷が若干大きくなる
- 並列記録モードではCypress DashboardのRecord機能を使用するため、Cypress Dashboard Serviceへのアクセスとrecord keyの設定が必要
- タイムアウトは60分（60 * 60 * 1000ミリ秒）に設定されている
- ciBuildIdのフォールバック順は CI_BUILD_ID > GITHUB_RUN_ID > GITHUB_SHA
