できない.dev

Playwright で要素をクリックできない(TimeoutError: locator.click)

locator.click のタイムアウトは、セレクタに一致する要素が無い・要素が actionability チェックを通らない・他要素に覆われている・iframe 内にある、の 4 系統。
trace や --debug で実際の状態を確認して切り分ける。

#playwright#timeout#locator#click#e2e

公開:

要約

TimeoutError: locator.click: Timeout 30000ms exceeded. で要素をクリックできないのは、単純な待ち時間不足よりも、セレクタが一致していない要素が操作可能(actionable)になっていない ことが大半。
Playwright は click 前に visible / enabled / stable などの actionability チェックを自動で待つため、タイムアウトは「チェックを通らない理由」を探すサインと捉える。

よくある原因

  1. 要素が存在しない: セレクタのタイポ、描画前、条件付きレンダリングで DOM に無い
  2. actionability チェックを通らない: display: nonedisabled、アニメーション中で位置が安定しない
  3. 他の要素が覆っている: モーダルの背景、Cookie バナー、固定ヘッダーがクリック位置を遮る
  4. iframe 内の要素: ページ直下の locator では iframe の中に届かない

解決策

1. 失敗時点の状態を確認する

npx playwright test --debug
# CI で落ちた場合は trace を残して確認
npx playwright show-trace trace.zip

エラーメッセージには「待っていた条件」(例: element is not visible)が含まれるので、まずそこを読む。
チェック内容の一覧は 公式の Auto-waiting にある。

2. ユーザー視点の locator に変える

// CSS セレクタより壊れにくい
await page.getByRole("button", { name: "保存" }).click();

DOM 構造依存の CSS セレクタは描画の変化で壊れやすい。
ロールやラベルで特定すれば、一致しない原因の多くを排除できる。

3. 覆っている要素を先に処理する

await page.getByRole("button", { name: "同意する" }).click(); // バナーを閉じる
await page.getByRole("link", { name: "詳細" }).click();

別要素が遮っている場合は、その要素を閉じる操作をテストに含めるのが正攻法。
どうしても回避できない装飾要素なら click({ force: true }) でチェックを飛ばせるが、実ユーザーが押せない状態を隠すリスクがあるため最終手段とする。

4. iframe は frameLocator で

await page.frameLocator("#payment-frame").getByLabel("カード番号").fill("4242...");

タイムアウト値自体を伸ばしたい場合は Timeouts の設定で調整できるが、原因を特定せずに伸ばすのは flaky テストの温床になる。

この記事は役立ちましたか?