e2e
tester-army/e2e — agentic end-to-end testing for web and mobile apps
e2e は Web アプリとモバイルアプリ向けの E2E テストフレームワーク。テストの中で自然言語のゴールを agent.act() に渡すとエージェントがアプリを操作し、その結果をロケータとアサーションで厳密に確かめる。どこまでエージェントに任せるかはテストごとに選べる。ロケータだけの決定的なテスト、ゴールだけのテスト、両者を混ぜたテストのどれも書ける。
後続のチェックで検証された agent.act の操作は記録され、次回の実行ではモデルを呼ばずに再生される。アプリが変わって再生が合わなくなった時点でエージェントが引き継ぐ。エージェントステップを含まないテストはモデル不要。モデルは自前のサブスクリプション、API キー、ローカルモデルのいずれでもよい。
npx e2e telemetry disable または E2E_TELEMETRY_DISABLED=1 で止められる。
テストファイルは既定で tests/**/*.e2e.ts。フィクスチャとして app・agent・screen(Web では browser も)を受け取る。
// tests/checkout.e2e.ts
import { test, expect } from 'e2e';
test('a member upgrades to Pro', async ({ app, agent, screen }) => {
await app.open('/settings/billing');
await agent.act('upgrade the workspace to the Pro plan');
await agent.assert('the invoice preview shows a prorated amount');
await expect(screen.getByRole('status')).toContainText('Pro');
});エージェントに任せるステップ
agent.act(goal)— ゴールを 1 つ渡す。操作の順序はモデルが決め、達成すれば返り、失敗すれば例外を投げる。agent.assert/agent.waitFor/agent.extract— 画面の状態をモデルが判定する。こちらは毎回モデルを呼ぶ。- テストデータは
paramsで渡す(例:'invite {email} as an editor')。毎回変わる値はunique()で包むとキャッシュが効く。
厳密に書くステップ
screen.getByRole/getByText/getByTestIdなどのセマンティックロケータで操作・取得する。expect(...)はアサーションを再試行する。アクションは要素の準備完了を待つ。- 2 つの要素に一致するロケータは
LOCATOR_AMBIGUOUSで失敗するので絞り込む。sleepは入れない。
- 1 回の
actにゴールは 1 つ。ゴール内の手順はモデル、ゴールの順番はテスト作者が決める。 - 画面に表示されている文言をそのまま使う。複数のゴールで共有する語彙はエージェントの
contextに書く。 - 各ゴールの直後に
expectかagent.assertで結果を確かめる。検証済みのactだけがキャッシュされる。 - モデルが生成した文の完全一致ではなく意味で判定する(例: 文全体ではなく
toContain('Pro'))。
targets が必須。UI のターゲットはエンジンとテスト対象アプリを並べて宣言する。アプリの起動コマンドも設定に書けるので、ランナーが開発サーバーを立ち上げてからテストを流す。各テストはターゲットごとに 1 回ずつ実行される。
import type { E2EConfig } from 'e2e';
import { web } from '@e2e-dev/web';
import { gateway } from 'ai';
export default {
targets: [
{
engine: web(),
app: {
url: 'http://127.0.0.1:3000',
command: { executable: 'pnpm', args: ['dev'], log: '.e2e/logs/app.log' },
},
},
],
// agent.* ステップに使うモデル(AI SDK のインスタンス)
agents: {
default: {
model: gateway('openai/gpt-6-luna-fast'),
system: 'You are a thorough QA agent. Verify every outcome on screen.',
},
},
} satisfies E2EConfig;モバイルは mobile({ platform: 'ios' }) のようにエンジンを差し替え、app に bundleId を指定する。テストは Web と同じ agent / screen / expect API で書ける。セットアップは npx e2e init がエンジン(Web かモバイル)とモデルプロバイダを尋ね、設定とサンプルテストを生成する。
| パッケージ | 役割 |
|---|---|
e2e | SDK、ランナー、CLI。全ドキュメントを同梱しており、コーディングエージェントはオフラインで node_modules/e2e/docs を読める。 |
@e2e-dev/web | ブラウザエンジン。Playwright 経由で Chromium / Firefox / WebKit を動かす。 |
@e2e-dev/mobile | iOS / Android エンジン。agent-device 経由でシミュレータ、エミュレータ、接続済み実機を動かす。 |
@e2e-dev/github | GitHub Actions から、1 回の実行につき 1 件のプルリクエストコメントで結果を投稿するレポーター。 |
@e2e-dev/kernel | Kernel がホストする Chromium で Web ターゲットを動かす。 |
@e2e-dev/eas | Expo の EAS Simulators(ホスト型 iOS シミュレータ / Android エミュレータ)でモバイルターゲットを動かす。 |
@e2e-dev/decision | LLM の代わりに決定モデルで act / assert を実行する。操作と要素は選択肢から選ばせ、入力値だけ小さなテキストモデルが書く。 |
エンジンは自作もできる(公式ドキュメントの「Writing an engine」)。完成済みのセットアップ例は examples/ に Vite、Next.js、Expo、SwiftUI の 4 本がある。
エージェントステップは agent.act / assert / waitFor / extract の 1 呼び出し。ランナーがマスキング、アクションの許可、予算、シークレット、レポートを受け持ち、エグゼキュータ(既定は組み込みエージェント)がどのアクションを要求するか決めて判定を返す。
モデルに渡るもの
- 指示と params、
actでは同じテスト内の完了済みステップ - 画面のテキストスナップショット(ロール、名前、テキスト、状態)。生 HTML、Cookie、ヘッダー、環境変数は含まない
- 操作後は画面の差分。大半が変わったときは全体を再送
- スクリーンショットは必要時か
vision指定時のみ。シークレット入力後は送らない
モデルが使えるもの
- エンジンが対応するツール:
tap、type、press、select、scroll、navigateなど - 認証情報を宣言したステップでは
type_secret - テキストで表せないコントロール向けのピクセルツール(座標でタップ・入力)
defineToolで定義したプロジェクトツール
ループは同じ呼び出しの繰り返しを検出する。3 回続けてアクションが失敗するとアプローチの変更を求め、5 回で判定を求める。ターゲット指定のアクションのタイムアウトは最大 15 秒。
| 判定 | 意味 | 例 |
|---|---|---|
| passed | 成功の根拠が画面にある | — |
| failed | 条件が偽、または確認できない | 根拠不足は ASSERTION_INCONCLUSIVE |
| blocked | 前提・環境・実行上限のせいでプロダクトとしての判定が出せない | AUTH_CREDENTIAL_INVALID(終了コード 2)、ENVIRONMENT_UNAVAILABLE(3)、STEP_BUDGET_EXHAUSTED / STEP_TIMEOUT(1) |
コストを決める要因
assertとextractは 1 回の判定(応答が壊れていれば修復呼び出しが 1 回追加)。waitForは画面が変わらない間はモデルを呼ばない。- スクリーンショットは載せた呼び出しごとに入力トークンを増やす。
- 合格した
actはリプレイキャッシュでモデル呼び出しなしに再実行される。
後続のチェックで結果が検証された agent.act() の操作を記録し、次回はモデルを呼ばずに再生する。assert / waitFor / extract は常にライブで実行される。
| 実行サマリ | step.cache.mode | 意味 |
|---|---|---|
| replayed | self-finalized | 記録の再生だけで完了した |
| handed off | agent-concluded | 再生を始めたが途中でエージェントが引き継いだ |
| missed | missed | 最初からエージェントが実行した |
エントリはテスト、ターゲット、指示、params、エージェントの組に属する。次の変更はミスになる。
- テスト名・ターゲット名の変更、指示や通常の param の変更
- 別のエージェントでの実行、エージェントの
contextやテストのagentContextの変更 - エンジンのメジャー / マイナーバージョンの更新
モデルの変更ではミスにならない。タイムスタンプや毎回新しいメールアドレスは unique() で包むと、再生時に現在の値へ差し替えられる。キャッシュを疑うときは npx e2e run --no-cache で一度切り分ける。
e2e init は .e2e/cache/ を Git の無視対象にする。記録をコミットする場合は、テストコードと同じく中の操作と入力値をレビューする(エントリは署名されていない)。
テストファイルを書かずにゴールだけを渡すと、エージェントが自分でステップを計画してアプリを操作し、気づいた点(期待、実際、再現手順、可能ならスクリーンショット)を .e2e/report.json に記録する。agent.act() と同じモデル、ツール、アクション規則を使う。
npx e2e explore 'Explore the checkout flow like a first-time buyer and report anything off'| 予算 | 既定 | 範囲 |
|---|---|---|
探索ステップ(--max-steps) | 8 | 1〜12 |
実時間(--timeout) | 10 分 | 3〜15 分 |
| ステップあたりの操作 / モデル呼び出し | 各 40 | 1〜100 |
| 結果 | 状態 | 終了 |
|---|---|---|
issue の指摘なし | passed | 0 |
issue が 1 件以上 | failed | 1 |
| 全ステップが blocked | blocked | 1〜3 |
| 設定・エンジン・プロバイダのエラー | error | 2 / 3 |
issue は機能上の不具合で実行を失敗させ、warning は見た目の問題で失敗させない。失敗または blocked のステップが新しい指摘なしに 3 回続くと探索を終える。
コーディングエージェントがスキルの bug-bash トピックに沿って実行する。ルート(ブランチなら差分も)を読み、1 文のチャーターを 5〜10 本書き、初回ユーザー・数値と文言・境界入力などの観点をペルソナとして e2e explore を 4 本ずつ並列に走らせる。環境やシードデータで説明できる指摘を除いたあと、残りの指摘ごとに期待動作をアサートする再現テストを書く。そのテストが ASSERTION_FAILED で落ちたものだけをバグとして報告する。
リポジトリには skills/e2e/SKILL.md があり、トピック別の参照ファイルを持つ。インストール済みの CLI は同じ内容を npx e2e guide <topic> で表示する。
| トピック | 読む場面 |
|---|---|
setup | プロジェクトへの導入、e2e.config.ts の作成、アプリ起動設定、モバイルターゲット |
writing-tests | フィクスチャ、ロケータ、アクション、マッチャ、サインインセッション |
agent | agent.* ステップ、モデル選び、コストと予算、リプレイキャッシュ |
running | CLI フラグ、レポーター、.e2e/report.json、終了コード、CI |
explore / bug-bash | テストなしの探索、並列探索と再現テストによる検証 |
debugging | エラーコードと対処、--headed、--debug、--ai-trace |
mcp | MCP 経由でライブのアプリを操作し、調べてからテストを書く流れ |
プロジェクトのライブアプリを stdio の MCP サーバーとしてコーディングエージェントに公開する。テストを書く前に画面を見て、ロケータが一意に当たるかを確かめるための仕組み。テストの実行と失敗の読解は CLI 側で行う。ai パッケージがなくても決定的なツールは動く。
claude mcp add e2e -- npx e2e mcp # Claude Code| ツール | 動作 |
|---|---|
open_session | 設定を読み、アプリの起動コマンドを実行し、エンジンを起動して URL を開く。セッション ID、カタログ、最初の観測結果を返す |
tools | セッションで使えるカタログ(observe、tap、type、locate、screenshot など)を一覧する |
call | カタログのツールを 1 つ実行する。引数はスキーマで検査され、誤りは INVALID_ARGUMENT |
close_session | 録画を保存し、エンジンを破棄し、他のセッションが使っていなければアプリのプロセスを止める |
カタログの locate は role / text / label / placeholder / testId のいずれか 1 つでロケータを試し、一致数と、テストに書くべき screen.* 呼び出しを返す。同時セッション数は既定 4(--max-sessions で 1〜16)。
npx e2e init [directory] # エンジンとモデルを選んで設定とサンプルを生成
npx e2e login [provider] # モデルプロバイダのサブスクリプションにサインイン
npx e2e models [provider]
npx e2e guide [topic] # スキルのトピックを表示
npx e2e mcp [options]
npx e2e run [files...] [options] # --headed / --debug / --ai-trace / --no-cache など
npx e2e explore [goal] [options]
npx e2e list [files...] [options]
npx e2e cache ls|clear|stats
npx e2e feedback -m <text> [options]
npx e2e telemetry [status|enable|disable]CLI は Node.js 24.8 以降(22 系なら 22.22.3 以降)で動く。npm、pnpm、Yarn、Bun のどれでもインストールできるが、実行ランタイムは Node.js(bunx e2e も Node.js 上で動く)。
| 終了コード | 意味 |
|---|---|
| 0 | 選択したテストがすべて合格、flaky、または明示的にスキップ |
| 1 | テストまたはセットアップの失敗、テストのタイムアウト |
| 2 | CLI、設定、収集、依存関係、認証情報、モデル設定、ポリシーのエラー |
| 3 | エンジン、アプリプロセス、モデルプロバイダ、成果物、後片付けのインフラ障害 |
| 4 | ランナー内部の不変条件違反や未処理エラー |
| 130 | ユーザーまたは CI のシグナルで中断 |
複数の結果が混ざった実行では 130 > 4 > 3 > 2 > 1 > 0 の優先順で終了コードが決まる。個々の結果は .e2e/report.json に残る。.e2e/(report.json、artifacts/、cache/、logs/)は出力専用で、読むが編集しない。
テストコードにシークレットを書かない。アカウントは設定の credentials、その他の値は secrets に宣言し、credentials.user(name).password や secrets.get(name) で不透明な Secret を受け取る。Secret を渡せるのは fill() と agent.act の params だけ。
- モデルが受け取るのはシークレットの名前と用途だけ。ランナーがモデルの選んだフィールドを検査してから値を入力する。
- 画面上に現れた既知のシークレット値は
<secret:name>に、セキュアフィールドはvalue=<secure>に置き換えられる。 - シークレットを入力したあとは、その試行の間スクリーンショットをモデルに送らない。
アプリの内容がツール、認証情報、予算を与えることはできない。ランナーはモデルのテキストをコード、セレクタ、シェルコマンド、設定として評価しない。
| 拒否される操作 | 理由 |
|---|---|
http: / https: 以外への移動(about:blank を除く) | file:、data:、javascript: はローカルやインラインのコンテンツを読み込むため |
| セキュアフィールドの値・テキスト・属性の読み取り | シークレットではなく結果をアサートする |
宣言されていない名前での type_secret | ステップの params にあるシークレットだけ入力できる |
| パスワード以外のフィールドへのパスワード入力 | 用途が一致する必要がある |
シークレット入力後の app.screenshot() | セキュアフィールド以外にシークレットが表示されている可能性がある |
- GitHub: tester-army/e2e — モノレポ(packages / examples / skills / docs)
- e2e ドキュメント — Quickstart、Goals、Caching agent steps、Security など
- Quickstart —
npx e2e initからの導入手順 - examples/ — Vite、Next.js、Expo、SwiftUI の独立したサンプル
- npm: e2e
- callstack/agent-device — モバイルエンジンの基盤