e2e-testing について
Langflowのフロントエンド向けに、PlaywrightのE2Eテスト作成と保守を案内するスキルです。機能テスト、失敗の調査、カバレッジ確認に取り組む開発者向けに、フィクスチャ、セレクター、ヘルパー、タグの規約を説明します。
主な機能
- 01Playwright E2Eテストの作成と絞り込み
- 02フィクスチャによるAPIとフローのエラー検出
- 03優先順位に沿った安定セレクターの選択
- 04セットアップ・画面・フロー用ヘルパーの再利用
- 05リリースと領域タグの付与
活用シーン
- 01新機能のE2Eテスト作成
- 02失敗したPlaywrightテストの調査
- 03テストタグとカバレッジの確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ langflow-ai/ langflow --skill e2e-testingインストール後、Claude Code で/e2e-testingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/fixtures.mdhelpers.mdselectors.md詳細
Langflow E2E テスト(Playwright)
適用するケース
- 機能やフローの E2E テストの作成をユーザーが依頼した場合
- 失敗する E2E テストの 修正をユーザーが依頼した場合
- E2E テストのカバレッジの レビューをユーザーが依頼した場合
- ユーザーがコンポーネントの
data-testid属性を変更した場合(既存のテストが壊れる可能性があります) - ユーザーが
src/frontend/tests/utils/のテストユーティリティを変更した場合
次の場合は適用しないでください:
- ユーザーが単体テストについて尋ねている場合(Jest には
frontend-testingスキルを使用してください) - ユーザーがバックエンドテストについて尋ねている場合(pytest には
backend-code-reviewスキルを使用してください)
技術スタック
| ツール | バージョン | 目的 |
|---|---|---|
| Playwright | 1.59.1 | E2E テストランナー+ブラウザー自動化 |
| Chromium | (同梱) | デフォルトのブラウザー(Firefox/Safari は無効) |
| カスタムフィクスチャ | tests/fixtures.ts | API エラーとフロー実行の失敗を自動検出 |
主なコマンド
# Run all E2E tests
npx playwright test
# Run tests filtered by tag
npx playwright test --grep "@release"
npx playwright test --grep "@workspace"
npx playwright test --grep "@starter-projects"
# Run a specific test file
npx playwright test tests/core/features/run-flow.spec.ts
# Debug mode (headed browser + step through)
npx playwright test --debug
# Show HTML report after run
npx playwright show-report
# Update snapshots (if used)
npx playwright test --update-snapshots
設定
ファイル:src/frontend/playwright.config.ts
| 設定 | 値 | 理由 |
|---|---|---|
fullyParallel | true | テストを並列実行して高速化します |
timeout | 5 分 | フローのビルドには時間がかかる場合があるため、誤ったタイムアウトを防ぎます |
retries | 3(ローカル)、2(CI) | 不安定なネットワーク/レンダリングの問題を再試行で捕捉します |
workers | 2 | 速度とリソース使用量のバランスを取ります |
actionTimeout | 20s | 個々のアクション(クリック、入力など)のタイムアウト |
trace | on-first-retry | デバッグ用に失敗時のトレースを記録します |
baseURL | http://localhost:3000 | Vite 開発サーバー |
WebServer:Playwright はバックエンド(7860 で uvicorn)とフロントエンド(3000 で npm start)を自動起動します。
ディレクトリ構成
src/frontend/tests/
├── fixtures.ts # Custom test fixture with error detection
├── globalTeardown.ts # Cleanup (removes temp DB after tests)
├── core/
│ ├── features/ # Main feature tests (run-flow, playground, etc.)
│ ├── integrations/ # Starter project / template tests
│ ├── regression/ # Bug regression tests
│ └── unit/ # Component-level Playwright tests
├── extended/
│ ├── features/ # Extended features (MCP, auto-save, etc.)
│ ├── integrations/ # Extended integrations
│ └── regression/ # Extended regressions
└── utils/ # 37+ shared helper functions
ファイル名
.spec.tsサフィックスを付けた kebab-case:run-flow.spec.ts、playground.spec.ts、flow-lock.spec.ts- テンプレートのテストではスペースを使用できます:
Document QA.spec.ts、Social Media Agent.spec.ts - 並列化用のシャーディングされたテスト:
chatInputOutputUser-shard-0.spec.ts
注:E2E テストでは
.spec.ts(Playwright の規約)を使用します。単体テストでは.test.tsx(Jest の規約)を使用します。混在させないでください。
テストの構成
基本的なテスト
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
test(
"user should be able to run a flow successfully",
{ tag: ["@release", "@workspace"] },
async ({ page }) => {
await awaitBootstrapTest(page);
// Arrange: Create a flow
await page.getByTestId("blank-flow").click();
// Act: Add components and run
await page.getByTestId("sidebar-search-input").fill("Chat Output");
// ... setup ...
// Assert: Verify result
await expect(page.getByTestId("build-status-success")).toBeVisible({ timeout: 30000 });
},
);
test.describe を使用する場合
test.describe("Flow Lock Feature", () => {
test(
"should lock and unlock a flow",
{ tag: ["@release", "@api"] },
async ({ page }) => {
// ...
},
);
test(
"should prevent editing when locked",
{ tag: ["@release"] },
async ({ page }) => {
// ...
},
);
});
シリアルモードを使用する場合(順序に依存するテスト)
test.describe.configure({ mode: "serial" });
test("step 1: create flow", async ({ page }) => { /* ... */ });
test("step 2: edit flow", async ({ page }) => { /* ... */ });
test("step 3: delete flow", async ({ page }) => { /* ... */ });
イベント配信モードを使用する場合(ストリーミング/ポーリング/直接)
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
withEventDeliveryModes(
"Document Q&A should work",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
// This test runs 3 times: streaming, polling, direct
// Each mode is configured automatically via route interception
},
);
タグシステム
すべてのテストに @release タグを付ける必要があります。リリース実行ではこのタグを検索するため、タグがない、または誤ったタグが付いた spec は、リリースのカバレッジから気付かれないまま除外されます。@release に加えて、以下のドメインタグを付けてください(テストには複数のタグを付けられます)。使用できるタグはこの6つのみです。新しいタグを作らないでください。
| タグ | 目的 | 使用するケース |
|---|---|---|
@release | リリース実行の対象(すべての spec で必須) | すべてのテスト |
@workspace | ワークスペース/フローの管理 | フローの作成、編集、削除 |
@api | API に依存する機能 | バックエンドのエンドポイントを呼び出すテスト |
@database | データベース操作 | 永続化を伴うテスト |
@components | コンポーネントレベルのテスト | 個々のコンポーネントの動作 |
@starter-projects | テンプレート/スタータープロジェクトのテスト | 事前構築済みフローのテンプレート |
// Right: tag your test
test("my feature test", { tag: ["@release", "@workspace"] }, async ({ page }) => { ... });
// Wrong: no tags — test can't be filtered
test("my feature test", async ({ page }) => { ... });
カスタムフィクスチャ:エラー検出
@playwright/test ではなく、必ず ../../fixtures から test と expect をインポートしてください。
// Right
import { expect, test } from "../../fixtures";
// Wrong — bypasses error detection
import { expect, test } from "@playwright/test";
理由:カスタムフィクスチャはすべての /api/ レスポンスを自動で監視し、次の場合にテストを失敗させます。
- HTTP 400、404、422、または500エラーが発生した場合
- フローの実行結果として、イベントストリームに
error: trueが返された場合 - ストリーミングされたレスポンスに Python の例外が含まれる場合
想定されるエラー(エラー処理のテストなど)を許可するには、次のようにします。
test("should show error on invalid input", { tag: ["@release"] }, async ({ page }) => {
page.allowFlowErrors(); // Allow flow errors for this test
// ... test that expects errors ...
});
セレクター戦略
優先順位(上から順に推奨)
getByTestId— 最も安定しており、Langflow では95%のケースで使用しますgetByRole— ボタン、見出し、フォーム要素に使用しますgetByText— 表示されるテキストコンテンツに使用しますwaitForSelector— CSS セレクターや動的な要素に使用しますlocator— 複雑なセレクター(CSS、XPath)に使用します
共通の data-testid パターン
キャンバスとナビゲーション:
blank-flow— 新規ブランクフローボタンsidebar-search-input— コンポーネント検索canvas_controls_dropdown— キャンバス操作メニューfit_view、zoom_out、zoom_in— キャンバス操作react-flow-id— ReactFlow キャンバスコンテナ
コンポーネントのフィールド:
popover-anchor-input-{fieldname}— コンポーネントパラメータの入力フィールドinput-chat-playground— プレイグラウンドのチャット入力div-chat-message— プレイグラウンドのチャットメッセージ
アクション:
add-component-button-{component}— キャンバスにコンポーネントを追加button-send— チャットメッセージを送信button_run_{component}— 特定のコンポーネントを実行publish-button、save-flow-button— フローの操作edit-fields-button— インスペクションパネルのフィールドエディタを切り替え
モーダルとパネル:
modal-title— モーダルの見出しicon-Globe— グローバル変数icon-Lock— フローロックの切り替えsession-selector— プレイグラウンドのセッション切り替え
重要: グローバル変数とバッジ
コンポーネントのフィールドでグローバル変数が選択されている場合(load_from_db: true + value: "OPENAI_API_KEY")、フィールドは <input> 要素の代わりにバッジをレンダリングします。つまり、getByTestId("popover-anchor-input-api_key") はその要素を見つけられません — DOM に存在しないためです。
グローバル変数が事前に選択されているテンプレート: Market Research、Price Deal Finder、Research Agent。 選択されていないテンプレート(input がレンダリングされる): Instagram Copywriter。
コアヘルパー関数
src/frontend/tests/utils/ にあります:
| 関数 | 何をするか | いつ使うか |
|---|---|---|
awaitBootstrapTest(page) | アプリが完全に読み込まれるまで待機します | すべてのテストの開始時 |
initialGPTsetup(page) | 完全なセットアップ: adjustView → updateComponents → selectModel → addKey → adjustView → unselectNodes | OpenAI の設定が必要なテスト |
adjustScreenView(page, opts?) | ビューをフィット + ズームアウトします | キャンバスにコンポーネントを追加した後 |
zoomOut(page, times) | N 回ズームアウトします | コンポーネントが小さすぎる場合 |
selectGptModel(page) | すべての Language Model ノードに gpt-4o-mini を選択します | GPT に依存するテスト |
addOpenAiInputKey(page) | すべての openai_api_key フィールドに OPENAI_API_KEY を入力します | API キーが必要なテスト |
enableInspectPanel(page) | インスペクションパネルを ON に切り替えます | edit-fields-button の前に必ず呼び出す必要があります |
disableInspectPanel(page) | インスペクションパネルを OFF に切り替えます | インスペクション後のクリーンアップ |
updateOldComponents(page) | 古いコンポーネントが存在する場合に「Update all」をクリックします | 保存されたフローを読み込んだ後 |
unselectNodes(page) | 空のキャンバス領域をクリックしてすべてのノードの選択を解除します | ノード操作の後 |
renameFlow(page, { flowName }) | 現在のフローの名前を変更します | フロー管理のテスト |
uploadFile(page, filename) | テストアセットからファイルをアップロードします | ファイルアップロードのテスト |
withEventDeliveryModes(...) | テストを3回実行します: streaming、polling、direct | スタータープロジェクトのテスト |
initialGPTsetup のオプション
await initialGPTsetup(page); // All steps
await initialGPTsetup(page, {
skipAdjustScreenView: true,
skipUpdateOldComponents: true,
skipSelectGptModel: true,
});
インスペクションパネルのパターン(重要)
// MUST enable inspection panel FIRST
await enableInspectPanel(page);
// Click a node to select it
await page.getByTestId("title-OpenAI").click();
// Open field editor
await page.getByTestId("edit-fields-button").click();
// Toggle field visibility
await page.getByTestId("showmodel_name").click();
// Close field editor
await page.getByTestId("edit-fields-button").click();
enableInspectPanel(page) をスキップすると、edit-fields-button は表示されません。
スキップパターン
// Skip test if env var missing
test.skip(!process?.env?.OPENAI_API_KEY, "OPENAI_API_KEY required to run this test");
// Skip test unconditionally with reason
test.skip(true, "Feature not yet implemented with new designs");
優れた E2E テストの書き方
すべきこと:
- すべてのテストにタグを付ける:
@release(該当する場合は任意のドメインタグも追加) ../../fixturesからインポートする:@playwright/testからではなくawaitBootstrapTest(page)で開始する — 常に- 安定したセレクタには
getByTestIdを使う - 明示的なタイムアウトを設定する: 非同期操作では
waitForSelectorとexpect(...).toBeVisible()に設定 - 完全なユーザーフローをテストする: セットアップ → アクション → 検証
withEventDeliveryModesを使う: フロー実行(チャット、ビルド)を伴うテストの場合
やってはいけないこと:
page.waitForTimeout()は、絶対に必要な場合を除いて使用しないでください — 代わりにwaitForSelectorまたはexpect().toBeVisible()を使用してください- API キーをハードコードしないでください —
process.env.OPENAI_API_KEYから読み込んでください - 理由なくテストをスキップしないでください —
test.skip()には必ず第2引数を指定してください @playwright/testからインポートしないでください — カスタムフィクスチャを使用してくださいedit-fields-buttonにアクセスする前にenableInspectPanel(page)を呼び出し忘れないでください- グローバル変数が選択されているときに、入力フィールドが存在すると決めつけないでください(代わりにバッジが表示されます)
チャレンジテスト(ここにも適用)
E2E テストでは、敵対的なシナリオもカバーしてください。
- 無効な入力:10K 文字の貼り付け、特殊文字(
<script>alert(1)</script>)、空の送信 - ネットワークの中断:ビルド中にユーザーの接続が切れた場合、どうなりますか?
- 権限の境界:ユーザーは直接 URL を通じて、別のユーザーのフローにアクセスできますか?
- 同時操作:削除のダブルクリック、チャットメッセージの連続送信
- エラーからの回復:500 エラーから UI は適切に回復しますか?