mcpField
S
stably-sdk-rules

stably-sdk-rules

github.com/stablyaiテスト・品質2465 回インストール28 週間前に更新
リポジトリ

stably-sdk-rules について

stably-sdk-rulesは、Stably Playwright SDKでテストを作成するためのガイドで、ブラウザー自動化の開発者向けです。通常のPlaywrightとAIによる操作・アサーションの使い分けに加え、メール認証とテスト分離を説明します。

主な機能

  1. 01Playwrightのロケーターとアサーションの指針
  2. 02動的なビジュアルアサーション
  3. 03ページからの構造化データ抽出
  4. 04AIロケーターと操作の利用
  5. 05テスト用受信箱の分離
  6. 06Google認証フローへの対応

活用シーン

  1. 01不安定なキャンバス操作のテスト
  2. 02動的なページ内容の検証
  3. 03ページやメールからのデータ抽出
  4. 04OTPや認証メールのフローのテスト

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/stablyai/agent-skills --skill stably-sdk-rules

インストール後、Claude Code で/stably-sdk-rulesを入力して読み込まれたか確認します。

同梱ファイルREADME.md

詳細

Stably SDK のルール

クイックルール

  1. 決定論的な操作やアサーションには、まず生の Playwright を使用します(高速かつ低コストです)。
  2. Playwright が不安定になった場合は、コストより信頼性を優先します。
  3. キャンバス、座標ベースのドラッグ/クリック、不安定な複数ステップのフローには agent.act() を使用します。
  4. 動的な視覚アサーションには expect(...).aiAssert() を使用し、プロンプトを具体的にします。
  5. 視覚情報からデータを抽出する場合は、page.extract() / locator.extract() を使用します。
  6. 標準のロケーターではセマンティックなセレクターを見つけにくい場合は、page.getLocatorsByAI() を使用します。
  7. OTP/マジックリンク/確認メールのフローには Inbox を使用します。
  8. すべてのプロンプトは自己完結させ、暗黙の過去のコンテキストに依存しないでください。
  9. agent.act() のタスクは小さく保ち、ループ/計算/条件分岐はコードで処理します。
  10. ビューポート外のコンテンツが重要な場合にのみ fullPage: true を使用します。
  11. トレースを読みやすくするため、ロケーターには必ず .describe("...") を追加します。
  12. メールを分離するには、各テストで一意の Inbox.build({ suffix }) を使用し、クリーンアップします。

セットアップ(単一ブロック)

npm install -D @playwright/test @stablyai/playwright-test @stablyai/email
export STABLY_API_KEY=YOUR_KEY
export STABLY_PROJECT_ID=YOUR_PROJECT_ID
import { test, expect } from "@stablyai/playwright-test";
import { Inbox } from "@stablyai/email";

任意:プログラムから API キーを設定します。

import { setApiKey } from "@stablyai/playwright-test";
setApiKey("YOUR_KEY");

コアとなるルール

  • ロケーターのルール:ロケーターを操作する際は、必ず .describe("...") を使用します。
  • アサーションの選択:
    • まず Playwright のアサーションを使用します。
    • 動的なチェックや視覚要素の多いチェックには aiAssert を使用します。
  • 操作の選択:
    • 決定論的なステップには Playwright を使用します。
    • 不安定なタスクやセマンティックなタスク(特にキャンバス/座標)には agent.act を使用します。
  • プロンプトの品質:
    • 対象、意図、制約を明示します。
    • ステップ間でデータを渡す際は、曖昧な参照ではなく変数を使用します。

最小限の使用パターン

aiAssert
await expect(page).aiAssert("Shows revenue trend chart and spotlight card");
await expect(page.locator(".header").describe("Header")).aiAssert("Has nav, avatar, and bell icon");

アサーションで画面外のコンテンツが必要な場合にのみ、fullPage: true を使用します。

extract
const orderId = await page.extract("Extract the order ID from the first row");

スキーマを指定する場合:

import { z } from "zod";
const Schema = z.object({ revenue: z.string(), users: z.number() });
const metrics = await page.extract("Get revenue and active users", { schema: Schema });
getLocatorsByAI

Playwright >= 1.54.1 が必要です。

const { locator, count } = await page.getLocatorsByAI("the login button");
expect(count).toBe(1);
await locator.describe("Login button located by AI").click();
agent.act
await agent.act("Find the first pending order and mark it as shipped", { page });

適切なパターン:コードで値を計算してから、具体的な値をプロンプトに渡します。

Inbox(メールの分離)

インストール:npm install -D @stablyai/email。STABLY_API_KEY と STABLY_PROJECT_ID の環境変数が必要です(または Inbox.build() に渡します)。

const inbox = await Inbox.build({ suffix: `test-${Date.now()}` });
// inbox.address → "[email protected]"

await page.getByLabel("Email").describe("Email input").fill(inbox.address);

const email = await inbox.waitForEmail({ subject: "verification", timeoutMs: 60_000 });
const { data: otp } = await inbox.extractFromEmail({
  id: email.id,
  prompt: "Extract the 6-digit OTP code",
});

await inbox.deleteAllEmails();
Inbox.build のオプション
オプション型説明
suffixstringテストを分離するためのサフィックス(例:"test-123" → "[email protected]")
apiKeystring既定では STABLY_API_KEY 環境変数を使用します
projectIdstring既定では STABLY_PROJECT_ID 環境変数を使用します

並列実行時に分離できるよう、各テストで一意の suffix を必ず使用してください。受信ボックスは、作成前に受信したメールを自動的に除外します。

waitForEmail
const email = await inbox.waitForEmail({
  from: "[email protected]",    // filter by sender
  subject: "verification",         // contains match by default
  subjectMatch: "exact",           // or "contains" (default)
  timeoutMs: 60_000,               // default: 120000 (2 min)
  pollIntervalMs: 5000,            // default: 3000 (3 sec)
});

タイムアウト時間内に一致するメールが届かない場合は、EmailTimeoutError がスローされます。

extractFromEmail

{ data, reason } を返します。失敗すると EmailExtractionError がスローされます。

// String extraction
const { data: otp } = await inbox.extractFromEmail({
  id: email.id,
  prompt: "Extract the 6-digit OTP code",
});

// Structured extraction with Zod schema
import { z } from "zod";
const { data } = await inbox.extractFromEmail({
  id: email.id,
  prompt: "Extract the verification URL and expiration time",
  schema: z.object({ url: z.string().url(), expiresIn: z.string() }),
});
Inbox のプロパティ
プロパティ型説明
addressstringメールアドレス全体(指定した場合はサフィックス付き)
suffixstring | undefinedInbox.build() に渡されたサフィックス
createdAtDate受信ボックスの作成時刻。この時刻より前のメールは自動的に除外されます
listEmails
const { emails, nextCursor } = await inbox.listEmails(options?);
オプション型既定値説明
fromstring—送信者のアドレスでフィルターします
subjectstring—件名でフィルターします
subjectMatch'contains' | 'exact''contains'件名の照合モード
limitnumber20結果の最大数(最大:100)
cursorstring—前回の nextCursor によるページネーションカーソル
sinceDate—既定の作成時刻フィルターを上書きします
includeOlderbooleanfalse受信ボックスの作成前に受信したメールを含めます
その他のメソッド
const email = await inbox.getEmail(id);                          // get by ID
await inbox.deleteEmail(email.id);                               // delete single
await inbox.deleteAllEmails();                                   // delete all (this inbox only)
メールオブジェクトのプロパティ
プロパティ型説明
idstring一意の識別子
mailboxstringコンテナ(例:"INBOX")
from{ address: string, name?: string }送信者
to{ address: string, name?: string }[]宛先
subjectstring件名
receivedAtDate受信日時
textstring?プレーンテキスト本文
htmlstring[]?HTML 本文パーツ
Playwright フィクスチャパターン
import { test as base } from "@stablyai/playwright-test";
import { Inbox } from "@stablyai/email";

const test = base.extend<{ inbox: Inbox }>({
  inbox: async ({}, use, testInfo) => {
    const inbox = await Inbox.build({ suffix: `test-${testInfo.testId}` });
    await use(inbox);
    await inbox.deleteAllEmails();
  },
});

test("signup flow", async ({ page, inbox }) => {
  await page.fill("#email", inbox.address);
  await page.click("#signup");
  const email = await inbox.waitForEmail({ subject: "Welcome" });
  // ...
});
組織のメールアドレスを確認する

メールアドレスは Stably ダッシュボードで確認できます。

  • Settings > Email Inbox:コピー用ボタン付きで、アドレス全体が表示されます
  • コード内:Inbox.build() を呼び出した後、inbox.address を使うとアドレス全体を取得できます

アドレスの形式は {org-name}@mail.stably.ai です。ユーザーが許可リストへの登録を必要とする場合は、メールプロバイダーの許可リストに mail.stably.ai を追加してください。

ユーザーには、Settings > Email Inbox に移動して個別のアドレスを確認するよう案内してください。

認証フロー(Google)

ポップアップのカスタムスクリプトではなく、ヘルパーを使用してください。

import { authWithGoogle } from "@stablyai/playwright-test/auth";

await authWithGoogle({
  context,
  email: process.env.GOOGLE_AUTH_EMAIL!,
  password: process.env.GOOGLE_AUTH_PASSWORD!,
  otpSecret: process.env.GOOGLE_AUTH_OTP_SECRET!,
});

必須の環境変数:

  • GOOGLE_AUTH_EMAIL
  • GOOGLE_AUTH_PASSWORD
  • GOOGLE_AUTH_OTP_SECRET

専用のテスト用 Google アカウントのみを使用してください。

トラブルシューティング(概要)

  • aiAssert の処理が遅い/不安定:ロケーターの対象範囲を絞り、プロンプトを具体化し、不要な fullPage: true を避けてください。
  • agent.act が失敗する:タスクを小さく分割し、明示的な制約を渡し、必要な場合に限って maxCycles を増やしてください。
  • メールのタイムアウト:件名/送信者のフィルターを確認し、一意の受信トレイサフィックスを使用してください。

完全なリファレンス

コメント