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 チェックを自動で待つため、タイムアウトは「チェックを通らない理由」を探すサインと捉える。
よくある原因
- 要素が存在しない: セレクタのタイポ、描画前、条件付きレンダリングで DOM に無い
- actionability チェックを通らない:
display: noneやdisabled、アニメーション中で位置が安定しない - 他の要素が覆っている: モーダルの背景、Cookie バナー、固定ヘッダーがクリック位置を遮る
- 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 テストの温床になる。