e2e-studio-tests について
Supabase Studio の Playwright エンドツーエンドテストの実行、作成、デバッグに関するガイドです。コマンド、堅牢なセレクター、コールドスタートの違い、CI 失敗の調査方法を扱います。
主な機能
- 01全件または指定テストの実行
- 02名前による絞り込みと UI モードでのデバッグ
- 03セレクター作成の規約
- 04固定待機と強制クリックの回避
- 05コールドスタートと CI 失敗の調査
活用シーン
- 01Studio のエンドツーエンドテストの実行
- 02特定のテストや名前によるテストの特定
- 03ローカルでは成功し CI で失敗する問題の調査
MCP 対応
ローカルでのデバッグ時に Playwright MCP ツールで UI を確認する方法に触れています。MCP サーバーの提供や管理については説明していません。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ supabase/ supabase --skill e2e-studio-testsインストール後、Claude Code で/e2e-studio-testsを入力して読み込まれたか確認します。
詳細
E2E Studio テスト
Studio アプリケーションの Playwright エンドツーエンドテストを実行します。
テストの実行
テストは e2e/studio ディレクトリから実行する必要があります。
cd e2e/studio && pnpm run e2e
特定のファイルを実行する
cd e2e/studio && pnpm run e2e -- features/cron-jobs.spec.ts
grep フィルターを使って実行する
cd e2e/studio && pnpm run e2e -- --grep "test name pattern"
デバッグ用の UI モード
cd e2e/studio && pnpm run e2e -- --ui
環境のセットアップ
- テストの実行時に、web server の設定を通じて Supabase のローカルコンテナーが自動的に起動します
- セルフホストモード(
IS_PLATFORM=false)では、テストは並列実行されます(3 ワーカー) - セルフホストテストでは手動でのセットアップは不要です
テストファイルの構成
- テストは
e2e/studio/features/*.spec.tsにあります - カスタムテストユーティリティを使用します:
import { test } from '../utils/test.js' - テストフィクスチャには、
page、refなどのヘルパーが用意されています
よく使うパターン
余裕のあるタイムアウトで要素を待機します。
await expect(locator).toBeVisible({ timeout: 30000 })
デバッグ用に expect にメッセージを追加します。
await expect(locator).toBeVisible({ timeout: 30000 }, 'Element should be visible after page load')
データベースの状態を共有するテストには、シリアルモードを使用します。
test.describe.configure({ mode: 'serial' })
堅牢なセレクターの作成
セレクターの優先順位(高い順)
-
アクセシブルな名前を指定した
getByRole- 最も堅牢で、アクセシビリティをテストできますpage.getByRole('button', { name: 'Save' }) page.getByRole('button', { name: 'Configure API privileges' }) -
getByTestId- 安定した明示的なテストフックですpage.getByTestId('table-editor-side-panel') -
完全一致の
getByText- 一意のテキストに適していますpage.getByText('Data API Access', { exact: true }) -
CSS を使った
locator- 使用は控えめにしてください。より壊れやすい方法ですpage.locator('[data-state="open"]')
避けるべきパターン
-
XPath セレクター - DOM の変更に弱いです
// BAD locator('xpath=ancestor::div[contains(@class, "space-y")]') -
locator('..')による親要素へのたどり - 構造の変更で動作しなくなります// BAD element.locator('..').getByRole('button') -
汎用要素に対する広範な
filter({ hasText })- 複数の要素に一致する可能性があります// BAD - popover may have more than one combobox // Could consider scoping down the container or filtering the combobox more specifically popover.getByRole('combobox')
コンポーネントにアクセシブルなラベルを追加する
コンポーネントに適切なアクセシブルな名前がない場合は、ソースコードに追加します。
// In the React component
<Button aria-label="Configure API privileges">
<Settings />
</Button>
次に、テストでそのラベルを使用します。
page.getByRole('button', { name: 'Configure API privileges' })
検索範囲の絞り込み
間違った要素に一致しないよう、セレクターの範囲を特定のコンテナーに絞ります。
// Good - scoped to side panel
const sidePanel = page.getByTestId('table-editor-side-panel')
const toggle = sidePanel.getByRole('switch')
// Good - find unique element, then scope from there
const popover = page.locator('[data-radix-popper-content-wrapper]')
const roleSection = popover.getByText('Anonymous (anon)', { exact: true })
waitForTimeout を避ける
waitForTimeout は決して使用せず、常に特定の状態を待機します。
// BAD
await page.waitForTimeout(1000)
// GOOD - wait for UI element
await expect(page.getByText('Success')).toBeVisible()
// GOOD - wait for API response
const apiPromise = waitForApiResponse(page, 'pg-meta', ref, 'query?key=table-create')
await saveButton.click()
await apiPromise
// GOOD - wait for toast indicating operation complete
await expect(page.getByText('Table created successfully')).toBeVisible({ timeout: 15000 })
クリック時に force: true を避ける
非表示の要素を強制的にクリックするのではなく、まず表示させます。
// BAD
await menuButton.click({ force: true })
// GOOD - hover to reveal, then click
await tableRow.hover()
await expect(menuButton).toBeVisible()
await menuButton.click()
デバッグ
トレースを表示する
cd e2e/studio && pnpm exec playwright show-trace <path-to-trace.zip>
HTML レポートを表示する
cd e2e/studio && pnpm exec playwright show-report
エラーコンテキスト
エラーコンテキストのファイルは test-results/ ディレクトリに保存されます。
Playwright MCP ツール
ローカルでのデバッグ時に UI を調べるには、Playwright MCP ツールを使用します。
CI とローカル開発の違い
主な違いは、コールドスタートとウォーム状態です。
CI(コールドスタート)
テストは空のデータベースから実行されます。テストを実行するたびにデータベースがリセットされ、新しいコンテナーが起動します。pg_cron などの拡張機能は、デフォルトでは有効になっていません。
pnpm dev:studio-local を使ったローカル開発
開発サーバーを起動した状態でデバッグする場合、データベースに以前の実行からの状態(拡張機能が有効になっている、テストデータが存在するなど)が残っていることがあります。
コールドスタート時のバグへの対処
ローカルでは動作しても CI で失敗するテストは、既存の状態を前提としていることがよくあります。
よくある問題
- 拡張機能が有効になっていない(テストのセットアップで有効にする必要があります)
- 並列テストが共有状態を変更しようとしたときに発生する競合状態(
test.describe.configure({ mode: 'serial' })を使用します) - 状態がセットアップされていないためにページ構造が異なり、ロケーターが間違った要素に一致する
CI の動作をローカルで再現する
テストフレームワークは pnpm run e2e の実行時にデータベースを自動的にリセットします。これにより CI と同じ動作になります。
Playwright MCP のデバッグに pnpm dev:studio-local を使用する場合は、状態が CI と異なることに注意してください。
CI 失敗時のデバッグワークフロー
- まず、
pnpm run e2e -- features/<file>.spec.tsを使ってテストをローカルで実行します(コールドスタート)。 test-results/ディレクトリでエラーコンテキストを確認します。- UI の状態を調べる必要がある場合は、
pnpm dev:studio-localを起動して Playwright MCP ツールを使用します。 - 開発サーバーで見える状態が、CI には存在しない場合があることを忘れないでください。