diff --git a/docs/src/selectors.md b/docs/src/selectors.md index 3a5a96e81e..f0dba4fdd9 100644 --- a/docs/src/selectors.md +++ b/docs/src/selectors.md @@ -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 - -``` - -```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