できない.dev

Playwright で locator が要素を特定できない(strict mode violation)

strict mode violation は locator が 2 つ以上の要素に一致したときのエラー。
getByRole で属性まで指定する、filter() で絞り込む、要素に test id を付与するのが基本の解決策。
first() での回避は最終手段。

#playwright#locator#strict-mode#selector#e2e

公開:

要約

Error: strict mode violation: locator('button') resolved to 3 elements は、locator が複数の要素に一致した ことを示すエラー。
Playwright の locator は strict mode がデフォルトで、操作対象は 1 要素に特定されている必要がある。
セレクタを曖昧なまま first() で逃げるのではなく、一意に特定できる locator に書き換える のが正攻法。

よくある原因

  1. セレクタが汎用的: button.item のようなセレクタがページ全体で複数一致する
  2. 繰り返し要素: 一覧やテーブルで同じテキストのボタン(「編集」「削除」など)が行ごとに描画される
  3. 重複 DOM: レスポンシブ対応でモバイル用・デスクトップ用に同じ要素が 2 セット存在する
  4. コンポーネント再利用: 同一構造のカードやフォームが複数描画される

解決策

1. ロールと名前で一意化する

// NG: strict mode violation になりやすい
await page.locator("button").click();
 
// OK: ロール + アクセシブルネームで特定
await page.getByRole("button", { name: "保存" }).click();

推奨される locator の選び方は 公式ドキュメントの Locators にまとまっている。

2. filter で絞り込む

// 「Tokyo」を含む行の中の「編集」ボタン
await page
  .getByRole("row")
  .filter({ hasText: "Tokyo" })
  .getByRole("button", { name: "編集" })
  .click();

繰り返し要素は「どの行・どのカードの中か」という文脈ごと指定すると、データの増減に強いテストになる。

3. test id を付与する

<button data-testid="submit-order">注文する</button>
await page.getByTestId("submit-order").click();

アクセシブルな属性で特定しづらい要素には data-testid を付与する。

4. nth / first は意図がある場合のみ

await page.getByRole("listitem").first().click();

「先頭の項目を選ぶ」ことが仕様として正しい場合のみ使う。
一意化を諦めるための first() は、DOM の並び替えで別要素を操作する flaky テストにつながる。

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