diff --git a/docs/src/locators.md b/docs/src/locators.md index bfb456413864c..ada1193cbe3ea 100644 --- a/docs/src/locators.md +++ b/docs/src/locators.md @@ -77,6 +77,7 @@ For example, consider the following DOM structure. ```html ``` + Locate the element by its role of `button` with name "Sign in". ```js @@ -98,6 +99,10 @@ await page.GetByRole("button", new() { NameString = "Sign in" }) .ClickAsync(); ``` +```html + +``` + :::tip Use the [code generator](./codegen.md) to generate a locator, and then edit it as you'd like. ::: @@ -186,33 +191,31 @@ The [`method: Page.getByRole`] locator reflects how users and assistive technolo For example, consider the following DOM structure. ```html -

my form

-
- - - + + +
+ +
``` -form with newsletter checkbox that is checked and a submit button - You can locate each element by it's implicit role: ```js -await expect(page.getByRole('heading', { name: 'my form' })) +await expect(page.getByRole('heading', { name: 'Sign up' })) .toBeVisible() -await page.getByRole('checkbox', { checked: true, name: "newsletter" }) - .uncheck(); +await page.getByRole('checkbox', { name: 'Subscribe' }) + .check(); await page.getByRole('button', { name: /submit/i }) .click(); ``` ```python async -await expect(page.get_by_role("heading", name="my form")).to_be_visible() +await expect(page.get_by_role("heading", name="Sign up")).to_be_visible() -await page.get_by_role("checkbox", checked=True, name="newsletter").uncheck() +await page.get_by_role("checkbox", name="Subscribe").check() await page.get_by_role("button", name=re.compile("submit", re.IGNORECASE)).click() ``` @@ -220,34 +223,41 @@ await page.get_by_role("button", name=re.compile("submit", re.IGNORECASE)).click ```python sync expect(page.get_by_role("heading", name="my from")).to_be_visible() -page.get_by_role("checkbox", checked=True, name="newsletter").uncheck() +page.get_by_role("checkbox", name="Subscribe").check() page.get_by_role("button", name=re.compile("submit", re.IGNORECASE)).click() ``` ```java -assertThat(page.getByRole("heading", new Page.GetByRoleOptions().setName("my form"))) +assertThat(page.getByRole("heading", new Page.GetByRoleOptions().setName("Sign up"))) .isVisible(); -page.getByRole("checkbox", new Page.GetByRoleOptions().setChecked(true).setName("newsletter")) - .uncheck(); +page.getByRole("checkbox", new Page.GetByRoleOptions().setName("Subscribe")) + .check(); page.getByRole("button", new Page.GetByRoleOptions().setName(Pattern.compile("submit", Pattern.CASE_INSENSITIVE))) .click(); ``` ```csharp -await Expect(page.GetByRole("heading", new() { NameString = "my form" })) +await Expect(page.GetByRole("heading", new() { NameString = "Sign up" })) .ToBeVisibleAsync(); -await page.GetByRole("checkbox", new() { Checked = true, NameString = "newsletter" }) - .UncheckAsync(); +await page.GetByRole("checkbox", new() { NameString = "Subscribe" }) + .CheckAsync(); await page.GetByRole("button", new() { NameRegex = new Regex("submit", RegexOptions.IgnoreCase) }) .ClickAsync(); ``` -form with newsletter checkbox unchecked and submit button highlighted +```html +
+ + +
+ +
+``` Role locators include [buttons, checkboxes, headings, links, lists, tables, and many more](https://www.w3.org/TR/html-aria/#docconformance) and follow W3C specifications for [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles), [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). @@ -267,7 +277,6 @@ For example, consider the following DOM structure. ``` -password input with label of password You can fill the input after locating it by the label text: @@ -290,7 +299,11 @@ page.get_by_label("Password").fill("secret") ```csharp await page.GetByLabel("Password").FillAsync("secret"); ``` -password input with label and password filled in with encryption + +```html + + +``` :::tip When to use label locators Use this locator when locating form fields. @@ -302,11 +315,9 @@ Inputs may have a placeholder attribute to hint to the user what value should be For example, consider the following DOM structure. ```html - + ``` -input field filled in with name@example.com - You can fill the input after locating it by the placeholder text: ```js @@ -332,7 +343,9 @@ await page.GetByPlaceholder("name@example.com") .FillAsync("playwright@microsoft.com"); ``` -input field filled in with playwright@microsoft.com +```html + +``` :::tip When to use placeholder locators Use this locator when locating form elements that do not have labels but do have placeholder texts. @@ -347,7 +360,6 @@ For example, consider the following DOM structure. ```html Welcome, John ``` -Welcome, John You can locate the element by the text it contains: @@ -380,7 +392,6 @@ await expect(page.getByText('Welcome, John', { exact: true })) .toBeVisible(); ``` - ```java assertThat(page.getByText("Welcome, John", new Page.GetByTextOptions().setExact(true))) .isVisible(); @@ -437,16 +448,12 @@ You can also [filter by text](#filter-by-text) which can be useful when trying t All images should have an `alt` attribute that describes the image. You can locate an image based on the text alternative using [`method: Page.getByAltText`]. - For example, consider the following DOM structure. ```html playwright logo ``` -playwright logo - - You can click on the image after locating it by the text alternative: ```js @@ -472,8 +479,9 @@ await page.GetByAltText("playwright logo") .ClickAsync(); ``` -playwright logo being clicked - +```html +playwright logo +``` :::tip When to use alt locators Use this locator when your element supports alt text such as `img` and `area` elements. @@ -488,7 +496,6 @@ For example, consider the following DOM structure. ```html 25 issues ``` -25 issues You can check the issues count after locating it by the title text: @@ -528,7 +535,6 @@ For example, consider the following DOM structure. ```html ``` -Screenshot 2022-11-10 at 16 07 47 You can locate the element by it's test id: @@ -555,7 +561,9 @@ await page.GetByTestId("directions") .ClickAsync(); ``` -button with Itinéraire text showing click action +```html + +``` :::tip When to use testid locators You can also use test ids when you choose to use the test id methodology or when you can't locate by [role](#locate-by-role) or [text](#locate-by-text). @@ -735,9 +743,6 @@ Consider the following example with a custom web component: ``` -Title, shadow-root and details - - You can locate in the same way as if the shadow root was not present at all. To click `
Details
`: @@ -758,10 +763,13 @@ page.get_by_text("Details").click() await page.GetByText("Details").ClickAsync(); ``` -Title, shadow-root and details with text details highlighted - - -

+```html + +
Title
+ #shadow-root +
Details
+
+``` To click ``: @@ -781,9 +789,13 @@ page.locator("x-details", has_text="Details" ).click() await page.Locator("x-details", new() { HasTextString = "Details" }).ClickAsync(); ``` -Title, shadow-root and details highlighted - -

+```html + +
Title
+ #shadow-root +
Details
+
+``` To ensure that `` contains the text "Details": ```js @@ -808,17 +820,14 @@ Consider the following DOM structure where we want to click on the buy button of ```html

Product 1

- +
-

Product 2

- +
``` -2 product cards with text and a button - ### Filter by text Locators can be filtered by text with the [`method: Locator.filter`] method. It will search for a particular string somewhere inside the element, possibly in a descendant element, case-insensitively. You can also pass a regular expression. @@ -826,28 +835,28 @@ Locators can be filtered by text with the [`method: Locator.filter`] method. It ```js await page.getByTestId('product-card') .filter({ hasText: 'Product 2' }) - .getByRole('button', { name: 'Buy' }) + .getByRole('button', { name: 'Add to cart' }) .click(); ``` ```java page.getByTestId("product-card") .filter(new Locator.FilterOptions().setHasText("Product 2")) - .getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Buy")) + .getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Add to cart")) .click(); ``` ```python async -await page.get_by_test_id("product-card").filter(has_text="Product 2").get_by_role("button", name="Buy").click() +await page.get_by_test_id("product-card").filter(has_text="Product 2").get_by_role("button", name="Add to cart").click() ``` ```python sync -page.get_by_test_id("product-card").filter(has_text="Product 2").get_by_role("button", name="Buy").click() +page.get_by_test_id("product-card").filter(has_text="Product 2").get_by_role("button", name="Add to cart").click() ``` ```csharp await page.GetByTestId("product-card") .Filter(new() { HasTextString = "Product 2" }) - .GetByRole(AriaRole.Button, new () { NameString = "Buy" }) + .GetByRole(AriaRole.Button, new () { NameString = "Add to cart" }) .ClickAsync(); ``` @@ -856,36 +865,45 @@ Use a regular expression: ```js await page.getByTestId('product-card') .filter({ hasText: /Product 2/ }) - .getByRole('button', { name: 'Buy' }) + .getByRole('button', { name: 'Add to cart' }) .click(); ``` ```java page.getByTestId("product-card") .filter(new Locator.FilterOptions().setHasText(Pattern.compile("Product 2"))) - .getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Buy")) + .getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Add to cart")) .click(); ``` ```python async -await page.get_by_test_id("product-card").filter(has_text=re.compile("Product 2")).get_by_role("button", name="Buy").click() +await page.get_by_test_id("product-card").filter(has_text=re.compile("Product 2")).get_by_role("button", name="Add to cart").click() ``` ```python sync page.get_by_test_id("product-card") .filter(has_text=re.compile("Product 2")) - .get_by_role("button", name="Buy") + .get_by_role("button", name="Add to cart") .click() ``` ```csharp await page.GetByTestId("product-card") .Filter(new() { HasTextRegex = new Regex("Product 2") }) - .GetByRole(AriaRole.Button, new () { NameString = "Buy" }) + .GetByRole(AriaRole.Button, new () { NameString = "Add to cart" }) .ClickAsync(); ``` -2 product cards with text and a button and the second one being highlighted +```html +
+

Product 1

+ +
+
+

Product 2

+ +
+``` ### Filter by another locator @@ -894,32 +912,41 @@ Locators support an option to only select elements that have a descendant matchi ```js await page.getByTestId('product-card') .filter({ has: page.getByRole('heading', { name: 'Product 2' })}) - .getByRole('button', { name: 'Buy' }) + .getByRole('button', { name: 'Add to cart' }) .click() ``` ```java page.getByTestId("product-card") .filter(new Locator.FilterOptions().setHas(page.GetByRole(AriaRole.HEADING, new Page.GetByRoleOptions().setName("Product 2")))) - .getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Buy"))) + .getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Add to cart"))) .click() ``` ```python async -await page.get_by_test_id("product-card").filter(has=page.get_by_role("heading", name="Product 2")).get_by_role("button", name="Buy").click() +await page.get_by_test_id("product-card").filter(has=page.get_by_role("heading", name="Product 2")).get_by_role("button", name="Add to cart").click() ``` ```python sync page.get_by_test_id("product-card") .filter(has=page.get_by_role("heading", name="Product 2")) - .get_by_role("button", name="Buy") + .get_by_role("button", name="Add to cart") .click() ``` ```csharp await page.GetByTestId("product-card") .Filter(new() { Has = page.GetByRole(AriaRole.Heading, new () { NameString = "Product 2" })}) - .GetByRole(AriaRole.Button, new () { NameString = "Buy" }) + .GetByRole(AriaRole.Button, new () { NameString = "Add to cart" }) .ClickAsync(); ``` -2 product cards with text and a button and the second one being highlighted +```html +
+

Product 1

+ +
+
+

Product 2

+ +
+``` We can also assert the product card to make sure there is only one @@ -955,13 +982,13 @@ Note that the inner locator is matched starting from the outer one, not from the You can chain methods that create a locator, like [`method: Page.getByText`] or [`method: Locator.getByRole`], to narrow down the search to a particular part of the page. -In this example we first create a locator called product by locating the test id. We then filter by text. We can use the product locator again to get by role of button and click it and then use an assertion to make sure there is only one product with the text ' Product 2'. +In this example we first create a locator called product by locating the test id. We then filter by text. We can use the product locator again to get by role of button and click it and then use an assertion to make sure there is only one product with the text "Product 2". ```js const product = page.getByTestId('product-card') .filter({ hasText: 'Product 2' }); -await product.getByRole('button', { name: 'Buy' }) +await product.getByRole('button', { name: 'Add to cart' }) .click(); await expect(product).toHaveCount(1); @@ -970,20 +997,20 @@ await expect(product).toHaveCount(1); ```python async product = page.get_by_test_id("product-card").filter(has_text="Product 2") -await product.get_by_role("button", name="Buy").click() +await product.get_by_role("button", name="Add to cart").click() ``` ```python sync product = page.get_by_test_id("product-card").filter(has_text="Product 2") -product.get_by_role("button", name="Buy").click() +product.get_by_role("button", name="Add to cart").click() ``` ```java Locator product = page.getByTestId("product-card") .filter(new Locator.FilterOptions().setHasText("Product 2")); -product.getByRole(AriaRole.BUTTON, new Locator.GetByRoleOptions().setName("Buy")) +product.getByRole(AriaRole.BUTTON, new Locator.GetByRoleOptions().setName("Add to cart")) .click(); ``` @@ -991,11 +1018,20 @@ product.getByRole(AriaRole.BUTTON, new Locator.GetByRoleOptions().setName("Buy") var product = page.GetByTestId("product-card") .Filter(new() { HasTextString = "Product 2" }); -await product.GetByRole("button", new() { NameString = "Buy" }) +await product.GetByRole("button", new() { NameString = "Add to cart" }) .ClickAsync(); ``` -2 product cards with text and a button and the second one being highlighted +```html +
+

Product 1

+ +
+
+

Product 2

+ +
+``` ## Lists @@ -1013,8 +1049,6 @@ For example, consider the following DOM structure: ``` -list of 3 items, apple, banana and orange - Use the count assertion to ensure that the list has 3 items. ```js @@ -1050,7 +1084,6 @@ For example, consider the following DOM structure:
  • orange
  • ``` -list of 3 items, apple, banana and orange Use [`method: LocatorAssertions.toHaveText`] to ensure that the list has the text "apple", "banana" and "orange". @@ -1119,7 +1152,13 @@ await page.GetByText("orange") .ClickAsync(); ``` -list of apple, banana and orange highlighting orange +```html +
      +
    • apple
    • +
    • banana
    • +
    • orange
    • +
    +``` #### Filter by text Use the [`method: Locator.filter`] to locate a specific item in a list. @@ -1161,7 +1200,14 @@ await page.GetByRole("listitem") .Filter(new() { HasTextString = "orange" }) .ClickAsync(); ``` -list of apple, banana and orange highlighting orange + +```html +
      +
    • apple
    • +
    • banana
    • +
    • orange
    • +
    +``` #### Get by test id @@ -1202,7 +1248,13 @@ await page.GetByTestId("orange") .ClickAsync(); ``` -list of apple, banana and orange highlighting orange +```html +
      +
    • apple
    • +
    • banana
    • +
    • orange
    • +
    +``` #### Get by nth item @@ -1256,7 +1308,6 @@ For example, consider the following DOM structure: ``` -text John and Mary with buttons say hello and say goodbye beside their names To take a screenshot of the row with "Mary" and "Say goodbye": @@ -1299,8 +1350,10 @@ await rowLocator.Filter(new() { HasTextString = "Mary" }) You should now have a "screenshot.png" file in your project's root directory. -text Mary with buttons say goodbye - +```html +
    Mary
    +
    +``` ### Rare use cases #### Get All text contents