From 37cd57365252e75b88d93bb091237f955df481f9 Mon Sep 17 00:00:00 2001 From: Debbie O'Brien Date: Fri, 28 Oct 2022 23:49:25 +0200 Subject: [PATCH] docs: add missing locators + improvements (#18404) --- docs/src/locators.md | 280 +++++++++++++++++++++++++++++++------------ 1 file changed, 206 insertions(+), 74 deletions(-) diff --git a/docs/src/locators.md b/docs/src/locators.md index 48e13c93db..b47a56f231 100644 --- a/docs/src/locators.md +++ b/docs/src/locators.md @@ -6,29 +6,66 @@ title: "Locators" [Locator]s are the central piece of Playwright's auto-waiting and retry-ability. In a nutshell, locators represent a way to find element(s) on the page at any moment. +### Quick Guide + +These are the recommended built in locators. + +- [`method: Page.getByRole`] to locate by explicit and implicit accessibility attributes. +- [`method: Page.getByText`] to locate by text content. +- [`method: Page.getByLabel`] to locate a form control by associated label's text. +- [`method: Page.getByPlaceholder`] to locate an input by placeholder. +- [`method: Page.getByAltText`] to locate an element, usually image, by its text alternative. +- [`method: Page.getByTitle`] to locate an element by its title. +- [`method: Page.getByTestId`] to locate an element based on its `data-testid` attribute (other attribute can be configured). + ```js -const locator = page.getByText('Submit'); -await locator.click(); +await page.getByLabel('User Name').fill('John'); + +await page.getByLabel('Password').fill('secret-password'); + +await page.getByRole('button', { name: 'Sign in' }).click(); + +await expect(page.getByText('Welcome, John!')).toBeVisible(); ``` ```java -Locator locator = page.getByText("Submit"); -locator.click(); +page.getByLabel("User Name").fill("John"); + +page.getByLabel("Password").fill("secret-password"); + +page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Sign in")).click(); + +assertThat(page.getByText("Welcome, John!")).isVisible(); ``` ```python async -locator = page.get_by_text("Submit") -await locator.click() +await page.get_by_label("User Name").fill("John") + +await page.get_by_label("Password").fill("secret-password") + +await page.get_by_role("button", name="Sign in").click() + +await expect(page.get_by_text("Welcome, John!")).to_be_visible() ``` ```python sync -locator = page.get_by_text("Submit") -locator.click() +page.get_by_label("User Name").fill("John") + +page.get_by_label("Password").fill("secret-password") + +page.get_by_role("button", name="Sign in").click() + +expect(page.get_by_text("Welcome, John!")).to_be_visible() ``` ```csharp -var locator = page.GetByText("Submit"); -await locator.ClickAsync(); +await page.GetByLabel("User Name").FillAsync("John"); + +await page.GetByLabel("Password").FillAsync("secret-password"); + +await page.GetByRole("button", new() { Name = "Sign in" }).ClickAsync(); + +await Expect(page.GetByText("Welcome, John!")).ToBeVisibleAsync(); ``` Every time locator is used for some action, up-to-date DOM element is located in the page. So in the snippet @@ -168,10 +205,111 @@ var locator = page.FrameLocator("#my-frame").GetByText("Submit"); await locator.ClickAsync(); ``` +### Locate based on accessible attributes -### Locate by text using [`method: Page.getByText`] +The [`method: Page.getByRole`] locator reflects how users and assistive technology perceive the page, for example whether some element is a button or a checkbox. When locating by role, you should usually pass the accessible name as well, so that locator pinpoints the exact element. -The easiest way to find an element is to look for the text it contains. You can match by a substring, exact string, or a regular expression. +```js +await page.getByRole('button', { name: /submit/i }).click(); + +await page.getByRole('checkbox', { checked: true, name: "Check me" }).check(); +``` + +```python async +await page.get_by_role("button", name=re.compile("submit", re.IGNORECASE)).click() + +await page.get_by_role("checkbox", checked=True, name="Check me").check() +``` + +```python sync +page.get_by_role("button", name=re.compile("submit", re.IGNORECASE)).click() + +page.get_by_role("checkbox", checked=True, name="Check me").check() +``` + +```java +page.getByRole("button", new Page.GetByRoleOptions().setName(Pattern.compile("submit", Pattern.CASE_INSENSITIVE))).click(); + +page.getByRole("checkbox", new Page.GetByRoleOptions().setChecked(true).setName("Check me"))).check(); +``` + +```csharp +await page.GetByRole("button", new() { Name = new Regex("submit", RegexOptions.IgnoreCase) }).ClickAsync(); + +await page.GetByRole("checkbox", new() { Checked = true, Name = "Check me" }).CheckAsync(); +``` + +Role locators 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). + +Note that role locators **do not replace** accessibility audits and conformance tests, but rather give early feedback about the ARIA guidelines. + +### Locate by label text + +Most form controls usually have dedicated labels that could be conveniently used to interact with the form. In this case, you can locate the control by its associated label using [`method: Page.getByLabel`]. + +For example, consider the following DOM structure. + +```html + +``` + +You can fill the input after locating it by the label text: + +```js +await page.getByLabel('Password').fill('secret'); +``` + +```java +page.getByLabel("Password").fill("secret"); +``` + +```python async +await page.get_by_label("Password").fill("secret") +``` + +```python sync +page.get_by_label("Password").fill("secret") +``` + +```csharp +await page.GetByLabel("Password").FillAsync("secret"); +``` + +### Locate by placeholder text + +Inputs may have a placeholder attribute to hint to the user what value should be entered. You can locate such an input using [`method: Page.getByPlaceholder`]. + +For example, consider the following DOM structure. + +```html + +``` + +You can fill the input after locating it by the placeholder text: + +```js +await page.getByPlaceholder("name@example.com").fill("playwright@microsoft.com"); +``` + +```java +page.getByPlaceholder("name@example.com").fill("playwright@microsoft.com"); +``` + +```python async +await page.get_by_placeholder("name@example.com").fill("playwright@microsoft.com") +``` + +```python sync +page.get_by_placeholder("name@example.com").fill("playwright@microsoft.com") +``` + +```csharp +await page.GetByPlacheolder("name@example.com").FillAsync("playwright@microsoft.com"); +``` + +### Locate by text + +The easiest way to find an element is to look for the text it contains. You can match by a substring, exact string, or a regular expression when using [`method: Page.getByText`]. ```js await page.getByText('Log in').click(); @@ -221,47 +359,73 @@ await page.GetByTestId("product-item").Filter(new() { HasText = "Playwright Book Matching by text always normalizes whitespace, even with exact match. For example, it turns multiple spaces into one, turns line breaks into spaces and ignores leading and trailing whitespace. ::: -### Locate based on accessible attributes with [`method: Page.getByRole`] +### Locate by alt text -The [`method: Page.getByRole`] locator reflects how users and assistive technology percieve the page, for example whether some element is a button or a checkbox. When locating by role, you should usually pass the accessible name as well, so that locator pinpoints the exact element. +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 +``` + +You can click on the image after locating it by the text alternative: ```js -await page.getByRole('button', { name: /submit/i }).click(); - -await page.getByRole('checkbox', { checked: true, name: "Check me" }).check(); -``` - -```python async -await page.get_by_role("button", name=re.compile("submit", re.IGNORECASE)).click() - -await page.get_by_role("checkbox", checked=True, name="Check me"]).check() -``` - -```python sync -page.get_by_role("button", name=re.compile("submit", re.IGNORECASE)).click() - -page.get_by_role("checkbox", checked=True, name="Check me"]).check() +await page.getByAltText('playwright logo').click(); ``` ```java -page.getByRole("button", new Page.GetByRoleOptions().setName(Pattern.compile("submit", Pattern.CASE_INSENSITIVE))).click(); +page.getByAltText("playwright logo").click(); +``` -page.getByRole("checkbox", new Page.GetByRoleOptions().setChecked(true).setName("Check me"))).check(); +```python async +await page.get_by_alt_text("playwright logo").click() +``` + +```python sync +page.get_by_alt_text("playwright logo").click() ``` ```csharp -await page.GetByRole("button", new() { Name = new Regex("submit", RegexOptions.IgnoreCase) }).ClickAsync(); +await page.GetByAltText("playwright logo").ClickAsync(); +``` +### Locate by title -await page.GetByRole("checkbox", new() { Checked = true, Name = "Check me" }).CheckAsync(); +Locate an element with a matching title attribute using [`method: Page.getByTitle`]. + +For example, consider the following DOM structure. + +```html +25 issues ``` -Role locators follow W3C specificaitons 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). +You can check the issues count after locating it by the title text: -Note that role locators **do not replace** accessibility audits and conformance tests, but rather give early feedback about the ARIA guidelines. +```js +await expect(page.getByTitle('Issues count')).toHaveText('25 issues'); +``` -### Define explicit contract and use [`method: Page.getByTestId`] +```java +assertThat(page.getByTitle("Issues count")).hasText("25 issues"); +``` -User-facing attributes like text or accessible name can change over time. In this case it is convenient to define explicit test ids. +```python async +await expect(page.get_by_title("Issues count")).to_have_text("25 issues") +``` + +```python sync +expect(page.get_by_title("Issues count")).to_have_text("25 issues") +``` + +```csharp +await Expect(page.GetByTitle("Issues count")).toHaveText("25 issues"); +``` + +### Define explicit contract and use a data-testid attribute + +User-facing attributes like text or accessible name can change over time. In this case it is convenient to define explicit test ids and query them with [`method: Page.getByTestId`]. ```html @@ -276,50 +440,18 @@ page.getByTestId("directions").click(); ``` ```python async -await page.get_by_test_id('directions').click() +await page.get_by_test_id("directions").click() ``` ```python sync -page.get_by_test_id('directions').click() +page.get_by_test_id("directions").click() ``` ```csharp await page.GetByTestId("directions").ClickAsync(); ``` -By default, [`method: Page.getByTestId`] will locate elements baed on the `data-testid` attribute, but you can configure it in your test config or calling [`method: Selectors.setTestIdAttribute`]. - -### Locate by label text with [`method: Page.getByLabel`] - -Most form controls usually have dedicated labels that could be conveniently used to interact with the form. In this case, you can locate the control by its associated label. - -For example, consider the following DOM structure. - -```html - -``` - -You can fill the input after locating it by the label text: - -```js -await page.getByLabel('Password').fill('secret'); -``` - -```java -page.getByLabel("Password").fill("secret"); -``` - -```python async -await page.get_by_label("Password").fill("secret") -``` - -```python sync -page.get_by_label("Password").fill("secret") -``` - -```csharp -await page.GetByLabel("Password").FillAsync("secret"); -``` +By default, [`method: Page.getByTestId`] will locate elements based on the `data-testid` attribute, but you can configure it in your test config or calling [`method: Selectors.setTestIdAttribute`]. ### Locate in a subtree @@ -486,10 +618,10 @@ page.getByRole('section').filter({ has: page.getByTestId('subscribe-button') }) page.getByRole("section").filter(new Locator.FilterOptions().setHas(page.getByTestId("subscribe-button"))) ``` ```python async -page.get_by_role("section"), has=page.get_by_test_id("subscribe-button")) +page.get_by_role("section").filter(has=page.get_by_test_id("subscribe-button")) ``` ```python sync -page.get_by_role("section"), has=page.get_by_test_id("subscribe-button")) +page.get_by_role("section").filter(has=page.get_by_test_id("subscribe-button")) ``` ```csharp page.GetByRole("section"), new() { Has = page.GetByTestId("subscribe-button") })