その他のロケーター
はじめに
最も一般的で推奨されるロケーターについては、メインのロケーターガイドを参照してください。
推奨されるロケーターであるpage.getByRole()やpage.getByText()に加えて、Playwrightはこのガイドで説明されているさまざまな他のロケーターをサポートしています。
CSSロケーター
実装に強く結びついており、ページの変更時に壊れる可能性のあるCSSを使用する代わりに、テキストやアクセシブルロールのようなユーザーに見えるロケーターを優先することをお勧めします。
PlaywrightはCSSセレクターで要素を特定できます。
await page.locator('css=button').click();
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")`は`
`にマッチします。Playwright`:has-text()`は他のCSSセレクターと組み合わせて使用する必要があることに注意してください。そうしないと、`
`を含む指定されたテキストを含むすべての要素にマッチしてしまいます。// Wrong, will match many elements including <body>
await page.locator(':has-text("Playwright")').click();
// Correct, only matches the <article> element
await page.locator('article:has-text("Playwright")').click(); -
`#nav-bar :text("Home")` - `:text()`擬似クラスは、指定されたテキストを含む最小の要素にマッチします。マッチングは大文字と小文字を区別せず、空白をトリムし、部分文字列を検索します。
例えば、これは`#nav-bar`要素のどこかに「Home」というテキストを持つ要素を見つけます。
await page.locator('#nav-bar :text("Home")').click();
-
`#nav-bar :text-is("Home")` - `:text-is()`擬似クラスは、正確なテキストを持つ最小の要素にマッチします。厳密なマッチングは大文字と小文字を区別し、空白をトリムし、完全な文字列を検索します。
例えば、`:text-is("Log")`は``にはマッチしません。なぜなら、`
同様に、`:text-is("Download")`は`
download `にはマッチしません。なぜなら、大文字と小文字を区別するからです。
-
`#nav-bar :text-matches("reg?ex", "i")` - `:text-matches()`擬似クラスは、JavaScriptのような正規表現にマッチするテキストコンテンツを持つ最小の要素にマッチします。
例えば、`:text-matches("Log\s*in", "i")`は`
Login `と`log IN `にマッチします。
テキストのマッチングは常に空白を正規化します。例えば、複数のスペースを1つに、改行をスペースに変換し、前後の空白を無視します。
`button`および`submit`タイプの入力要素は、テキストコンテンツの代わりにその`value`によってマッチされます。例えば、`:text("Log in")`は``にマッチします。
CSS: 可視要素のみにマッチング
PlaywrightはCSSセレクターで`:visible`擬似クラスをサポートしています。例えば、`css=button`はページ上のすべてのボタンにマッチしますが、`css=button:visible`は可視のボタンにのみマッチします。これは、非常に似ているが可視性が異なる要素を区別するのに役立ちます。
最初のボタンが不可視で、2番目のボタンが可視であるページを考えてみましょう。
<button style='display: none'>Invisible</button>
<button>Visible</button>
-
これは両方のボタンを見つけ、厳密性の違反エラーをスローします。
await page.locator('button').click();
-
これは2番目のボタンのみを見つけ、それが可視であるためクリックします。
await page.locator('button:visible').click();
CSS: 他の要素を含む要素
`:has()`擬似クラスは実験的なCSS擬似クラスです。これは、指定された要素の`:scope`に相対的なパラメータとして渡されたセレクターのいずれかが少なくとも1つの要素にマッチする場合に、その要素を返します。
以下のスニペットは、内部に`
await page.locator('article:has(div.promo)').textContent();
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")').click();
`: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()を使用しても、期待通りに動作しない可能性が高く、探している要素ではなく、ランダムな空の`
// Fill an input to the right of "Username".
await page.locator('input:right-of(:text("Username"))').fill('value');
// Click a button near the promo card.
await page.locator('button:near(.promo-card)').click();
// Click the radio input in the list closest to the "Label 3".
await page.locator('[type=radio]:left-of(:text("Label 3"))').first().click();
すべてのレイアウト擬似クラスは、最後の引数としてオプションの最大ピクセル距離をサポートしています。例えば`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)').click();
`:nth-match()`は、locator.waitFor()を使用して、指定された数の要素が出現するまで待機するのにも役立ちます。
// Wait until all three buttons are visible
await page.locator(':nth-match(:text("Buy"), 3)').waitFor();
`:nth-child()`とは異なり、要素は兄弟である必要はなく、ページのどこにでも存在できます。上記のコードスニペットでは、3つのボタンすべてが`:text("Buy")`セレクターにマッチし、`:nth-match()`が3番目のボタンを選択します。
N番目の要素ロケーター
ゼロベースのインデックスを渡す`nth=`ロケーターを使用して、クエリをn番目のマッチに絞り込むことができます。
// Click first button
await page.locator('button').locator('nth=0').click();
// Click last button
await page.locator('button').locator('nth=-1').click();
親要素ロケーター
他の要素の親要素をターゲットにする必要がある場合、ほとんどの場合、子ロケーターによってlocator.filter()を使用すべきです。例えば、次のDOM構造を考えてみましょう
<li><label>Hello</label></li>
<li><label>World</label></li>
テキスト「Hello」を持つラベルの親`
const child = page.getByText('Hello');
const parent = page.getByRole('listitem').filter({ has: child });
あるいは、親要素に適したロケーターが見つからない場合は、`xpath=..`を使用してください。この方法は、DOM構造の変更がテストを壊す可能性があるため、あまり信頼できないことに注意してください。可能な場合はlocator.filter()を優先してください。
const parent = page.getByText('Hello').locator('xpath=..');
Reactロケーター
Reactロケーターは実験的であり、`_`がプレフィックスとして付加されます。将来的に機能が変更される可能性があります。
Reactロケーターは、コンポーネント名とプロパティ値によって要素を見つけることができます。構文はCSS属性セレクターと非常によく似ており、すべてのCSS属性セレクター演算子をサポートしています。
Reactロケーターでは、コンポーネント名は**CamelCase**で表記されます。
await page.locator('_react=BookItem').click();
その他の例
- **コンポーネント**でマッチ: `_react=BookItem`
- コンポーネントと**厳密なプロパティ値**でマッチ、大文字小文字を区別: `_react=BookItem[author = "Steven King"]`
- プロパティ値のみでマッチ、**大文字小文字を区別しない**: `_react=[author = "steven king" i]`
- コンポーネントと**truthyなプロパティ値**でマッチ: `_react=MyButton[enabled]`
- コンポーネントと**boolean値**でマッチ: `_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ロケーターでは、コンポーネント名は**ケバブケース**で表記されます。
await page.locator('_vue=book-item').click();
その他の例
- **コンポーネント**でマッチ: `_vue=book-item`
- コンポーネントと**厳密なプロパティ値**でマッチ、大文字小文字を区別: `_vue=book-item[author = "Steven King"]`
- プロパティ値のみでマッチ、**大文字小文字を区別しない**: `_vue=[author = "steven king" i]`
- コンポーネントと**truthyなプロパティ値**でマッチ: `_vue=my-button[enabled]`
- コンポーネントと**boolean値**でマッチ: `_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').click();
`//`または`..`で始まるセレクター文字列は、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']`
).waitFor();
ラベルからフォームコントロールへのリターゲット
ラベルからコントロールへのリターゲットに頼るのではなく、ラベルテキストによる特定を推奨します。
Playwrightのターゲット入力アクションは、ラベルとコントロールを自動的に区別するため、ラベルをターゲットにして関連するコントロールに対するアクションを実行できます。
例えば、次のDOM構造を考えてみましょう: `<label for="password">Password:</label><input id="password" type="password">`。このラベルをpage.getByText()を使用して「Password」テキストでターゲットにすることができます。しかし、以下の操作はラベルではなく入力に対して実行されます。
- locator.click()はラベルをクリックし、自動的に入力フィールドにフォーカスします;
- locator.fill()は入力フィールドに入力します;
- locator.inputValue()は入力フィールドの値を返します;
- locator.selectText()は入力フィールドのテキストを選択します;
- locator.setInputFiles()は`type=file`の入力フィールドにファイルをセットします;
- locator.selectOption()はセレクトボックスからオプションを選択します。
// Fill the input by targeting the label.
await page.getByText('Password').fill('secret');
しかし、他のメソッドはラベル自体をターゲットにします。例えば、expect(locator).toHaveText()は入力フィールドではなく、ラベルのテキストコンテンツをアサートします。
// Fill the input by targeting the label.
await expect(page.locator('label')).toHaveText('Password');
レガシーテキストロケーター
代わりに、最新のテキストロケーターを推奨します。
レガシーテキストロケーターは、渡されたテキストを含む要素にマッチします。
await page.locator('text=Log in').click();
レガシーテキストロケーターにはいくつかのバリエーションがあります
-
`text=Log in` - デフォルトのマッチングは大文字と小文字を区別せず、空白をトリムし、部分文字列を検索します。例えば、`text=Log`は`
Log in `にマッチします。await page.locator('text=Log in').click();
-
`text="Log in"` - テキスト本文は、単一引用符または二重引用符でエスケープして、空白をトリムした後の正確なコンテンツを持つテキストノードを検索できます。
例えば、`text="Log"`は`
Log in `にはマッチしません。なぜなら、``には「Log」とは等しくない単一のテキストノード「Log in」が含まれているからです。しかし、`text="Log"`は` Log in `にはマッチします。なぜなら、``にはテキストノード「 Log 」が含まれているからです。この厳密なモードは大文字と小文字を区別するマッチングを意味するため、`text="Download"`は` download `にはマッチしません。引用符で囲まれた本文は通常の_エスケープ_ルールに従います。例えば、二重引用符で囲まれた文字列内で二重引用符をエスケープするには`\"`を使用します: `text="foo\"bar"`。
await page.locator('text="Log in"').click();
-
`/Log\s*in/i` - 本文は、`/`記号で囲まれたJavaScriptのような正規表現であることができます。例えば、`text=/Log\s*in/i`は`
Login `と`log IN `にマッチします。await page.locator('text=/Log\\s*in/i').click();
引用符(`"`または`'`)で始まり、引用符で終わる文字列セレクターは、レガシーテキストロケーターと見なされます。例えば、`"Log in"`は内部で`text="Log in"`に変換されます。
マッチングは常に空白を正規化します。例えば、複数のスペースを1つに、改行をスペースに変換し、前後の空白を無視します。
`button`および`submit`タイプの入力要素は、テキストコンテンツの代わりにその`value`によってマッチされます。例えば、`text=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').fill('value');
// Click an element with data-test-id "submit"
await page.locator('data-test-id=submit').click();
属性セレクターは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`要素をキャプチャします。