その他のロケーター
はじめに
最も一般的で推奨されるロケーターについては、主要なロケーターガイドをご確認ください。
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 in」という単一のテキストノードを含んでおり、それが「Log」と等しくないからです。しかし、: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つのボタンがあるページを考えてみましょう。1つ目は非表示で、2つ目は表示されています。
<button style='display: none'>Invisible</button>
<button>Visible</button>
-
これは両方のボタンを見つけ、strictness違反エラーをスローします。
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: いずれかの条件にマッチする要素
カンマ区切りのCSSセレクターリストは、そのリスト内のいずれかのセレクターで選択できるすべての要素にマッチします。
// 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"))
のようにレイアウト擬似クラスを単独で使用すると、探している入力ではなく、テキストとターゲット入力の間に存在する空の要素がマッチしてしまう可能性が高いです。
レイアウト擬似クラスは、getBoundingClientRect
を使用して要素の距離と相対位置を計算します。
:right-of(div > button)
- 内部セレクターにマッチする要素の右側にあり、任意の垂直位置にある要素にマッチします。:left-of(div > button)
- 内部セレクターにマッチする要素の左側にあり、任意の垂直位置にある要素にマッチします。:above(div > button)
- 内部セレクターにマッチする要素のいずれかの上方にあり、任意の水平位置にある要素にマッチします。:below(div > button)
- 内部セレクターにマッチする要素のいずれかの下方にあり、任意の水平位置にある要素にマッチします。:near(div > button)
- 内部セレクターにマッチする要素のいずれかに近い(50CSSピクセル以内)要素にマッチします。
結果として得られるマッチはアンカー要素からの距離によってソートされるため、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番目要素ロケーター
0ベースのインデックスを渡すことで、nth=
ロケーターを使用してクエリをn番目のマッチに絞り込むことができます。
// 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ロケーターでは、コンポーネント名はCamelCaseで表記されます。
await page.Locator("_react=BookItem").ClickAsync();
その他の例
- コンポーネントでマッチ:
_react=BookItem
- コンポーネントと正確なプロパティ値でマッチ(大文字と小文字を区別):
_react=BookItem[author = "Steven King"]
- プロパティ値のみでマッチ(大文字と小文字を区別しない):
_react=[author = "steven king" i]
- コンポーネントとtruthyプロパティ値でマッチ:
_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と同様に、ミニファイされていないアプリケーションビルドに対してのみ機能します。
Vueロケーター
Vueロケーターは実験的であり、_
がプレフィックスとして付きます。機能は将来変更される可能性があります。
Vueロケーターを使用すると、コンポーネント名とプロパティ値で要素を検索できます。構文はCSS属性セレクターと非常に似ており、すべてのCSS属性セレクター演算子をサポートしています。
Vueロケーターでは、コンポーネント名はkebab-caseで表記されます。
await page.Locator("_vue=book-item").ClickAsync();
その他の例
- コンポーネントでマッチ:
_vue=book-item
- コンポーネントと正確なプロパティ値でマッチ(大文字と小文字を区別):
_vue=book-item[author = "Steven King"]
- プロパティ値のみでマッチ(大文字と小文字を区別しない):
_vue=[author = "steven king" i]
- コンポーネントとtruthyプロパティ値でマッチ:
_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と同様に、ミニファイされていないアプリケーションビルドに対してのみ機能します。
XPathロケーター
実装に依存し、ページの変更時に容易に壊れるXPathを使用する代わりに、テキストやアクセシブルなロールのようなユーザーに見えるロケーターを優先することをお勧めします。
XPathロケーターはDocument.evaluate
の呼び出しと同等です。
await page.Locator("xpath=//button").ClickAsync();
//
または..
で始まるセレクター文字列はXPathセレクターとみなされます。例えば、Playwrightは'//html/body'
を'xpath=//html/body'
に変換します。
XPathはシャドウルートを貫通しません。
XPathユニオン
パイプ演算子 (|
) は、XPathで複数のセレクターを指定するために使用できます。これにより、リスト内のいずれかのセレクターで選択できるすべての要素にマッチします。
// 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">
。あなたは「Password」というテキストを持つラベルをPage.GetByText()を使用してターゲットにできます。しかし、以下の操作はラベルではなく入力フィールドに対して実行されます。
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 in"
を含んでおり、それが"Log"
と等しくないからです。しかし、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
要素を捕捉します。