docs: add examples and clarifications to getByText (#18380)

Also be more explicit about normalizing whitespace, event with exact
match.

Fixes #17831.
This commit is contained in:
Dmitry Gozman 2022-10-27 10:27:18 -07:00 committed by GitHub
parent 3e112193a6
commit c4404ea98f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 263 additions and 29 deletions

View file

@ -1074,7 +1074,7 @@ Text to locate the element for.
* since: v1.27
- `exact` <[boolean]>
Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular expression.
Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular expression. Note that exact match still trims whitespace.
## locator-get-by-role-role
* since: v1.27
@ -1185,7 +1185,109 @@ use: {
## template-locator-get-by-text
Allows locating elements that contain given text.
Allows locating elements that contain given text. Consider the following DOM structure:
```html
<div>Hello <span>world</span></div>
<div>Hello</div>
```
You can locate by text substring, exact string, or a regular expression:
```js
// Matches <span>
page.getByText('world')
// Matches first <div>
page.getByText('Hello world')
// Matches second <div>
page.getByText('Hello', { exact: true })
// Matches both <div>s
page.getByText(/Hello/)
// Matches second <div>
page.getByText(/^hello$/i)
```
```python async
# Matches <span>
page.get_by_text("world")
# Matches first <div>
page.get_by_text("Hello world")
# Matches second <div>
page.get_by_text("Hello", exact=True)
# Matches both <div>s
page.get_by_text(re.compile("Hello"))
# Matches second <div>
page.get_by_text(re.compile("^hello$", re.IGNORECASE))
```
```python sync
# Matches <span>
page.get_by_text("world")
# Matches first <div>
page.get_by_text("Hello world")
# Matches second <div>
page.get_by_text("Hello", exact=True)
# Matches both <div>s
page.get_by_text(re.compile("Hello"))
# Matches second <div>
page.get_by_text(re.compile("^hello$", re.IGNORECASE))
```
```java
// Matches <span>
page.getByText("world")
// Matches first <div>
page.getByText("Hello world")
// Matches second <div>
page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
// Matches both <div>s
page.getByText(Pattern.compile("Hello"))
// Matches second <div>
page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
```
```csharp
// Matches <span>
page.GetByText("world")
// Matches first <div>
page.GetByText("Hello world")
// Matches second <div>
page.GetByText("Hello", new() { Exact: true })
// Matches both <div>s
page.GetByText(new Regex("Hello"))
// Matches second <div>
page.GetByText(new Regex("^hello$", RegexOptions.IgnoreCase))
```
See also [`method: Locator.filter`] that allows to match by another criteria, like an accessible role, and then filter by the text content.
:::note
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.
:::
:::note
Input elements of the type `button` and `submit` are matched by their `value` instead of the text content. For example, locating by text `"Log in"` matches `<input type=button value="Log in">`.
:::
## template-locator-get-by-alt-text

View file

@ -217,6 +217,10 @@ page.get_by_test_id("product-item").filter(has_text="Playwright Book").click()
await page.GetByTestId("product-item").Filter(new() { HasText = "Playwright Book" }).ClickAsync();
```
:::note
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`]
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.

View file

@ -236,7 +236,7 @@ await page.Locator("text=Log in").ClickAsync();
Text selector has a few variations:
- `text=Log in` - default matching is case-insensitive and searches for a substring. For example, `text=Log` matches `<button>Log in</button>`.
- `text=Log in` - default matching is case-insensitive, trims whitespace and searches for a substring. For example, `text=Log` matches `<button>Log in</button>`.
```js
await page.locator('text=Log in').click();
@ -254,7 +254,7 @@ Text selector has a few variations:
await page.Locator("text=Log in").ClickAsync();
```
- `text="Log in"` - text body can be escaped with single or double quotes to search for a text node with exact content. For example, `text="Log"` does not match `<button>Log in</button>` because `<button>` contains a single text node `"Log in"` that is not equal to `"Log"`. However, `text="Log"` matches `<button>Log<span>in</span></button>`, because `<button>` contains a text node `"Log"`. This exact mode implies case-sensitive matching, so `text="Download"` will not match `<button>download</button>`.
- `text="Log in"` - text body can be escaped with single or double quotes to search for a text node with exact content after trimming whitespace. For example, `text="Log"` does not match `<button>Log in</button>` because `<button>` contains a single text node `"Log in"` that is not equal to `"Log"`. However, `text="Log"` matches `<button> Log <span>in</span></button>`, because `<button>` contains a text node `" Log "`. This exact mode implies case-sensitive matching, so `text="Download"` will not match `<button>download</button>`.
Quoted body follows the usual escaping rules, e.g. use `\"` to escape double quote in a double-quoted string: `text="foo\"bar"`.
@ -310,7 +310,7 @@ Text selector has a few variations:
await page.Locator("text=/Log\\s*in/i").ClickAsync();
```
- `article:has-text("Playwright")` - the `:has-text()` pseudo-class can be used inside a [css] selector. It matches any element containing specified text somewhere inside, possibly in a child or a descendant element. Matching is case-insensitive and searches for a substring. For example, `article:has-text("Playwright")` matches `<article><div>Playwright</div></article>`.
- `article:has-text("Playwright")` - the `:has-text()` pseudo-class can be used inside a [css] selector. It matches any element containing specified text somewhere inside, possibly in a child or a descendant element. Matching is case-insensitive, trims whitestapce and searches for a substring. For example, `article:has-text("Playwright")` matches `<article><div>Playwright</div></article>`.
Note that `:has-text()` should be used together with other `css` specifiers, otherwise it will match all the elements containing specified text, including the `<body>`.
```js

View file

@ -2498,7 +2498,7 @@ export interface Page {
getByAltText(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;
@ -2518,7 +2518,7 @@ export interface Page {
getByLabel(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;
@ -2537,7 +2537,7 @@ export interface Page {
getByPlaceholder(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;
@ -2634,14 +2634,46 @@ export interface Page {
getByTestId(testId: string): Locator;
/**
* Allows locating elements that contain given text.
* Allows locating elements that contain given text. Consider the following DOM structure:
*
* ```html
* <div>Hello <span>world</span></div>
* <div>Hello</div>
* ```
*
* You can locate by text substring, exact string, or a regular expression:
*
* ```js
* // Matches <span>
* page.getByText('world')
*
* // Matches first <div>
* page.getByText('Hello world')
*
* // Matches second <div>
* page.getByText('Hello', { exact: true })
*
* // Matches both <div>s
* page.getByText(/Hello/)
*
* // Matches second <div>
* page.getByText(/^hello$/i)
* ```
*
* See also [locator.filter([options])](https://playwright.dev/docs/api/class-locator#locator-filter) that allows to match
* by another criteria, like an accessible role, and then filter by the text content.
*
* > NOTE: 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.
* > NOTE: Input elements of the type `button` and `submit` are matched by their `value` instead of the text content. For
* example, locating by text `"Log in"` matches `<input type=button value="Log in">`.
* @param text Text to locate the element for.
* @param options
*/
getByText(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;
@ -2659,7 +2691,7 @@ export interface Page {
getByTitle(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;
@ -5645,7 +5677,7 @@ export interface Frame {
getByAltText(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;
@ -5665,7 +5697,7 @@ export interface Frame {
getByLabel(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;
@ -5684,7 +5716,7 @@ export interface Frame {
getByPlaceholder(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;
@ -5781,14 +5813,46 @@ export interface Frame {
getByTestId(testId: string): Locator;
/**
* Allows locating elements that contain given text.
* Allows locating elements that contain given text. Consider the following DOM structure:
*
* ```html
* <div>Hello <span>world</span></div>
* <div>Hello</div>
* ```
*
* You can locate by text substring, exact string, or a regular expression:
*
* ```js
* // Matches <span>
* page.getByText('world')
*
* // Matches first <div>
* page.getByText('Hello world')
*
* // Matches second <div>
* page.getByText('Hello', { exact: true })
*
* // Matches both <div>s
* page.getByText(/Hello/)
*
* // Matches second <div>
* page.getByText(/^hello$/i)
* ```
*
* See also [locator.filter([options])](https://playwright.dev/docs/api/class-locator#locator-filter) that allows to match
* by another criteria, like an accessible role, and then filter by the text content.
*
* > NOTE: 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.
* > NOTE: Input elements of the type `button` and `submit` are matched by their `value` instead of the text content. For
* example, locating by text `"Log in"` matches `<input type=button value="Log in">`.
* @param text Text to locate the element for.
* @param options
*/
getByText(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;
@ -5806,7 +5870,7 @@ export interface Frame {
getByTitle(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;
@ -10175,7 +10239,7 @@ export interface Locator {
getByAltText(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;
@ -10195,7 +10259,7 @@ export interface Locator {
getByLabel(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;
@ -10214,7 +10278,7 @@ export interface Locator {
getByPlaceholder(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;
@ -10311,14 +10375,46 @@ export interface Locator {
getByTestId(testId: string): Locator;
/**
* Allows locating elements that contain given text.
* Allows locating elements that contain given text. Consider the following DOM structure:
*
* ```html
* <div>Hello <span>world</span></div>
* <div>Hello</div>
* ```
*
* You can locate by text substring, exact string, or a regular expression:
*
* ```js
* // Matches <span>
* page.getByText('world')
*
* // Matches first <div>
* page.getByText('Hello world')
*
* // Matches second <div>
* page.getByText('Hello', { exact: true })
*
* // Matches both <div>s
* page.getByText(/Hello/)
*
* // Matches second <div>
* page.getByText(/^hello$/i)
* ```
*
* See also [locator.filter([options])](https://playwright.dev/docs/api/class-locator#locator-filter) that allows to match
* by another criteria, like an accessible role, and then filter by the text content.
*
* > NOTE: 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.
* > NOTE: Input elements of the type `button` and `submit` are matched by their `value` instead of the text content. For
* example, locating by text `"Log in"` matches `<input type=button value="Log in">`.
* @param text Text to locate the element for.
* @param options
*/
getByText(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;
@ -10336,7 +10432,7 @@ export interface Locator {
getByTitle(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;
@ -15604,7 +15700,7 @@ export interface FrameLocator {
getByAltText(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;
@ -15624,7 +15720,7 @@ export interface FrameLocator {
getByLabel(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;
@ -15643,7 +15739,7 @@ export interface FrameLocator {
getByPlaceholder(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;
@ -15740,14 +15836,46 @@ export interface FrameLocator {
getByTestId(testId: string): Locator;
/**
* Allows locating elements that contain given text.
* Allows locating elements that contain given text. Consider the following DOM structure:
*
* ```html
* <div>Hello <span>world</span></div>
* <div>Hello</div>
* ```
*
* You can locate by text substring, exact string, or a regular expression:
*
* ```js
* // Matches <span>
* page.getByText('world')
*
* // Matches first <div>
* page.getByText('Hello world')
*
* // Matches second <div>
* page.getByText('Hello', { exact: true })
*
* // Matches both <div>s
* page.getByText(/Hello/)
*
* // Matches second <div>
* page.getByText(/^hello$/i)
* ```
*
* See also [locator.filter([options])](https://playwright.dev/docs/api/class-locator#locator-filter) that allows to match
* by another criteria, like an accessible role, and then filter by the text content.
*
* > NOTE: 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.
* > NOTE: Input elements of the type `button` and `submit` are matched by their `value` instead of the text content. For
* example, locating by text `"Log in"` matches `<input type=button value="Log in">`.
* @param text Text to locate the element for.
* @param options
*/
getByText(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;
@ -15765,7 +15893,7 @@ export interface FrameLocator {
getByTitle(text: string|RegExp, options?: {
/**
* Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular
* expression.
* expression. Note that exact match still trims whitespace.
*/
exact?: boolean;
}): Locator;