トーストとちらつきを捕まえる
ユースケース: 保存をクリックすると2秒間トーストが表示される。もっと厄介なケースでは、画面遷移中にエラー画面が300msだけ表示される、バリデーションメッセージが勝手に消える、ページが完成して見えた後にレイアウトが跳ねる。見に行った頃にはもう消えているものを検証しなければなりません。
なぜ難しいのか
Section titled “なぜ難しいのか”expect(toast).toBeVisible()はトーストとの競走になります。何かの理由でアサーションが遅れれば(CIの負荷、遅いセレクタ)、トーストは消えていてテストはフレーキーになります。- トーストが表示されなかったこと、エラー画面が一瞬表示されなかったことのアサーションはさらに困難です。アクション後のスクリーンショットは、その最中に何が起きたかについて何も証明しません。
- 動画を録って目視する方法はスケールせず、全フレームを差分してアサーションに使うのはそれ自体が一大プロジェクトです。
agent-screen-observer は画面をストリームとして監視し(CDPスクリーンキャスト — フレームはページの再描画時にのみ届きます)、アクション前のベースラインと全フレームを古典的なコンピュータビジョンで差分し、視覚イベントを報告します。現れて消えたもの、一瞬通過した画面、動き続けている領域などです。
npm install -D agent-screen-observerPlaywrightフィクスチャで設定いらず
Section titled “Playwrightフィクスチャで設定いらず”import { test, expect } from 'agent-screen-observer/playwright';
test('can save', async ({ page }) => { await page.goto('/editor'); await page.click('#save'); // 自動的に観測される await expect(page.locator('h1')).toBeVisible(); // アサーションは今まで通り});デフォルトでは、視覚的な検出結果(一時表示要素、一瞬表示された画面)をテストを失敗させずに警告として報告します。既存のスイートに取り付けるだけで、あなたのUIが陰で何をしていたかが見えてきます。
一時表示UIへの本気のアサーション
Section titled “一時表示UIへの本気のアサーション”トーストそのものがテスト対象なら、コアAPIを使います。
import { test, expect } from '@playwright/test';import { ScreenObserver } from 'agent-screen-observer';
test('保存すると確認トーストが表示される', async ({ page }) => { await page.goto('/editor'); const observer = await ScreenObserver.attach(page, { outDir: 'out/save-toast' });
const report = await observer.observe('click Save', () => page.click('#save'));
// トースト = アクション後に現れて、また消えたもの。 const toast = report.events.find((e) => e.type === 'transient_element'); expect(toast, report.summary).toBeTruthy(); expect(toast!.appearedAtMs).toBeLessThan(1000); // すみやかに表示されたこと
await observer.detach();});レポートは不在の証明にも使えます。ナビゲーション中にエラー画面が一瞬も表示されなかったこと:
const report = await observer.observe('submit order', () => page.click('#place-order'),);const nav = report.events.find((e) => e.type === 'full_screen_change');// states = 通過した画面状態のタイムライン。3以上なら何かが一瞬表示されている。expect(nav?.states?.length ?? 0, report.summary).toBeLessThanOrEqual(2);すべての観測は証拠を outDir に書き出します。変化領域ごとのクロップ、中間画面のキーフレーム、前後のフルスクリーンショット、そして人間のレビュー用に実時間の replay.gif です。
遅れてくるレイアウトシフトを捕まえる(フレーキーの温床)
Section titled “遅れてくるレイアウトシフトを捕まえる(フレーキーの温床)”report.settled / settleTimeMs は画面が本当に変化しなくなった時刻を教えてくれます。アクティビティバーストは段階的レンダリング — ページが「完成して見えた」1〜2秒後にコンテンツが飛び込んでくる現象 — を暴きます。これこそが、20回に1回 click() が別の要素に当たるページの正体です。フレーキーテストを自動修復するを参照してください。
- キャプチャはChromium限定です(CDPスクリーンキャスト)。
- 動き続ける領域(スピナー、動画、カルーセル)は自動的にマスクされ、
animated_regionとして一度だけ報告されます。静定の判定やイベントを汚しません。 - 検出は決定論的なピクセル差分で、LLMは関与しません。UIをAIエージェントで操作している場合は、1行のテキストサマリーをそのまま安価に渡せる設計です(スクリーンショット1枚の約1,200トークンに対して数十トークン)。
- フレーキーテストを自動修復する — オブザーバーのレポートをフレーク診断に使う。
- 慣性つき地図UIをテストする — インタラクティブUIのピクセルベース観測。