Selectors quick fix (#18742)

This commit is contained in:
Debbie O'Brien 2022-11-14 15:23:25 +01:00 committed by GitHub
parent 8e882fdd58
commit 25605c616c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -5,215 +5,9 @@ title: "Selectors"
Selectors are strings that are used to create [Locator]s. Locators are used to perform actions on the elements by means of methods such as [`method: Locator.click`], [`method: Locator.fill`] and alike. For debugging selectors, see [here](./debug-selectors).
Writing good selectors is part art, part science so be sure to checkout the [Best Practices](#best-practices) section.
## Quick guide
- Text selector
```js
await page.locator('text=Log in').click();
```
```java
page.locator("text=Log in").click();
```
```python async
await page.locator("text=Log in").click()
```
```python sync
page.locator("text=Log in").click()
```
```csharp
await page.Locator("text=Log in").ClickAsync();
```
Learn more about [text selector][text].
- CSS selector
```js
await page.locator('button').click();
await page.locator('#nav-bar .contact-us-item').click();
```
```java
page.locator("button").click();
page.locator("#nav-bar .contact-us-item").click();
```
```python async
await page.locator("button").click()
await page.locator("#nav-bar .contact-us-item").click()
```
```python sync
page.locator("button").click()
page.locator("#nav-bar .contact-us-item").click()
```
```csharp
await page.Locator("button").ClickAsync();
await page.Locator("#nav-bar .contact-us-item").ClickAsync();
```
Learn more about [css selector][css].
- Select by attribute, with css selector
```js
await page.locator('[data-test=login-button]').click();
await page.locator('[aria-label="Sign in"]').click();
```
```java
page.locator("[data-test=login-button]").click();
page.locator("[aria-label='Sign in']").click();
```
```python async
await page.locator("[data-test=login-button]").click()
await page.locator("[aria-label='Sign in']").click()
```
```python sync
page.locator("[data-test=login-button]").click()
page.locator("[aria-label='Sign in']").click()
```
```csharp
await page.Locator("[data-test=login-button]").ClickAsync();
await page.Locator("[aria-label='Sign in']").ClickAsync();
```
Learn more about [css selector][css].
- Combine css and text selectors
```js
await page.locator('article:has-text("Playwright")').click();
await page.locator('#nav-bar >> text=Contact Us').click();
```
```java
page.locator("article:has-text(\"Playwright\")").click();
page.locator("#nav-bar :text(\"Contact us\")").click();
```
```python async
await page.locator("article:has-text('Playwright')").click()
await page.locator("#nav-bar :text('Contact us')").click()
```
```python sync
page.locator("article:has-text('Playwright')").click()
page.locator("#nav-bar :text('Contact us')").click()
```
```csharp
await page.Locator("article:has-text(\"Playwright\")").ClickAsync();
await page.Locator("#nav-bar :text(\"Contact us\")").ClickAsync();
```
Learn more about [`:has-text()` and `:text()` pseudo classes][text].
- Element that contains another, with css selector
```js
await page.locator('.item-description:has(.item-promo-banner)').click();
```
```java
page.locator(".item-description:has(.item-promo-banner)").click();
```
```python async
await page.locator(".item-description:has(.item-promo-banner)").click()
```
```python sync
page.locator(".item-description:has(.item-promo-banner)").click()
```
```csharp
await page.Locator(".item-description:has(.item-promo-banner)").ClickAsync();
```
Learn more about [`:has()` pseudo class](#selecting-elements-that-contain-other-elements).
- Selecting based on layout, with css selector
```js
await page.locator('input:right-of(:text("Username"))').click();
```
```java
page.locator("input:right-of(:text(\"Username\"))").click();
```
```python async
await page.locator("input:right-of(:text('Username'))").click()
```
```python sync
page.locator("input:right-of(:text('Username'))").click()
```
```csharp
await page.Locator("input:right-of(:text(\"Username\"))").ClickAsync();
```
Learn more about [layout selectors](#selecting-elements-based-on-layout).
- Only visible elements, with css selector
```js
await page.locator('.login-button:visible').click();
```
```java
page.locator(".login-button:visible").click();
```
```python async
await page.locator(".login-button:visible").click()
```
```python sync
page.locator(".login-button:visible").click()
```
```csharp
await page.Locator(".login-button:visible").ClickAsync();
```
Learn more about [selecting visible elements](#selecting-visible-elements).
- Pick n-th match
```js
await page.locator(':nth-match(:text("Buy"), 3)').click();
```
```java
page.locator(":nth-match(:text('Buy'), 3)").click();
```
```python async
await page.locator(":nth-match(:text('Buy'), 3)").click()
```
```python sync
page.locator(":nth-match(:text('Buy'), 3)").click()
```
```csharp
await page.Locator(":nth-match(:text('Buy'), 3)").ClickAsync();
```
Learn more about [`:nth-match()` pseudo-class](#pick-n-th-match-from-the-query-result).
- XPath selector
```js
await page.locator('xpath=//button').click();
```
```java
page.locator("xpath=//button").click();
```
```python async
await page.locator("xpath=//button").click()
```
```python sync
page.locator("xpath=//button").click()
```
```csharp
await page.Locator("xpath=//button").ClickAsync();
```
Learn more about [XPath selector][xpath].
- React selector (experimental)
```js
await page.locator('_react=ListItem[text *= "milk" i]').click();
```
```java
page.locator("_react=ListItem[text *= 'milk' i]").click();
```
```python async
await page.locator("_react=ListItem[text *= 'milk' i]").click()
```
```python sync
page.locator("_react=ListItem[text *= 'milk' i]").click()
```
```csharp
await page.Locator("_react=ListItem[text *= 'milk' i]").ClickAsync();
```
Learn more about [React selectors][react].
- Vue selector (experimental)
```js
await page.locator('_vue=list-item[text *= "milk" i]').click();
```
```java
page.locator("_vue=list-item[text *= 'milk' i]").click();
```
```python async
await page.locator("_vue=list-item[text *= 'milk' i]").click()
```
```python sync
page.locator("_vue=list-item[text *= 'milk' i]").click()
```
```csharp
await page.Locator("_vue=list-item[text *= 'milk' i]").ClickAsync();
```
Learn more about [Vue selectors][vue].
:::info
Check out our [locators guide](./locators) for more information on our new locators API.
:::
## Text selector
Text selector locates elements that contain passed text.
@ -1197,187 +991,6 @@ By default, chained selectors resolve to an element queried by the last selector
For example, `css=article >> text=Hello` captures the element with the text `Hello`, and `*css=article >> text=Hello` (note the `*`) captures the `article` element that contains some element with the text `Hello`.
## Best practices
The choice of selectors determines the resiliency of automation scripts. To reduce the maintenance burden, we recommend prioritizing user-facing attributes and explicit contracts.
### Prioritize user-facing attributes
Attributes like text content, input placeholder, accessibility roles and labels are user-facing attributes that change rarely. These attributes are not impacted by DOM structure changes.
The following examples use the built-in [text] and [css] selector engines.
```js
// queries "Login" text selector
await page.locator('text="Login"').click();
await page.locator('"Login"').click(); // short-form
// queries "Search GitHub" placeholder attribute
await page.locator('css=[placeholder="Search GitHub"]').fill('query');
await page.locator('[placeholder="Search GitHub"]').fill('query'); // short-form
// queries "Close" accessibility label
await page.locator('css=[aria-label="Close"]').click();
await page.locator('[aria-label="Close"]').click(); // short-form
// combine role and text queries
await page.locator('css=nav >> text=Login').click();
```
```java
// queries "Login" text selector
page.locator("text=\"Login\"").click();
page.locator("\"Login\"").click(); // short-form
// queries "Search GitHub" placeholder attribute
page.locator("css=[placeholder='Search GitHub']").fill("query");
page.locator("[placeholder='Search GitHub']").fill("query"); // short-form
// queries "Close" accessibility label
page.locator("css=[aria-label='Close']").click();
page.locator("[aria-label='Close']").click(); // short-form
// combine role and text queries
page.locator("css=nav >> text=Login").click();
```
```python async
# queries "Login" text selector
await page.locator('text="Login"').click()
await page.locator('"Login"').click() # short-form
# queries "Search GitHub" placeholder attribute
await page.locator('css=[placeholder="Search GitHub"]').fill('query')
await page.locator('[placeholder="Search GitHub"]').fill('query') # short-form
# queries "Close" accessibility label
await page.locator('css=[aria-label="Close"]').click()
await page.locator('[aria-label="Close"]').click() # short-form
# combine role and text queries
await page.locator('css=nav >> text=Login').click()
```
```python sync
# queries "Login" text selector
page.locator('text="Login"').click()
page.locator('"Login"').click() # short-form
# queries "Search GitHub" placeholder attribute
page.locator('css=[placeholder="Search GitHub"]').fill('query')
page.locator('[placeholder="Search GitHub"]').fill('query') # short-form
# queries "Close" accessibility label
page.locator('css=[aria-label="Close"]').click()
page.locator('[aria-label="Close"]').click() # short-form
# combine role and text queries
page.locator('css=nav >> text=Login').click()
```
```csharp
// queries "Login" text selector
await page.Locator("text=\"Login\"").ClickAsync();
await page.Locator("\"Login\"").ClickAsync(); // short-form
// queries "Search GitHub" placeholder attribute
await page.Locator("css=[placeholder='Search GitHub']").FillAsync("query");
await page.Locator("[placeholder='Search GitHub']").FillAsync("query"); // short-form
// queries "Close" accessibility label
await page.Locator("css=[aria-label='Close']").ClickAsync();
await page.Locator("[aria-label='Close']").ClickAsync(); // short-form
// combine role and text queries
await page.Locator("css=nav >> text=Login").ClickAsync();
```
### Define explicit contract
When user-facing attributes change frequently, it is recommended to use explicit test ids, like `data-test-id`. These `data-*` attributes are supported by the [css] and [id selectors][id].
```html
<button data-test-id="directions">Itinéraire</button>
```
```js
// queries data-test-id attribute with css
await page.locator('css=[data-test-id=directions]').click();
await page.locator('[data-test-id=directions]').click(); // short-form
// queries data-test-id with id
await page.locator('data-test-id=directions').click();
```
```java
// queries data-test-id attribute with css
page.locator("css=[data-test-id=directions]").click();
page.locator("[data-test-id=directions]").click(); // short-form
// queries data-test-id with id
page.locator("data-test-id=directions").click();
```
```python async
# queries data-test-id attribute with css
await page.locator('css=[data-test-id=directions]').click()
await page.locator('[data-test-id=directions]').click() # short-form
# queries data-test-id with id
await page.locator('data-test-id=directions').click()
```
```python sync
# queries data-test-id attribute with css
page.locator('css=[data-test-id=directions]').click()
page.locator('[data-test-id=directions]').click() # short-form
# queries data-test-id with id
page.locator('data-test-id=directions').click()
```
```csharp
// queries data-test-id attribute with css
await page.Locator("css=[data-test-id=directions]").ClickAsync();
await page.Locator("[data-test-id=directions]").ClickAsync(); // short-form
// queries data-test-id with id
await page.Locator("data-test-id=directions").ClickAsync();
```
### Avoid selectors tied to implementation
[xpath] and [css] can be tied to the DOM structure or implementation. These selectors can break when
the DOM structure changes. Similarly, [`method: Locator.nth`], [`method: Locator.first`], and [`method: Locator.last`] are tied to implementation and the structure of the DOM, and will target the incorrect element if the DOM changes.
```js
// avoid long css or xpath chains
await page.locator('#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input').click();
await page.locator('//*[@id="tsf"]/div[2]/div[1]/div[1]/div/div[2]/input').click();
```
```java
// avoid long css or xpath chains
page.locator("#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input").click();
page.locator("//*[@id='tsf']/div[2]/div[1]/div[1]/div/div[2]/input").click();
```
```python async
# avoid long css or xpath chains
await page.locator('#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input').click()
await page.locator('//*[@id="tsf"]/div[2]/div[1]/div[1]/div/div[2]/input').click()
```
```python sync
# avoid long css or xpath chains
page.locator('#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input').click()
page.locator('//*[@id="tsf"]/div[2]/div[1]/div[1]/div/div[2]/input').click()
```
```csharp
// avoid long css or xpath chains
await page.Locator("#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input").ClickAsync();
await page.Locator("//*[@id='tsf']/div[2]/div[1]/div[1]/div/div[2]/input").ClickAsync();
```
[text]: #text-selector
[css]: #css-selector