ui-test について
ui-testは、実ブラウザーでUI変更を敵対的にテストするエージェントスキルです。差分駆動、探索型、並列のテストに対応し、サブエージェントが割り当てられた確認を実行して根拠付きの結果を報告します。
主な機能
- 01差分駆動のUIテスト
- 02探索型ブラウザーテスト
- 03サブエージェントによる並列テスト
- 04構造化アサーションと失敗時の画像
- 05機能・境界条件・アクセシビリティ確認
活用シーン
- 01git diffによるUI変更の検証
- 02フォームやフローの境界条件の調査
- 03複数ブラウザーで独立したテストを実施
- 04根拠付きの再現可能な不具合報告
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ browserbase/ skills --skill ui-testインストール後、Claude Code で/ui-testを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
EXAMPLES.mdLICENSE.txtREADME.mdreferences/adversarial-patterns.mdbrowser-recipes.mddesign-consistency.mddesign-system.example.mdexploratory-testing.mdparallel-testing.mdreport-template.htmlux-heuristics.md詳細
UI テスト — エージェント型 UI テストスキル
実際のブラウザーで UI の変更をテストします。目的は、動作を確認することではなく、問題を見つけることです。
3 つのワークフロー:
- 差分駆動 — git diff を分析し、変更された箇所だけをテストします
- 探索的 — アプリを操作し、開発者が想定していなかったバグを見つけます
- 並列 — 独立したテストグループを複数の Browserbase ブラウザーに振り分けます
テストの進め方
メインエージェントは調整役です。テスト戦略を計画し、サブエージェントに作業を委任し、結果をまとめます。実際のブラウザーテストはサブエージェントが行います。
計画:複数の視点から検討し、一度だけ実行
サブエージェントを起動する前に、3 つすべての計画ラウンドを自分で完了し、出力しなければなりません。 計画は自身の応答内で行います。サブエージェントに委任してはいけません。実行に先走らないでください。
ラウンド 1 — 機能面: 主要なユーザーフローは何ですか? 何が正常に動作すべきですか? 各テストを「操作 → 期待される結果」の形式で書き出します。
ラウンド 2 — 敵対的な観点: ラウンド 1 を読み直します。見落としている点はありませんか? 次の点を考慮します。ユーザーの種類や役割の違い、エラー経路、空の状態、競合状態、境界入力(空、大量、特殊文字、連続クリック)。
ラウンド 3 — カバレッジの不足: ラウンド 1 と 2 を読み直します。次の点はどうでしょうか。アクセシビリティ(axe-core、キーボードのみでの操作)、モバイルのビューポート、コンソールエラー、アプリの他の部分との見た目の一貫性。
重複を取り除く: 3 つのラウンドを統合し、テストを番号付きリストにまとめます。重複を削除します。各テストをグループに割り当てます(例:グループ A、グループ B)。
その後、一度だけ実行します — 各グループに対してサブエージェントを 1 つ起動します。各サブエージェントには、実行する特定のテストリストだけを渡します。それ以外は渡しません。サブエージェントは探索や計画を行わず、割り当てられたテストを実行して結果を報告します。
Agent ツールを呼び出す前に、3 つのラウンド、統合した計画、グループの割り当てを応答内に出力します。
作業を分割する際の原則
- サブエージェントは割り当てられたテストを実行し、自由に探索してはいけません。 メインエージェントは、各サブエージェントに具体的な番号付きのテストリストを渡します。サブエージェントは計画、探索、テスト対象の決定を行わず、リストのテストを実行して終了します。
- ボトルネックになるのは最も遅いエージェントです — 1 つのエージェントに作業が偏らないように分割します。少数の大きなエージェントより、多数の小さなエージェントを優先します。
- 変更内容に合わせて作業量を調整します — 単一コンポーネントの修正に、多数のエージェントや多くの手順は不要です。ページ全体の再設計では必要になる場合があります。差分の範囲に応じて計画を立てます。
- 失敗しても途中で打ち切らないでください — 割り当てられたテストの範囲内で、できるだけ多くのバグを見つけます。
サブエージェントにステップ数の上限を指定する
メインエージェントは、すべてのサブエージェント向けプロンプトに、明示的なブラウズのステップ数上限を含めなければなりません。 サブエージェントは自分で上限を設定しません。指示がなければ、完了するまで実行し続けます。
大まかな目安:対象を絞った少数のチェックでは約 25 ステップ、機能面・敵対的な観点・アクセシビリティを含むページ全体のテストでは約 40 ステップ、複数のページや広範なカテゴリでは約 75 ステップです。割り当てられたテストで実際に必要な作業に応じて調整してください — これらは出発点であり、規則ではありません。
大まかな目安:対象を絞った少数のチェックでは約 25 ステップ、機能面・敵対的な観点・アクセシビリティを含むページ全体のテストでは約 40 ステップ、複数のページや広範なカテゴリでは約 75 ステップです。割り当てられたテストで実際に必要な作業に応じて調整してください — これらは出発点であり、規則ではありません。
すべてのサブエージェント向けプロンプトには、次を含める必要があります。
You have a budget of N browse steps (each `browse` command = 1 step). Count your steps as you go. When you reach N, stop immediately and report:
- STEP_PASS/STEP_FAIL for every test you completed
- STEP_SKIP|<test-id>|budget reached for every test you didn't get to
Do not retry or continue after hitting the budget.
Run only these tests: [numbered list from the merged plan]
Do not explore beyond the assigned tests.
Do NOT generate an HTML report or write any files. Return only step markers and your findings as text.
メインエージェントは、自身で browse コマンドを実行してはいけません(開発サーバーが起動していることを確認する場合を除きます)。テストはすべてサブエージェントが行います。
サブエージェントがステップ数の上限に達した場合、メインエージェントは途中までの結果をそのまま受け入れます。 サブエージェントを再実行したり、やり直したりしてはいけません。開発者に未実施のテストがわかるよう、最終レポートには SKIPPED のテストを含めます。
レポート
各サブエージェントは次の内容を報告します。
Tests: 8 | Passed: 5 | Failed: 2 | Skipped: 1 | Pages visited: 2
メインエージェントは結果をまとめ、次の最終レポートを作成します。
Tests: 20 | Passed: 14 | Failed: 4 | Skipped: 2 | Agents: 3 | Pass rate: 70%
「使用したステップ数」は報告しないでください。browse コマンドの実行回数は実装上の内部情報であり、レビュアーにとって有用な指標ではありません。
テストの基本方針
敵対的なテスターとして行動してください。 正しさを証明するのではなく、バグを見つけることが目的です。
- テストするすべての機能を壊そうとしてください。 「ボタンが存在するか」を確認するだけではなく、素早く2回クリックする、空のフォームを送信する、500文字を貼り付ける、操作の途中で Escape キーを押すなどを試します。
- 開発者が想定していないことをテストしてください。 空の状態、エラーからの復旧、キーボードだけでの操作、モバイルでの表示崩れなどです。
- すべてのアサーションは証拠に基づく必要があります。 操作前後のスナップショットを比較します。ref で特定の要素を確認します。アクセシビリティツリーまたは決定論的なチェックによる具体的な証拠なしに、PASS と報告してはいけません。
- 失敗は再現に十分な詳細を添えて報告してください。 正確な操作、期待した結果、実際の結果、修正案を含めます。
アサーションプロトコル
各テストステップでは、必ず構造化されたアサーションを出力してください。「問題なさそうです」といった自由形式の記述は避けます。
ステップマーカー
各テストステップにつき、マーカーを1つだけ出力してください。
STEP_PASS|<step-id>|<evidence>
または
STEP_FAIL|<step-id>|<expected> → <actual>|<screenshot-path>
step-id:homepage-cta、form-validation-error、modal-cancelなどの短い識別子evidence:ステップの成功を証明する観察結果(要素 ref、テキスト内容、URL、eval の結果など)expected → actual:期待した結果と実際の結果screenshot-path:保存したスクリーンショットのパス(失敗時のみ。「失敗時のスクリーンショット撮影」を参照)
失敗時のスクリーンショット撮影
STEP_FAIL にはすべて、開発者が視覚的に問題を確認できるよう、対応するスクリーンショットを添付する必要があります。
テストステップが失敗した場合:
# 1. Take a screenshot immediately after observing the failure
browse screenshot --path .context/ui-test-screenshots/<step-id>.png
# If --path is not supported, take the screenshot and save manually:
browse screenshot
# The browse CLI will output the screenshot path — move/copy it:
cp /tmp/browse-screenshot-*.png .context/ui-test-screenshots/<step-id>.png
テスト実行の開始時に、スクリーンショット用ディレクトリを作成します。
mkdir -p .context/ui-test-screenshots
ルール:
- ファイル名は step-id にします(例:
double-submit.png、axe-audit.png、modal-focus-trap.png) .context/ui-test-screenshots/に保存します。このディレクトリは gitignore に登録されており、開発者や他のエージェントがアクセスできます。- 並列実行では、セッション名を含めます:
<session>-<step-id>.png(例:signup-double-submit.png) - 失敗した時点でスクリーンショットを撮影します。復旧後ではなく、壊れた状態をキャプチャします。
- 視覚やレイアウトに関するバグでは、比較用にベースライン(正常に動作する状態)のスクリーンショットも撮影します:
<step-id>-baseline.png
検証方法(厳密な順)
- 決定論的なチェック(最も強力)—
browse evalは、確認可能な構造化データを返します。例:axe-core の違反数、document.title、フォームフィールドの値、コンソールエラーの配列、要素数。 - スナップショット内の要素照合 — 特定のロールとテキストを持つ要素が、アクセシビリティツリーに存在することを確認します。ref で確認します:
@0-12 button "Save"。要素がツリーに存在するかどうかは明確に判断できます。 - 操作前後の比較 — 操作前にスナップショットを取得し、操作を実行してから、操作後のスナップショットを取得します。ツリーが期待どおりに変化したこと(要素の出現・消失、テキストの変更など)を確認します。
- スクリーンショットと視覚的な判断(最も弱い)— アクセシビリティツリーでは確認できない、色、間隔、レイアウトなどの視覚的な要素に限って使用します。必ず、何を評価しているのかを具体的に記述してください。
操作前後の比較パターン
これは検証の基本ループです。すべてのインタラクションで使用します。
# 1. BEFORE: capture state
browse snapshot
# Record: what elements exist, their text, their refs
# 2. ACT: perform the interaction
browse click @0-12
# 3. AFTER: capture new state
browse snapshot
# Compare: what changed? What appeared? What disappeared?
# 4. ASSERT: emit marker based on comparison
# If dialog appeared: STEP_PASS|modal-open|dialog "Confirm" appeared at @0-20
# If nothing changed:
browse screenshot --path .context/ui-test-screenshots/modal-open.png
# STEP_FAIL|modal-open|expected dialog to appear → snapshot unchanged|.context/ui-test-screenshots/modal-open.png
セットアップ
which browse || npm install -g browse
権限確認の繰り返しを避ける
このスキルでは、多数の browse コマンド(スナップショット、クリック、eval)を実行します。毎回の承認を避けるには、browse を許可コマンドに追加します。
.claude/settings.json(プロジェクトレベル)または ~/.claude/settings.json(ユーザーレベル)に、次の両方のパターンを追加します:
{
"permissions": {
"allow": [
"Bash(browse:*)",
"Bash(BROWSE_SESSION=*)"
]
}
}
1つ目のパターンは通常の browse コマンドに対応します。2つ目は並列セッション(BROWSE_SESSION=signup browse open ...)に対応します。承認プロンプトを避けるには、両方が必要です。
モードの選択
| 対象 | モード | コマンド | 認証 |
|---|---|---|---|
localhost / 127.0.0.1 | ローカル | browse open <url> --local | 不要(デフォルトでクリーンな隔離ローカルブラウザーを使用) |
| デプロイ済み/ステージングサイト | リモート | browse open <url> --remote | Browserbase の認証情報。対応している場合はコンテキストを使用 |
ルール:対象 URL に localhost または 127.0.0.1 が含まれる場合、最初の browse open に --local を指定します。
ローカルモード(localhost ではデフォルト)
browse open http://localhost:3000 --local
browse open ... --local は、デフォルトでクリーンな分離型ローカルブラウザーを使用します。再現性の高い localhost の QA 実行に最適です。
必要な場合に限り、ローカルモードのバリエーションを使用してください。
browse open <url> --auto-connect— デバッグ可能な既存のローカル Chrome を自動検出します。既存のローカルログイン/Cookie/状態がテストで明示的に必要な場合に限り使用してください。browse open <url> --cdp <port|url>— 特定の CDP ターゲットに接続します(ローカルブラウザーへの明示的な接続)。
リモートモード(Cookie 同期によるデプロイ済みサイト)
# Step 1: Sync cookies from local Chrome to Browserbase
node .claude/skills/cookie-sync/scripts/cookie-sync.mjs --domains your-app.com
# Output: Context ID: ctx_abc123
# Step 2: Open in remote mode with the synced context
SESSION_JSON="$(browse cloud sessions create --context-id ctx_abc123 --persist --keep-alive)"
SESSION_ID="$(echo "$SESSION_JSON" | jq -r .id)"
CONNECT_URL="$(echo "$SESSION_JSON" | jq -r .connectUrl)"
browse open https://staging.your-app.com --cdp "$CONNECT_URL"
browse snapshot
# ... run tests ...
browse stop
browse cloud sessions update "$SESSION_ID" --status REQUEST_RELEASE
Cookie 同期フラグ:--domains、--context、--verified、--proxy "City,ST,US"
ワークフロー A:差分駆動テスト
フェーズ 1:差分を分析する
git diff --name-only HEAD~1 # or: git diff --name-only / git diff --name-only main...HEAD
git diff HEAD~1 -- <file> # read actual changes
変更されたファイルを分類します。
| ファイルパターン | UI への影響 | テスト内容 |
|---|---|---|
*.tsx、*.jsx、*.vue、*.svelte | コンポーネント | レンダリング、操作、状態、エッジケース |
pages/**、app/**、src/routes/** | ルート/ページ | ナビゲーション、ページ読み込み、コンテンツ、404 の処理 |
*.css、*.scss、*.module.css | スタイル | 見た目(スクリーンショット)、レスポンシブ対応 |
*form*、*input*、*field* | フォーム | 検証、送信、空の入力、長い入力、特殊文字 |
*modal*、*dialog*、*dropdown* | インタラクティブ要素 | 開く/閉じる、Escape キー、フォーカストラップ、キャンセルと確定 |
*nav*、*menu*、*header* | ナビゲーション | リンク、アクティブ状態、ルーティング、キーボード操作 |
| UI 以外のファイルのみ | なし | スキップ —「UI テストは不要」と報告 |
フェーズ 2:ファイルを URL に対応付ける
フレームワークを検出します:cat package.json | grep -E '"(next|react|vue|nuxt|svelte|@sveltejs|angular|vite)"'
| フレームワーク | デフォルトポート | ファイル → URL のパターン |
|---|---|---|
| Next.js App Router | 3000 | app/dashboard/page.tsx → /dashboard |
| Next.js Pages Router | 3000 | pages/about.tsx → /about |
| Vite | 5173 | ルーター設定を確認 |
| Nuxt | 3000 | pages/index.vue → / |
| SvelteKit | 5173 | src/routes/+page.svelte → / |
| Angular | 4200 | ルーティングモジュールを確認 |
フェーズ 3:正しいコードが実行されていることを確認する
テストの前に、開発サーバーが古いブランチではなく、差分に含まれるコードを配信していることを確認します。
PR または特定のブランチをテストする場合:
# Check what branch is currently checked out
git branch --show-current
# If it's not the PR branch, switch to it
git fetch origin <branch> && git checkout <branch>
# Install deps — the lockfile may differ between branches
yarn install # or npm install / pnpm install
開発サーバーが別のブランチで既に実行されていた場合は、チェックアウト後に再起動します。
実行中の開発サーバーを探す:
for port in 3000 3001 5173 4200 8080 8000 5000; do
s=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:$port" 2>/dev/null)
if [ "$s" != "000" ]; then echo "Dev server on port $port (HTTP $s)"; fi
done
見つからない場合:開発サーバーを起動するようユーザーに伝えます。
実際にレンダリングされていることを確認する:
browse open と browse snapshot の実行後、アクセシビリティツリーに実際のページコンテンツ(ナビゲーション、見出し、インタラクティブ要素)が含まれていることを確認します。エラーオーバーレイや空の body だけになっていないことも確認してください。Next.js の開発サーバーは、全画面のビルドエラーダイアログを表示しながら HTTP 200 を返す場合があります。スナップショットが空、またはエラーダイアログが大半を占める場合、サーバーは正常に動作していません。テストの前にビルドを修正してください。
フェーズ 4:テスト計画を作成する
変更された領域ごとに、通常のケースと敵対的なテストの両方を計画します。
Test Plan (based on git diff)
=============================
Changed: src/components/SignupForm.tsx (added email validation)
1. [happy] Valid email submits successfully
URL: http://localhost:3000/signup
Steps: fill valid email → submit → verify success message appears
2. [adversarial] Invalid email shows error
Steps: fill "not-an-email" → submit → verify error message appears
3. [adversarial] Empty form submission
Steps: click submit without filling anything → verify error, no crash
4. [adversarial] XSS in email field
Steps: fill "<script>alert(1)</script>" → submit → verify sanitized/rejected
5. [adversarial] Rapid double-submit
Steps: click submit twice quickly → verify no duplicate submission
6. [adversarial] Keyboard-only flow
Steps: Tab to email → type → Tab to submit → Enter → verify success
フェーズ 5:テストを実行する
browse stop 2>/dev/null
mkdir -p .context/ui-test-screenshots
# localhost/default QA → clean, reproducible local run
browse open http://localhost:3000 --local
各テストでは、実行前/実行後のパターンに従います。
# Navigate
browse open http://localhost:3000/path --local
browse wait load
# BEFORE snapshot
browse snapshot
# Note the current state: elements, refs, text
# ACT
browse click @0-ref
# or: browse fill "selector" "value"
# or: browse type "text"
# or: browse press Enter
# AFTER snapshot
browse snapshot
# Compare against BEFORE: what changed?
# ASSERT with marker
# STEP_PASS|step-id|evidence OR STEP_FAIL|step-id|expected → actual
フェーズ 6:結果を報告する
## UI Test Results
### STEP_PASS|valid-email-submit|status "Thanks!" appeared at @0-42 after submit
- URL: http://localhost:3000/signup
- Before: form with email input @0-3, submit button @0-7
- Action: filled "[email protected]", clicked @0-7
- After: form replaced by status element with "Thanks! We'll be in touch."
### STEP_FAIL|double-submit|expected single submission → form submitted twice|.context/ui-test-screenshots/double-submit.png
- URL: http://localhost:3000/signup
- Before: form with submit button @0-7
- Action: clicked @0-7 twice rapidly
- After: two success toasts appeared, suggesting duplicate submission
- Screenshot: .context/ui-test-screenshots/double-submit.png
- Suggestion: disable submit button after first click, or debounce the handler
---
**Summary: 4/6 passed, 2 failed**
Failed: double-submit, xss-sanitization
Screenshots saved to `.context/ui-test-screenshots/` — open any failed step's screenshot to see the broken state.
完了したら、必ず browse stop を実行してください。
フェーズ 7:HTML レポートを生成する
テキストレポートの作成後、レビュー担当者がブラウザーで開けるスタンドアロンの HTML レポートを生成します。レポートにはスクリーンショットがインライン(base64)で埋め込まれるため、外部依存なしで単一ファイルとして動作します。
理由: テキストレポートはエージェントとの会話には適していますが、レビュー担当者(PM、デザイナー、他のエンジニア)は、開いて確認し、共有できる視覚的な成果物を必要とします。スクリーンショットをインラインで含めると、失敗がすぐにわかります。
生成方法
- references/report-template.html の HTML テンプレートを読み込みます
- テンプレートのプレースホルダーを実際のテストデータに置き換えて、レポートを作成します。
| プレースホルダー | 値 |
|---|---|
{{TITLE}} | <title> タグ用のレポートタイトル(例:「UI Test: PR #1234 — OAuth Settings」) |
{{TITLE_HTML}} | 表示される <h1> 用のレポートタイトル。PR の URL がある場合は、PR への参照をクリック可能なように <a> タグで囲みます(例:UI Test: <a href="https://github.com/org/repo/pull/1234">PR #1234</a> — OAuth Settings)。URL がない場合は、{{TITLE}} と同じプレーンテキストを使用します。 |
{{META}} | 日付、アプリの URL、ユーザー、ブランチを含む1行のコンテキスト |
{{TOTAL_TESTS}} | STEP_PASS と STEP_FAIL の合計数 |
{{AGENT_COUNT}} | 実行されたサブエージェントの数 |
{{PASS_COUNT}} | STEP_PASS の数 |
{{FAIL_COUNT}} | STEP_FAIL の数 |
{{PASS_RATE}} | 整数の割合(例:「92」) |
{{RATE_CLASS}} | good(90%以上)、warn(70~89%)、bad(70%未満) |
{{FAILURES_SECTION}} | 失敗したテストカードの HTML(下記参照) |
{{PASSES_SECTION}} | 成功したテストカードの HTML(下記参照) |
- 各テスト結果について、
<details>カードを生成します。レビュアーがすぐに確認できるよう、失敗したテストはデフォルトで開いた状態にします。
<div class="section">
<h2>Failures <span class="count">{{FAIL_COUNT}}</span></h2>
<details class="test-card fail" open>
<summary>
<span class="badge fail">FAIL</span>
<span class="step-id">step-id-here</span>
<span class="evidence">expected → actual</span>
</summary>
<div class="body">
<dl>
<dt>URL</dt><dd>http://localhost:3000/path</dd>
<dt>Action</dt><dd>What was done</dd>
<dt>Expected</dt><dd>What should have happened</dd>
<dt>Actual</dt><dd>What happened instead</dd>
</dl>
<div class="suggestion">Fix: description of suggested fix</div>
<div class="screenshot">
<img src="data:image/png;base64,..." alt="Screenshot of failure">
<div class="caption">step-id.png — captured at moment of failure</div>
</div>
</div>
</details>
</div>
<div class="section">
<h2>Passed <span class="count">{{PASS_COUNT}}</span></h2>
<details class="test-card pass">
<summary>
<span class="badge pass">PASS</span>
<span class="step-id">step-id-here</span>
<span class="evidence">evidence summary</span>
</summary>
<div class="body">
<dl>
<dt>URL</dt><dd>http://localhost:3000/path</dd>
<dt>Evidence</dt><dd>What was observed</dd>
</dl>
</div>
</details>
</div>
- スクリーンショットを base64 として埋め込み、HTML を完全に自己完結型にします。
# Convert screenshot to base64 data URI
base64 -i .context/ui-test-screenshots/step-id.png | tr -d '\n'
# Use as: src="data:image/png;base64,<output>"
STEP_FAIL マーカーで参照されている各スクリーンショットファイルを読み込み、base64 にエンコードして、対応するテストカードに <img src="data:image/png;base64,..."> として埋め込みます。STEP_PASS では、明示的に撮影されたスクリーンショット(例:ベースラインのスクリーンショット)がある場合のみ埋め込みます。
- 最終的な HTML を
.context/ui-test-report.htmlに書き込みます。
# Write the generated HTML
cat > .context/ui-test-report.html << 'REPORT_EOF'
<!DOCTYPE html>
...generated report...
REPORT_EOF
# Open it for the reviewer
open .context/ui-test-report.html # macOS
# xdg-open .context/ui-test-report.html # Linux
- ユーザーに
Report saved to .context/ui-test-report.htmlと伝え、ファイルを開くかどうか尋ねます。
ルール:
- 失敗のセクションを成功のセクションより先に配置します。レビュアーがまず確認したいのは問題点です。
- 失敗したカードはデフォルトで
openにし、成功したカードは折りたたみます。 - すべての STEP_FAIL カードには、スクリーンショットを必ず埋め込みます。スクリーンショットファイルが見つからない場合は、カードにその旨を記載します。
- 提案や修正方法が提示されている場合は、各失敗カードに含めます。
- レポートはオフラインで動作する必要があります。CDN リンクや外部アセットは使用しません。
- HTML は 5MB 未満にします。スクリーンショットを埋め込むと上限を超える場合は、画像の品質を下げるか、成功したテストのベースラインスクリーンショットを省略します。
敵対的テストパターン
テストするすべてのインタラクティブ要素に適用します。パターンの全一覧については、references/adversarial-patterns.md を参照してください(フォーム、モーダル、ナビゲーション、エラー状態、キーボードアクセシビリティ)。
決定論的チェック
これらは判断ではなく、構造化データを生成します。アサーションとして最も確実な方法です。
| チェック | 検出対象 | アサーション |
|---|---|---|
| axe-core | WCAG 違反 | violations.length === 0 |
| コンソールエラー | 実行時例外、失敗したリクエスト | エラー配列が空 |
| 画像の読み込みエラー | 画像の欠落や読み込み失敗 | naturalWidth === 0 の画像がない |
| フォームラベル | アクセシブルなラベルのない入力 | すべての入力で hasLabel: true |
browse eval の正確なレシピについては、references/browser-recipes.md を参照してください。
ワークフロー B:探索的テスト
差分も計画も必要ありません。アプリを開いて、問題が起きるか試します。ユーザーが「自分のアプリをテストして」「バグを見つけて」「このサイトを QA して」と言った場合に使用します。
アプローチ
- アプリを特定する —
package.jsonを読んでフレームワークを検出し、ルート URL を開いてスナップショットを取得し、何があるか確認します - すべてを巡回する — ナビゲーションリンクをクリックし、アクセスできるすべてのページを訪問して、存在するものを記録します
- 見つけたものをテストする — 各ページで、以下の敵対的パターン(フォーム、モーダル、ナビゲーション、キーボード、エラー状態)を適用します
- 決定論的なチェックを実行する — すべてのページで axe-core、コンソールエラー、画像のリンク切れ、フォームラベルを確認します
- 調査結果を報告する — STEP_PASS/STEP_FAIL マーカーを使い、失敗の再現手順を含めます
網羅性を体系立てて追求しないでください。ユーザーのように自由に探索しながら、問題を見つけることを意識してください。エージェントはこれが得意なので、自由に探索させましょう。
探索的テストのヒント
- ホームページから始め、自然な流れでナビゲーションをたどります
- 404 ページ(
/does-not-exist)を試します — カスタムページですか、それともデフォルトですか? - 空の状態(データがないページ)を探します
- 有効な入力を試す前に、フォームにでたらめな入力を試します
- すべてのページでモバイルのビューポート(375px)を確認します — はみ出していませんか?
- アプリに認証がある場合は、まず cookie-sync を使います
ワークフロー C:並列テスト
名前付きの browse セッション(BROWSE_SESSION=<name>)を使って、独立したテストグループを並行して実行します。各セッションには個別のブラウザーが割り当てられます。ローカルモードとリモートモードの両方で動作します。
複数のページやカテゴリーをテストするとき、所要時間を短縮したい場合に使用します。
セッションのセットアップ、エージェントへの作業の振り分け、認証用の cookie-sync、結果の統合を含むワークフロー全体については、references/parallel-testing.md を読んでください。
デザインの一貫性
変更された UI がアプリの他の部分と視覚的に一致しているか確認します。視覚チェックやデザインチェックを行う場合は、references/design-consistency.md を読んでください。
テストカテゴリー
| カテゴリー | 方法 | アサーションの種類 |
|---|---|---|
| アクセシビリティ | axe-core +キーボードナビゲーション | 決定論的(違反数) |
| 視覚的品質 | スクリーンショット+ヒューリスティック評価 | 視覚的な判断(最も信頼性が低いため、具体的な内容を記録) |
| レスポンシブ | ビューポートの一括確認+スクリーンショット | 視覚的+決定論的(はみ出しチェック) |
| コンソールの健全性 | コンソールキャプチャの評価 | 決定論的(エラー数) |
| UX ヒューリスティック | スナップショット+UX の法則+Nielsen のヒューリスティック | 構造化された判断(具体的なヒューリスティックを明記) |
| エラー状態 | 空の状態/エラー状態に移動 | 前後の比較 |
| データ表示 | テーブル/ダッシュボードのスナップショット | 要素の一致(列数、書式) |
| デザインの一貫性 | スクリーンショットのベースライン+変更ページとの比較 | 視覚的な判断(具体的なプロパティを明記) |
| 探索的テスト | 自由なナビゲーション+敵対的テスト | 前後の比較+判断 |
参考ガイド(必要に応じて読み込みます):
- 敵対的パターン — references/adversarial-patterns.md — フォーム、モーダル、ナビゲーション、キーボードアクセシビリティをテストするときに読み込みます
- ブラウザのレシピ — references/browser-recipes.md — 決定論的なチェック(axe-core、コンソール、画像、フォームラベル)を実行するときに読み込みます
- 探索的テスト — references/exploratory-testing.md — ワークフロー B(差分なし、自由な探索)で読み込みます
- UX ヒューリスティック — references/ux-heuristics.md — UX の品質を評価したり、具体的なヒューリスティックを引用したりするときに読み込みます
- デザインシステム — references/design-system.example.md — ユーザーがカスタマイズするためのテンプレートです
- デザインの一貫性 — references/design-consistency.md — 視覚的な一貫性を確認するときに読み込みます
- 並列テスト — references/parallel-testing.md — ワークフロー C(並行セッション)で読み込みます
- レポートテンプレート — references/report-template.html — フェーズ 7 のレポート生成用 HTML テンプレートです
具体的なコマンドを使った実例については、アサーションプロトコルの実際の動作を確認する必要があれば EXAMPLES.md を読んでください。
ベストプラクティス
- 敵対的にテストする — 動作を確認するだけでなく、問題を起こそうとしてください
- すべてのアサーションに根拠を示す — スナップショットの参照、評価結果、または変更前後の差分
- すべての操作で変更前後を確認する — スナップショットを取得し、操作して、再度スナップショットを取得し、比較します
- 失敗するたびにスクリーンショットを撮る — STEP_FAIL の直後に
browse screenshotを実行し、.context/ui-test-screenshots/<step-id>.pngに保存します - 決定論的なチェックを先に行う — 見た目を判断する前に、axe-core、コンソールエラー、フォームのラベルを確認します
- ローカルホストではクリーンなローカルモードで開始する — 再現可能な実行のため、最初の
browse openで--localを渡します。既存のローカル状態が必要な場合にのみ--auto-connectを使用します - 完了したら必ず
browse stopを実行する — 並列実行では、名前付きのセッションをすべて停止します - 失敗は再現手順とともに報告する — 操作、期待される結果、実際の結果、スクリーンショットのパス、提案
- 独立したテストは並列化する — デプロイ済みサイトの複数のページまたはカテゴリをテストする場合は、名前付きセッションでワークフロー C を使用します
トラブルシューティング
- 「アクティブなページがありません」:
browse stopを実行して再試行します。ゾンビプロセスの場合:pkill -f "browse.*daemon" - 開発サーバーが応答しない:
curl http://localhost:<port>— ユーザーに起動を依頼します awaitを含むbrowse evalが失敗する:代わりに.then()を使用します —browse evalはトップレベルの await をサポートしていません- 要素の参照が見つからない:
browse snapshotを再度実行します — ページが更新されると参照が変わります - スナップショットが空白になる:スナップショットを取得する前に
browse wait loadまたはbrowse wait selector ".expected"を実行します - SPA のディープリンクで 404 が発生する:最初に
/に移動してから、クリックして進みます - リモート認証に失敗する:
--context <id>を指定して cookie-sync を再実行し、--verifiedを試します - 並列セッションが競合する:すべての
browseコマンドでBROWSE_SESSION=<name>が使われていることを確認します — これがない場合、コマンドはデフォルトのセッションに送られます - セッションが停止しない:
BROWSE_SESSION=<name> browse stopを実行します。ゾンビプロセスの場合:pkill -f "browse.*<name>.*daemon"