その他のロケーター
はじめに
最も一般的でおすすめのロケーターについては、メインのロケーターガイドをご確認ください。
Page.GetByRole() や Page.GetByText() のような推奨されるロケーターに加えて、Playwright はこのガイドで説明されているさまざまなその他のロケーターをサポートしています。
CSS ロケーター
ページが変更されたときに壊れる可能性のある実装に結びついた CSS を使用する代わりに、テキストやアクセシブルなロールのようなユーザーに見えるロケーターを優先することをお勧めします。
Playwright は、CSS セレクターによって要素を特定できます。
await page.Locator("css=button").ClickAsync();
Playwright は、標準の CSS セレクターを 2 つの方法で拡張します。
- CSS セレクターは、オープンシャドウ DOM を貫通します。
- Playwright は、
:visible
、:has-text()
、:has()
、:is()
、:nth-match()
などのカスタム疑似クラスを追加します。
CSS: テキストによるマッチング
Playwright には、要素をテキストコンテンツでマッチングするための多数の CSS 疑似クラスが含まれています。
-
article:has-text("Playwright")
-:has-text()
は、指定されたテキストを内部のどこか、おそらく子要素または子孫要素に含む要素にマッチングします。マッチングは大文字と小文字を区別せず、空白をトリムし、部分文字列を検索します。たとえば、
article:has-text("Playwright")
は<article><div>Playwright</div></article>
にマッチングします。:has-text()
は他の CSS 指定子と一緒に使用する必要があることに注意してください。そうしないと、<body>
を含む指定されたテキストを含むすべての要素にマッチングします。// Wrong, will match many elements including <body>
await page.Locator(":has-text(\"Playwright\")").ClickAsync();
// Correct, only matches the <article> element
await page.Locator("article:has-text(\"Playwright\")").ClickAsync(); -
#nav-bar :text("Home")
-:text()
疑似クラスは、指定されたテキストを含む最小の要素にマッチングします。マッチングは大文字と小文字を区別せず、空白をトリムし、部分文字列を検索します。たとえば、これは
#nav-bar
要素内のどこかにテキスト "Home" を持つ要素を見つけます。await page.Locator("#nav-bar :text('Home')").ClickAsync();
-
#nav-bar :text-is("Home")
-:text-is()
疑似クラスは、正確なテキストを持つ最小の要素にマッチングします。正確なマッチングは大文字と小文字を区別し、空白をトリムし、完全な文字列を検索します。たとえば、
:text-is("Log")
は<button>Log in</button>
にマッチングしません。なぜなら、<button>
には"Log"
と等しくない単一のテキストノード"Log in"
が含まれているからです。ただし、:text-is("Log")
は<button> Log <span>in</span></button>
にマッチングします。なぜなら、<button>
にはテキストノード" Log "
が含まれているからです。同様に、
:text-is("Download")
は<button>download</button>
にマッチングしません。なぜなら、大文字と小文字が区別されるからです。
-
#nav-bar :text-matches("reg?ex", "i")
-:text-matches()
疑似クラスは、JavaScript のような正規表現にマッチングするテキストコンテンツを持つ最小の要素にマッチングします。たとえば、
:text-matches("Log\s*in", "i")
は<button>Login</button>
および<button>log IN</button>
にマッチングします。
テキストマッチングは常に空白を正規化します。たとえば、複数のスペースを 1 つに変換し、改行をスペースに変換し、先頭と末尾の空白を無視します。
button
および submit
タイプの入力要素は、テキストコンテンツの代わりに value
でマッチングされます。たとえば、:text("Log in")
は <input type=button value="Log in">
にマッチングします。
CSS: 可視要素のみのマッチング
Playwright は、CSS セレクターで :visible
疑似クラスをサポートしています。たとえば、css=button
はページ上のすべてのボタンにマッチングしますが、css=button:visible
は可視ボタンのみにマッチングします。これは、非常によく似ているが可視性が異なる要素を区別するのに役立ちます。
最初に非表示で 2 番目に表示される 2 つのボタンがあるページを考えてみましょう。
<button style='display: none'>Invisible</button>
<button>Visible</button>
-
これは両方のボタンを見つけ、厳密性違反エラーをスローします。
await page.Locator("button").ClickAsync();
-
これは 2 番目のボタンのみを見つけます。なぜなら、それが可視であり、それをクリックするからです。
await page.Locator("button:visible").ClickAsync();
CSS: 他の要素を含む要素
:has()
疑似クラスは、実験的な CSS 疑似クラスです。パラメーターとして渡されたセレクターのいずれかが、与えられた要素の :scope
を基準にして、少なくとも 1 つの要素にマッチングする場合、要素を返します。
次のスニペットは、内部に <div class=promo>
を持つ <article>
要素のテキストコンテンツを返します。
await page.Locator("article:has(div.promo)").TextContentAsync();
CSS: 条件の 1 つにマッチングする要素
コンマ区切りの CSS セレクターのリストは、そのリスト内のセレクターの 1 つによって選択できるすべての要素にマッチングします。
// Clicks a <button> that has either a "Log in" or "Sign in" text.
await page.Locator("button:has-text(\"Log in\"), button:has-text(\"Sign in\")").ClickAsync();
:is()
疑似クラスは、要素に追加の条件のリストを指定するのに役立つ可能性がある実験的な CSS 疑似クラスです。
CSS: レイアウトに基づいた要素のマッチング
レイアウトに基づいたマッチングは、予期しない結果を生み出す可能性があります。たとえば、レイアウトが 1 ピクセル変化すると、別の要素がマッチングされる可能性があります。
特徴的な機能が欠けている場合、ターゲット要素に適したセレクターを思いつくのが難しい場合があります。この場合、Playwright レイアウト CSS 疑似クラスを使用すると役立つ場合があります。これらは、通常の CSS と組み合わせて、複数の選択肢の 1 つを特定できます。
たとえば、input:right-of(:text("Password"))
は、テキスト "Password" の右側にある入力フィールドにマッチングします。ページに互いに区別するのが難しい複数の入力がある場合に役立ちます。
レイアウト疑似クラスは、input
のような他のものに加えて役立つことに注意してください。:right-of(:text("Password"))
のように、レイアウト疑似クラスを単独で使用すると、探している入力ではなく、テキストとターゲット入力の間にあるいくつかの空の要素を取得する可能性が高くなります。
レイアウト疑似クラスは、要素の距離と相対位置を計算するために、bounding client rect を使用します。
:right-of(div > button)
- 内側のセレクターにマッチングする任意の要素の右側にある要素に、任意の垂直位置でマッチングします。:left-of(div > button)
- 内側のセレクターにマッチングする任意の要素の左側にある要素に、任意の垂直位置でマッチングします。:above(div > button)
- 内側のセレクターにマッチングする任意の要素の上にある要素に、任意の水平位置でマッチングします。:below(div > button)
- 内側のセレクターにマッチングする任意の要素の下にある要素に、任意の水平位置でマッチングします。:near(div > button)
- 内側のセレクターにマッチングする任意の要素の近く (50 CSS ピクセル以内) にある要素にマッチングします。
結果のマッチングは、アンカー要素までの距離でソートされるため、Locator.First を使用して最も近いものを選択できます。これは、リストのような類似した要素が多数あり、最も近いものが明らかに正しいものである場合にのみ役立ちます。ただし、他のケースで Locator.First を使用しても、ほとんどの場合、期待どおりには機能しません。検索している要素ではなく、ランダムな空の <div>
のような最も近い要素、またはスクロールアウトされ、現在表示されていない要素をターゲットにしません。
// Fill an input to the right of "Username".
await page.Locator("input:right-of(:text(\"Username\"))").FillAsync("value");
// Click a button near the promo card.
await page.Locator("button:near(.promo-card)").ClickAsync();
// Click the radio input in the list closest to the "Label 3".
await page.Locator("[type=radio]:left-of(:text(\"Label 3\"))").First.ClickAsync();
すべてのレイアウト疑似クラスは、最後の引数としてオプションの最大ピクセル距離をサポートしています。たとえば、button:near(:text("Username"), 120)
は、テキスト "Username" を持つ要素から最大 120 CSS ピクセル離れた場所にあるボタンにマッチングします。
CSS: クエリ結果から n 番目のマッチングを選択
通常、ページの変化に強い属性またはテキストコンテンツによって要素を区別できます。
ページには多数の類似した要素が含まれている場合があり、特定の要素を選択するのが難しい場合があります。例えば
<section> <button>Buy</button> </section>
<article><div> <button>Buy</button> </div></article>
<div><div> <button>Buy</button> </div></div>
この場合、:nth-match(:text("Buy"), 3)
は上記のスニペットから 3 番目のボタンを選択します。インデックスは 1 から始まることに注意してください。
// Click the third "Buy" button
await page.Locator(":nth-match(:text('Buy'), 3)").ClickAsync();
:nth-match()
は、Locator.WaitForAsync() を使用して、指定された数の要素が表示されるまで待機するのにも役立ちます。
// Wait until all three buttons are visible
await page.Locator(":nth-match(:text('Buy'), 3)").WaitForAsync();
:nth-child()
とは異なり、要素は兄弟である必要はなく、ページのどこにあってもかまいません。上記のスニペットでは、3 つのボタンすべてが :text("Buy")
セレクターにマッチングし、:nth-match()
は 3 番目のボタンを選択します。
n 番目の要素ロケーター
クエリを n 番目のマッチングに絞り込むには、0 から始まるインデックスを渡す nth=
ロケーターを使用できます。
// Click first button
await page.Locator("button").Locator("nth=0").ClickAsync();
// Click last button
await page.Locator("button").Locator("nth=-1").ClickAsync();
親要素ロケーター
他の要素の親要素をターゲットにする必要がある場合、ほとんどの場合、子ロケーターで Locator.Filter() を行う必要があります。たとえば、次の DOM 構造を考えてみましょう。
<li><label>Hello</label></li>
<li><label>World</label></li>
テキスト "Hello"
を持つラベルの親 <li>
をターゲットにする場合は、Locator.Filter() を使用するのが最適です。
var child = page.GetByText("Hello");
var parent = page.GetByRole(AriaRole.Listitem).Filter(new () { Has = child });
または、親要素に適したロケーターが見つからない場合は、xpath=..
を使用します。DOM 構造への変更によってテストが中断されるため、この方法は信頼性が低いことに注意してください。可能な場合は Locator.Filter() を優先してください。
var parent = page.GetByText("Hello").Locator("xpath=..");
React ロケーター
React ロケーターは実験的であり、_
がプレフィックスとして付いています。機能は将来変更される可能性があります。
React ロケーターを使用すると、コンポーネント名とプロパティ値で要素を見つけることができます。構文は CSS 属性セレクター と非常によく似ており、すべての CSS 属性セレクター演算子をサポートしています。
React ロケーターでは、コンポーネント名はキャメルケースで記述されます。
await page.Locator("_react=BookItem").ClickAsync();
その他の例
- コンポーネントでマッチング:
_react=BookItem
- コンポーネントと正確なプロパティ値でマッチング (大文字と小文字を区別):
_react=BookItem[author = "Steven King"]
- プロパティ値のみでマッチング (大文字と小文字を区別しない):
_react=[author = "steven king" i]
- コンポーネントと真偽値のプロパティ値でマッチング:
_react=MyButton[enabled]
- コンポーネントとブール値でマッチング:
_react=MyButton[enabled = false]
- プロパティ値の部分文字列でマッチング:
_react=[author *= "King"]
- コンポーネントと複数のプロパティでマッチング:
_react=BookItem[author *= "king" i][year = 1990]
- ネストされたプロパティ値でマッチング:
_react=[some.nested.value = 12]
- コンポーネントとプロパティ値プレフィックスでマッチング:
_react=BookItem[author ^= "Steven"]
- コンポーネントとプロパティ値サフィックスでマッチング:
_react=BookItem[author $= "Steven"]
- コンポーネントとキーでマッチング:
_react=BookItem[key = '2']
- プロパティ値正規表現でマッチング:
_react=[author = /Steven(\\s+King)?/i]
ツリー内の React 要素名を見つけるには、React DevTools を使用してください。
React ロケーターは、React 15 以降をサポートしています。
React ロケーターと React DevTools は、minify されていないアプリケーションビルドに対してのみ機能します。
Vue ロケーター
Vue ロケーターは実験的であり、_
がプレフィックスとして付いています。機能は将来変更される可能性があります。
Vue ロケーターを使用すると、コンポーネント名とプロパティ値で要素を見つけることができます。構文は CSS 属性セレクター と非常によく似ており、すべての CSS 属性セレクター演算子をサポートしています。
Vue ロケーターでは、コンポーネント名はケバブケースで記述されます。
await page.Locator("_vue=book-item").ClickAsync();
その他の例
- コンポーネントでマッチング:
_vue=book-item
- コンポーネントと正確なプロパティ値でマッチング (大文字と小文字を区別):
_vue=book-item[author = "Steven King"]
- プロパティ値のみでマッチング (大文字と小文字を区別しない):
_vue=[author = "steven king" i]
- コンポーネントと真偽値のプロパティ値でマッチング:
_vue=my-button[enabled]
- コンポーネントとブール値でマッチング:
_vue=my-button[enabled = false]
- プロパティ値の部分文字列でマッチング:
_vue=[author *= "King"]
- コンポーネントと複数のプロパティでマッチング:
_vue=book-item[author *= "king" i][year = 1990]
- ネストされたプロパティ値でマッチング:
_vue=[some.nested.value = 12]
- コンポーネントとプロパティ値プレフィックスでマッチング:
_vue=book-item[author ^= "Steven"]
- コンポーネントとプロパティ値サフィックスでマッチング:
_vue=book-item[author $= "Steven"]
- プロパティ値正規表現でマッチング:
_vue=[author = /Steven(\\s+King)?/i]
ツリー内の Vue 要素名を見つけるには、Vue DevTools を使用してください。
Vue ロケーターは、Vue2 以降をサポートしています。
Vue ロケーターと Vue DevTools は、minify されていないアプリケーションビルドに対してのみ機能します。
XPath ロケーター
ページが変更されたときに壊れやすく、実装に結びついた XPath を使用する代わりに、テキストやアクセシブルなロールのようなユーザーに見えるロケーターを優先することをお勧めします。
XPath ロケーターは、Document.evaluate
を呼び出すことと同等です。
await page.Locator("xpath=//button").ClickAsync();
//
または ..
で始まるセレクタ文字列は、xpath セレクターと見なされます。たとえば、Playwright は '//html/body'
を 'xpath=//html/body'
に変換します。
XPath はシャドウルートを貫通しません。
XPath ユニオン
パイプ演算子 (|
) は、XPath で複数のセレクターを指定するために使用できます。リスト内のセレクターの 1 つによって選択できるすべての要素にマッチングします。
// Waits for either confirmation dialog or load spinner.
await page.Locator("//span[contains(@class, 'spinner__loading')]|//div[@id='confirmation']").WaitForAsync();
ラベルからフォームコントロールへのリターゲティング
ラベルからコントロールへのリターゲティングに依存する代わりに、ラベルテキストでロケートすることをお勧めします。
Playwright のターゲット入力アクションは、ラベルとコントロールを自動的に区別するため、ラベルをターゲットにして関連付けられたコントロールでアクションを実行できます。
たとえば、次の DOM 構造を考えてみましょう。<label for="password">Password:</label><input id="password" type="password">
。 Page.GetByText() を使用して、その "Password" テキストでラベルをターゲットにできます。ただし、次のアクションはラベルではなく入力で実行されます。
- Locator.ClickAsync() はラベルをクリックし、自動的に入力フィールドにフォーカスを合わせます。
- Locator.FillAsync() は入力フィールドに入力します。
- Locator.InputValueAsync() は入力フィールドの値を返します。
- Locator.SelectTextAsync() は入力フィールド内のテキストを選択します。
- Locator.SetInputFilesAsync() は
type=file
の入力フィールドのファイルを設定します。 - Locator.SelectOptionAsync() はセレクトボックスからオプションを選択します。
// Fill the input by targeting the label.
await page.GetByText("Password").FillAsync("secret");
ただし、Expect(Locator).ToHaveTextAsync() などの他のメソッドは、ラベル自体をターゲットにします。入力フィールドではなく、ラベルのテキストコンテンツをアサートします。
// Fill the input by targeting the label.
await Expect(Page.Locator("label")).ToHaveTextAsync("Password");
レガシーテキストロケーター
代わりに、最新のテキストロケーターをお勧めします。
レガシーテキストロケーターは、渡されたテキストを含む要素にマッチングします。
await page.Locator("text=Log in").ClickAsync();
レガシーテキストロケーターにはいくつかのバリエーションがあります。
-
text=Log in
- デフォルトのマッチングは大文字と小文字を区別せず、空白をトリムし、部分文字列を検索します。たとえば、text=Log
は<button>Log in</button>
にマッチングします。await page.Locator("text=Log in").ClickAsync();
-
text="Log in"
- テキスト本文は、単一引用符または二重引用符でエスケープして、空白をトリムした後の正確なコンテンツを持つテキストノードを検索できます。たとえば、
text="Log"
は<button>Log in</button>
にマッチングしません。なぜなら、<button>
には"Log"
と等しくない単一のテキストノード"Log in"
が含まれているからです。ただし、text="Log"
は<button> Log <span>in</span></button>
にマッチングします。なぜなら、<button>
にはテキストノード" Log "
が含まれているからです。この正確なモードは、大文字と小文字を区別するマッチングを意味するため、text="Download"
は<button>download</button>
にマッチングしません。引用符で囲まれた本文は、通常のエスケープルールに従います。たとえば、二重引用符で囲まれた文字列で二重引用符をエスケープするには
\"
を使用します。text="foo\"bar"
。await page.Locator("text='Log in'").ClickAsync();
-
/Log\s*in/i
- 本文は/
記号で囲まれた JavaScript のような正規表現 にすることができます。たとえば、text=/Log\s*in/i
は<button>Login</button>
および<button>log IN</button>
にマッチングします。await page.Locator("text=/Log\\s*in/i").ClickAsync();
引用符 ("
または '
) で始まり、引用符で終わる文字列セレクターは、レガシーテキストロケーターと見なされます。たとえば、"Log in"
は内部的に text="Log in"
に変換されます。
マッチングは常に空白を正規化します。たとえば、複数のスペースを 1 つに変換し、改行をスペースに変換し、先頭と末尾の空白を無視します。
button
および submit
タイプの入力要素は、テキストコンテンツの代わりに value
でマッチングされます。たとえば、text=Log in
は <input type=button value="Log in">
にマッチングします。
id, data-testid, data-test-id, data-test セレクター
代わりに、テスト ID でロケートすることをお勧めします。
Playwright は、特定の属性を使用して要素を選択するためのショートカットをサポートしています。現在、次の属性のみがサポートされています。
id
data-testid
data-test-id
data-test
// Fill an input with the id "username"
await page.Locator("id=username").FillAsync("value");
// Click an element with data-test-id "submit"
await page.Locator("data-test-id=submit").ClickAsync();
属性セレクターは CSS セレクターではないため、:enabled
のような CSS 固有のものはサポートされていません。より多くの機能については、適切な css セレクターを使用してください。たとえば、css=[data-test="login"]:enabled
。
セレクターのチェーニング
代わりに、ロケーター内でのマッチングをお勧めします。
engine=body
または短縮形で定義されたセレクターは、>>
トークンと組み合わせることができます。たとえば、selector1 >> selector2 >> selectors3
。セレクターがチェーンされると、次のセレクターは前のセレクターの結果を基準にクエリされます。
例えば、
css=article >> css=.bar > .baz >> css=span[attr=value]
は、次と同等です。
document
.querySelector('article')
.querySelector('.bar > .baz')
.querySelector('span[attr=value]');
セレクターが本文に >>
を含める必要がある場合は、チェーニングセパレーターと混同されないように、文字列内でエスケープする必要があります。たとえば、text="some >> text"
。
中間一致
他の要素を含む要素をロケートするには、別のロケーターでフィルタリングすることをお勧めします。
デフォルトでは、チェーンされたセレクターは、最後のセレクターによってクエリされた要素に解決されます。セレクターには、中間セレクターによってクエリされた要素をキャプチャするために、*
をプレフィックスとして付けることができます。
たとえば、css=article >> text=Hello
はテキスト Hello
を持つ要素をキャプチャし、*css=article >> text=Hello
(*
に注意) は、テキスト Hello
を持つ要素を含む article
要素をキャプチャします。