diff --git a/docs/src/api/class-elementhandle.md b/docs/src/api/class-elementhandle.md
index 21683c3d30..4c9e8075ea 100644
--- a/docs/src/api/class-elementhandle.md
+++ b/docs/src/api/class-elementhandle.md
@@ -375,7 +375,7 @@ Optional event-specific initialization properties.
Returns the return value of [`param: expression`].
The method finds an element matching the specified selector in the `ElementHandle`s subtree and passes it as a first
-argument to [`param: expression`]. See [Working with selectors](../selectors.md) for more
+argument to [`param: expression`]. See [Working with selectors](../locators.md#selectors) for more
details. If no elements match the selector, the method throws an error.
If [`param: expression`] returns a [Promise], then [`method: ElementHandle.evalOnSelector`] would wait for the promise to resolve and return its
@@ -436,7 +436,7 @@ Returns the return value of [`param: expression`].
The method finds all elements matching the specified selector in the `ElementHandle`'s subtree and passes an array of
matched elements as a first argument to [`param: expression`]. See
-[Working with selectors](../selectors.md) for more details.
+[Working with selectors](../locators.md#selectors) for more details.
If [`param: expression`] returns a [Promise], then [`method: ElementHandle.evalOnSelectorAll`] would wait for the promise to resolve and return its
value.
@@ -669,7 +669,7 @@ Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.
- returns: <[null]|[ElementHandle]>
The method finds an element matching the specified selector in the `ElementHandle`'s subtree. See
-[Working with selectors](../selectors.md) for more details. If no elements match the selector,
+[Working with selectors](../locators.md#selectors) for more details. If no elements match the selector,
returns `null`.
### param: ElementHandle.querySelector.selector = %%-query-selector-%%
@@ -683,7 +683,7 @@ returns `null`.
- returns: <[Array]<[ElementHandle]>>
The method finds all elements matching the specified selector in the `ElementHandle`s subtree. See
-[Working with selectors](../selectors.md) for more details. If no elements match the selector,
+[Working with selectors](../locators.md#selectors) for more details. If no elements match the selector,
returns empty array.
### param: ElementHandle.querySelectorAll.selector = %%-query-selector-%%
diff --git a/docs/src/api/class-frame.md b/docs/src/api/class-frame.md
index 12a4a7d109..ef2a2f9bba 100644
--- a/docs/src/api/class-frame.md
+++ b/docs/src/api/class-frame.md
@@ -449,7 +449,7 @@ the flaky tests. Use [`method: Locator.evaluate`], other [Locator] helper method
:::
The method finds an element matching the specified selector within the frame and passes it as a first argument to
-[`param: expression`]. See [Working with selectors](../selectors.md) for more details. If no
+[`param: expression`]. See [Working with selectors](../locators.md#selectors) for more details. If no
elements match the selector, the method throws an error.
If [`param: expression`] returns a [Promise], then [`method: Frame.evalOnSelector`] would wait for the promise to resolve and return its
@@ -514,7 +514,7 @@ In most cases, [`method: Locator.evaluateAll`], other [Locator] helper methods a
:::
The method finds all elements matching the specified selector within the frame and passes an array of matched elements
-as a first argument to [`param: expression`]. See [Working with selectors](../selectors.md) for
+as a first argument to [`param: expression`]. See [Working with selectors](../locators.md#selectors) for
more details.
If [`param: expression`] returns a [Promise], then [`method: Frame.evalOnSelectorAll`] would wait for the promise to resolve and return its
@@ -1222,7 +1222,7 @@ The use of [ElementHandle] is discouraged, use [Locator] objects and web-first a
:::
The method finds an element matching the specified selector within the frame. See
-[Working with selectors](../selectors.md) for more details. If no elements match the selector,
+[Working with selectors](../locators.md#selectors) for more details. If no elements match the selector,
returns `null`.
### param: Frame.querySelector.selector = %%-query-selector-%%
@@ -1245,7 +1245,7 @@ The use of [ElementHandle] is discouraged, use [Locator] objects instead.
:::
The method finds all elements matching the specified selector within the frame. See
-[Working with selectors](../selectors.md) for more details. If no elements match the selector,
+[Working with selectors](../locators.md#selectors) for more details. If no elements match the selector,
returns empty array.
### param: Frame.querySelectorAll.selector = %%-query-selector-%%
diff --git a/docs/src/api/class-playwright.md b/docs/src/api/class-playwright.md
index ca1e866ddd..f8d0be43ed 100644
--- a/docs/src/api/class-playwright.md
+++ b/docs/src/api/class-playwright.md
@@ -233,7 +233,7 @@ Exposes API that can be used for the Web API testing.
- type: <[Selectors]>
Selectors can be used to install custom selector engines. See
-[Working with selectors](../selectors.md) for more information.
+[Working with selectors](../locators.md#selectors) for more information.
## property: Playwright.webkit
* since: v1.8
diff --git a/docs/src/api/class-selectors.md b/docs/src/api/class-selectors.md
index 51241b070d..cc79568b3d 100644
--- a/docs/src/api/class-selectors.md
+++ b/docs/src/api/class-selectors.md
@@ -1,7 +1,7 @@
# class: Selectors
* since: v1.8
-Selectors can be used to install custom selector engines. See [Working with selectors](../selectors.md) for more
+Selectors can be used to install custom selector engines. See [Working with selectors](../locators.md#selectors) for more
information.
## async method: Selectors.register
diff --git a/docs/src/api/params.md b/docs/src/api/params.md
index f976220672..19670aca87 100644
--- a/docs/src/api/params.md
+++ b/docs/src/api/params.md
@@ -52,19 +52,19 @@ Whether to bypass the [actionability](../actionability.md) checks. Defaults to `
- `selector` <[string]>
A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. See
-[working with selectors](../selectors.md) for more details.
+[working with selectors](../locators.md#selectors) for more details.
## input-source
- `source` <[string]>
A selector to search for an element to drag. If there are multiple elements satisfying the selector, the first will be used. See
-[working with selectors](../selectors.md) for more details.
+[working with selectors](../locators.md#selectors) for more details.
## input-target
- `target` <[string]>
A selector to search for an element to drop onto. If there are multiple elements satisfying the selector, the first will be used. See
-[working with selectors](../selectors.md) for more details.
+[working with selectors](../locators.md#selectors) for more details.
## input-position
- `position` <[Object]>
@@ -130,12 +130,12 @@ Whether to check or uncheck the checkbox.
## query-selector
- `selector` <[string]>
-A selector to query for. See [working with selectors](../selectors.md) for more details.
+A selector to query for. See [working with selectors](../locators.md#selectors) for more details.
## find-selector
- `selector` <[string]>
-A selector to use when resolving DOM element. See [working with selectors](../selectors.md) for more details.
+A selector to use when resolving DOM element. See [working with selectors](../locators.md#selectors) for more details.
## wait-for-selector-state
- `state` <[WaitForSelectorState]<"attached"|"detached"|"visible"|"hidden">>
diff --git a/docs/src/ci-intro-js.md b/docs/src/ci-intro-js.md
index 9d81c35843..ec6c8c363f 100644
--- a/docs/src/ci-intro-js.md
+++ b/docs/src/ci-intro-js.md
@@ -7,13 +7,13 @@ When installing Playwright you are given the option to add a [GitHub Actions](ht
**What you will learn:**
-- [How to use GitHub Actions to run your tests](#github-actions)
-- [How to create a repo and push to GitHub](#create-a-repo-and-push-to-github)
-- [How to open the workflows](#opening-the-workflows)
-- [How to view the test logs](#viewing-test-logs)
-- [How to download the report from GitHub](#downloading-the-html-report)
-- [How to view the report](#viewing-the-html-report)
-- [How to view the trace](#viewing-the-trace)
+- [How to use GitHub Actions to run your tests](./ci-intro.md#github-actions)
+- [How to create a repo and push to GitHub](./ci-intro.md#create-a-repo-and-push-to-github)
+- [How to open the workflows](./ci-intro.md#opening-the-workflows)
+- [How to view the test logs](./ci-intro.md#viewing-test-logs)
+- [How to download the report from GitHub](./ci-intro.md#downloading-the-html-report)
+- [How to view the report](./ci-intro.md#viewing-the-html-report)
+- [How to view the trace](./ci-intro.md#viewing-the-trace)
## GitHub Actions
@@ -111,5 +111,5 @@ To learn more about running tests on CI check out our detailed guide on [Continu
## What's Next
- [Learn how to use Web First Assertions](/test-assertions.md)
-- [Learn how to use Selectors](/selectors.md)
- [Learn how to use Locators](/locators.md)
+- [Learn how to use Selectors](/locators.md#selectors)
diff --git a/docs/src/debug.md b/docs/src/debug.md
index 970fc2b23b..411672301b 100644
--- a/docs/src/debug.md
+++ b/docs/src/debug.md
@@ -3,7 +3,7 @@ id: debug
title: "Debugging Tests"
---
-The Playwright inspector is a great tool to help with debugging. It opens up a browser window highlighting the selectors as you step through each line of the test. You can also use the explore button to find other available [selectors](./selectors.md) which you can then copy into your test file and rerun your tests to see if it passes. For debugging selectors, see [here](./debug-selectors.md).
+The Playwright inspector is a great tool to help with debugging. It opens up a browser window highlighting the selectors as you step through each line of the test. You can also use the explore button to find other available [selectors](./locators.md#selectors) which you can then copy into your test file and rerun your tests to see if it passes. For debugging selectors, see [here](./debug-selectors.md).
## Playwright Inspector
@@ -104,7 +104,7 @@ Using `PWDEBUG=console` will configure the browser for debugging in Developer to
- **Runs headed**: Browsers always launch in headed mode
- **Disables timeout**: Sets default timeout to 0 (= no timeout)
- **Console helper**: Configures a `playwright` object in the browser to generate and highlight
- [Playwright selectors](./selectors.md). This can be used to verify text or
+ [Playwright selectors](./locators.md#selectors). This can be used to verify text or
composite selectors.
```bash tab=bash-bash lang=js
@@ -198,7 +198,7 @@ pwsh bin/Debug/netX/playwright.ps1 codegen wikipedia.org
### Stepping through the Playwright script
-The Inspector opens up a browser window highlighting the selectors as you step through each line of the test. Use the explore button to find other available [selectors](./selectors.md) which you can then copy into your test file and rerun your tests to see if they pass.
+The Inspector opens up a browser window highlighting the selectors as you step through each line of the test. Use the explore button to find other available [selectors](./locators.md#selectors) which you can then copy into your test file and rerun your tests to see if they pass.
diff --git a/docs/src/getting-started-vscode-js.md b/docs/src/getting-started-vscode-js.md
index b33fb4158f..5c409329a2 100644
--- a/docs/src/getting-started-vscode-js.md
+++ b/docs/src/getting-started-vscode-js.md
@@ -83,7 +83,7 @@ To record a test click on the record icon. This will create a `test-1.spec.ts` f
### Selector Highlighting
-As you interact with the page Codegen will generate the test for you in the newly created file in VS Code. When you hover over an element Playwright will highlight the element and show the [selector](./selectors.md) underneath it.
+As you interact with the page Codegen will generate the test for you in the newly created file in VS Code. When you hover over an element Playwright will highlight the element and show the [selector](./locators.md#selectors) underneath it.
diff --git a/docs/src/locators.md b/docs/src/locators.md
index e3018b803c..86764447f3 100644
--- a/docs/src/locators.md
+++ b/docs/src/locators.md
@@ -3,8 +3,9 @@ id: locators
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. Locator can be created with the [`method: Page.locator`] method.
+[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. Locator can be created with the [`method: Page.locator`] method.
+
+### Using Locators
```js
const locator = page.locator('text=Submit');
@@ -67,9 +68,9 @@ await locator.HoverAsync();
await locator.ClickAsync();
```
-## Creating Locators
+### Creating Locators
-Use [`method: Page.locator`] method to create a locator. This method takes a selector that describes how to find an element in the page. Playwright supports many different selectors like [Text](./selectors.md#text-selector), [CSS](./selectors.md#css-selector), [XPath](./selectors.md#xpath-selectors) and many more. Learn more about available selectors and how to pick one in this [in-depth guide](./selectors.md).
+Use [`method: Page.locator`] method to create a locator. This method takes a selector that describes how to find an element in the page. Playwright supports many different selectors like [Text](./text-selector), [CSS](./#css-selector), [XPath](./#xpath-selectors) and many more. Learn more about available selectors and how to pick one in this [in-depth guide](./#selectors).
```js
// Find by text.
@@ -126,7 +127,7 @@ await page.Locator("button.sign-up").ClickAsync();
await page.Locator("data-testid=sign-up").ClickAsync();
```
-## Strictness
+### Strictness
Locators are strict. This means that all operations on locators that imply
some target DOM element will throw an exception if more than one element matches
@@ -188,10 +189,10 @@ await page.Locator("button").CountAsync();
```
:::caution
-Using [`method: Locator.first`], [`method: Locator.last`], and [`method: Locator.nth`] is discouraged since it disables the concept of strictness, and as your page changes, Playwright may click on an element you did not intend. It's better to make your locator more specific. Learn more below in [Filtering Locators](#filtering-locators) and the [selectors guide](./selectors.md).
+Using [`method: Locator.first`], [`method: Locator.last`], and [`method: Locator.nth`] is discouraged since it disables the concept of strictness, and as your page changes, Playwright may click on an element you did not intend. It's better to make your locator more specific. Learn more below in [Filtering Locators](#filtering-locators) and the [selectors guide](./#selectors).
:::
-## Lists
+### Lists
You can also use locators to work with the element lists.
@@ -280,7 +281,7 @@ for (let i = 0; i < count; ++i)
var texts = await rows.EvaluateAllAsync("list => list.map(element => element.textContent)");
```
-## Filtering Locators
+### Filtering Locators
When creating a locator, you can pass additional options to filter it.
@@ -367,7 +368,7 @@ await rowLocator
.ScreenshotAsync();
```
-## Locator vs ElementHandle
+### Locator vs ElementHandle
:::caution
We only recommend using [ElementHandle] in the rare cases when you need to perform extensive DOM traversal
@@ -441,3 +442,1387 @@ var locator = page.Locator("text=Submit");
await locator.HoverAsync();
await locator.ClickAsync();
```
+## 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].
+
+
+
+### Text selector
+
+Text selector locates elements that contain passed text.
+
+```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();
+```
+
+Text selector has a few variations:
+
+- `text=Log in` - default matching is case-insensitive and searches for a substring. For example, `text=Log` matches ``.
+
+ ```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();
+ ```
+
+- `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 `` because `