diff --git a/docs/src/api/class-frame.md b/docs/src/api/class-frame.md
index ddf5d09030..c93540ce9b 100644
--- a/docs/src/api/class-frame.md
+++ b/docs/src/api/class-frame.md
@@ -887,6 +887,18 @@ await locator.ClickAsync();
* since: v1.17
+## method: Frame.get
+* since: v1.27
+- returns: <[Locator]>
+
+%%-template-locator-root-locator-%%
+
+### param: Frame.get.selector = %%-find-selector-%%
+* since: v1.27
+### option: Frame.get.-inline- = %%-locator-options-list-v1.14-%%
+* since: v1.27
+
+
## async method: Frame.getAttribute
* since: v1.8
- returns: <[null]|[string]>
@@ -907,6 +919,29 @@ Attribute name to get the value for.
### option: Frame.getAttribute.timeout = %%-input-timeout-%%
* since: v1.8
+
+## method: Frame.getByRole
+* since: v1.27
+- returns: <[Locator]>
+
+%%-template-locator-get-by-role-%%
+
+
+### param: Frame.getByRole.role = %%-locator-get-by-role-role-%%
+### option: Frame.getByRole.-inline- = %%-locator-get-by-role-option-list-v1.27-%%
+* since: v1.27
+
+
+## method: Frame.getByText
+* since: v1.27
+- returns: <[Locator]>
+
+%%-template-locator-get-by-text-%%
+
+### param: Frame.getByText.text = %%-locator-get-by-text-text-%%
+### option: Frame.getByText.exact = %%-locator-get-by-text-exact-%%
+
+
## async method: Frame.goto
* since: v1.8
* langs:
@@ -1131,8 +1166,7 @@ Returns whether the element is [visible](../actionability.md#visible). [`option:
* since: v1.14
- returns: <[Locator]>
-The method returns an element locator that can be used to perform actions in the frame.
-Locator is resolved to the element immediately before performing an action, so a series of actions on the same locator can in fact be performed on different DOM elements. That would happen if the DOM structure between those actions has changed.
+%%-template-locator-root-locator-%%
[Learn more about locators](../locators.md).
diff --git a/docs/src/api/class-framelocator.md b/docs/src/api/class-framelocator.md
index e39cd27fcb..ba89a6a405 100644
--- a/docs/src/api/class-framelocator.md
+++ b/docs/src/api/class-framelocator.md
@@ -114,6 +114,39 @@ in that iframe.
* since: v1.17
+## method: FrameLocator.get
+* since: v1.27
+- returns: <[Locator]>
+
+%%-template-locator-locator-%%
+
+### param: FrameLocator.get.selector = %%-find-selector-%%
+* since: v1.27
+### option: FrameLocator.get.-inline- = %%-locator-options-list-v1.14-%%
+* since: v1.27
+
+
+## method: FrameLocator.getByRole
+* since: v1.27
+- returns: <[Locator]>
+
+%%-template-locator-get-by-role-%%
+
+### param: FrameLocator.getByRole.role = %%-locator-get-by-role-role-%%
+### option: FrameLocator.getByRole.-inline- = %%-locator-get-by-role-option-list-v1.27-%%
+* since: v1.27
+
+
+## method: FrameLocator.getByText
+* since: v1.27
+- returns: <[Locator]>
+
+%%-template-locator-get-by-text-%%
+
+### param: FrameLocator.getByText.text = %%-locator-get-by-text-text-%%
+### option: FrameLocator.getByText.exact = %%-locator-get-by-text-exact-%%
+
+
## method: FrameLocator.last
* since: v1.17
- returns: <[FrameLocator]>
diff --git a/docs/src/api/class-locator.md b/docs/src/api/class-locator.md
index 691a78cf50..a179700674 100644
--- a/docs/src/api/class-locator.md
+++ b/docs/src/api/class-locator.md
@@ -618,6 +618,18 @@ await locator.ClickAsync();
* since: v1.17
+## method: Locator.get
+* since: v1.27
+- returns: <[Locator]>
+
+%%-template-locator-locator-%%
+
+### param: Locator.get.selector = %%-find-selector-%%
+* since: v1.27
+### option: Locator.get.-inline- = %%-locator-options-list-v1.14-%%
+* since: v1.27
+
+
## async method: Locator.getAttribute
* since: v1.14
- returns: <[null]|[string]>
@@ -633,6 +645,28 @@ Attribute name to get the value for.
### option: Locator.getAttribute.timeout = %%-input-timeout-%%
* since: v1.14
+
+## method: Locator.getByRole
+* since: v1.27
+- returns: <[Locator]>
+
+%%-template-locator-get-by-role-%%
+
+### param: Locator.getByRole.role = %%-locator-get-by-role-role-%%
+### option: Locator.getByRole.-inline- = %%-locator-get-by-role-option-list-v1.27-%%
+* since: v1.27
+
+
+## method: Locator.getByText
+* since: v1.27
+- returns: <[Locator]>
+
+%%-template-locator-get-by-text-%%
+
+### param: Locator.getByText.text = %%-locator-get-by-text-text-%%
+### option: Locator.getByText.exact = %%-locator-get-by-text-exact-%%
+
+
## async method: Locator.highlight
* since: v1.20
diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md
index c375d17e77..0636b8d8eb 100644
--- a/docs/src/api/class-page.md
+++ b/docs/src/api/class-page.md
@@ -2162,6 +2162,18 @@ await locator.ClickAsync();
An array of all frames attached to the page.
+## method: Page.get
+* since: v1.27
+- returns: <[Locator]>
+
+%%-template-locator-root-locator-%%
+
+### param: Page.get.selector = %%-find-selector-%%
+* since: v1.27
+### option: Page.get.-inline- = %%-locator-options-list-v1.14-%%
+* since: v1.27
+
+
## async method: Page.getAttribute
* since: v1.8
- returns: <[null]|[string]>
@@ -2182,6 +2194,28 @@ Attribute name to get the value for.
### option: Page.getAttribute.timeout = %%-input-timeout-%%
* since: v1.8
+
+## method: Page.getByRole
+* since: v1.27
+- returns: <[Locator]>
+
+%%-template-locator-get-by-role-%%
+
+### param: Page.getByRole.role = %%-locator-get-by-role-role-%%
+### option: Page.getByRole.-inline- = %%-locator-get-by-role-option-list-v1.27-%%
+* since: v1.27
+
+
+## method: Page.getByText
+* since: v1.27
+- returns: <[Locator]>
+
+%%-template-locator-get-by-text-%%
+
+### param: Page.getByText.text = %%-locator-get-by-text-text-%%
+### option: Page.getByText.exact = %%-locator-get-by-text-exact-%%
+
+
## async method: Page.goBack
* since: v1.8
- returns: <[null]|[Response]>
@@ -2447,12 +2481,7 @@ Returns whether the element is [visible](../actionability.md#visible). [`option:
* since: v1.14
- returns: <[Locator]>
-The method returns an element locator that can be used to perform actions on the page.
-Locator is resolved to the element immediately before performing an action, so a series of actions on the same locator can in fact be performed on different DOM elements. That would happen if the DOM structure between those actions has changed.
-
-[Learn more about locators](../locators.md).
-
-Shortcut for main frame's [`method: Frame.locator`].
+%%-template-locator-root-locator-%%
### param: Page.locator.selector = %%-find-selector-%%
* since: v1.14
diff --git a/docs/src/api/params.md b/docs/src/api/params.md
index 26a20e6257..62463f0afa 100644
--- a/docs/src/api/params.md
+++ b/docs/src/api/params.md
@@ -1058,6 +1058,115 @@ When set to `"hide"`, screenshot will hide text caret. When set to `"initial"`,
- %%-screenshot-option-mask-%%
- %%-input-timeout-%%
+## locator-get-by-text-text
+* since: v1.27
+- `text` <[string]|[RegExp]>
+
+## locator-get-by-text-exact
+* since: v1.27
+- `exact` <[boolean]>
+
+## locator-get-by-role-role
+* since: v1.27
+- `role` <[string]>
+
+## locator-get-by-role-option-checked
+* since: v1.27
+- `checked` <[boolean]>
+
+An attribute that is usually set by `aria-checked` or native `` controls. Available values for checked are `true`, `false` and `"mixed"`.
+
+Learn more about [`aria-checked`](https://www.w3.org/TR/wai-aria-1.2/#aria-checked).
+
+## locator-get-by-role-option-disabled
+* since: v1.27
+- `disabled` <[boolean]>
+
+A boolean attribute that is usually set by `aria-disabled` or `disabled`.
+
+:::note
+Unlike most other attributes, `disabled` is inherited through the DOM hierarchy.
+Learn more about [`aria-disabled`](https://www.w3.org/TR/wai-aria-1.2/#aria-disabled).
+:::
+
+## locator-get-by-role-option-expanded
+* since: v1.27
+- `expanded` <[boolean]>
+
+A boolean attribute that is usually set by `aria-expanded`.
+
+ Learn more about [`aria-expanded`](https://www.w3.org/TR/wai-aria-1.2/#aria-expanded).
+
+## locator-get-by-role-option-includeHidden
+* since: v1.27
+- `includeHidden` <[boolean]>
+
+A boolean attribute that controls whether hidden elements are matched. By default, only non-hidden elements, as [defined by ARIA](https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion), are matched by role selector.
+
+Learn more about [`aria-hidden`](https://www.w3.org/TR/wai-aria-1.2/#aria-hidden).
+
+## locator-get-by-role-option-level
+* since: v1.27
+- `level` <[int]>
+
+A number attribute that is usually present for roles `heading`, `listitem`, `row`, `treeitem`, with default values for `
-
` elements.
+
+Learn more about [`aria-level`](https://www.w3.org/TR/wai-aria-1.2/#aria-level).
+
+## locator-get-by-role-option-name
+* since: v1.27
+- `name` <[string]|[RegExp]>
+
+A string attribute that matches [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
+
+Learn more about [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
+
+## locator-get-by-role-option-pressed
+* since: v1.27
+- `pressed` <[boolean]>
+
+An attribute that is usually set by `aria-pressed`. Available values for pressed are `true`, `false` and `"mixed"`.
+
+Learn more about [`aria-pressed`](https://www.w3.org/TR/wai-aria-1.2/#aria-pressed).
+
+## locator-get-by-role-option-selected
+* since: v1.27
+- `selected`
+
+A boolean attribute that is usually set by `aria-selected`.
+
+Learn more about [`aria-selected`](https://www.w3.org/TR/wai-aria-1.2/#aria-selected).
+
+## locator-get-by-role-option-list-v1.27
+- %%-locator-get-by-role-option-checked-%%
+- %%-locator-get-by-role-option-disabled-%%
+- %%-locator-get-by-role-option-expanded-%%
+- %%-locator-get-by-role-option-includeHidden-%%
+- %%-locator-get-by-role-option-level-%%
+- %%-locator-get-by-role-option-name-%%
+- %%-locator-get-by-role-option-pressed-%%
+- %%-locator-get-by-role-option-selected-%%
+
## template-locator-locator
The method finds an element matching the specified selector in the locator's subtree. It also accepts filter options, similar to [`method: Locator.filter`] method.
+
+[Learn more about locators](../locators.md).
+
+## template-locator-root-locator
+
+The method returns an element locator that can be used to perform actions on this page / frame.
+Locator is resolved to the element immediately before performing an action, so a series of actions on the same locator can in fact be performed on different DOM elements. That would happen if the DOM structure between those actions has changed.
+
+[Learn more about locators](../locators.md).
+
+## template-locator-get-by-text
+
+Allows locating elements that contain given text.
+
+## template-locator-get-by-role
+
+Allows locating elements by their [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 selector **does not replace** accessibility audits and conformance tests, but rather gives early feedback about the ARIA guidelines.
+
+Note that many html elements have an implicitly [defined role](https://w3c.github.io/html-aam/#html-element-role-mappings) that is recognized by the role selector. You can find all the [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not recommend** duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.
+
diff --git a/packages/html-reporter/src/chip.spec.tsx b/packages/html-reporter/src/chip.spec.tsx
index f5b114bec1..0329e49d45 100644
--- a/packages/html-reporter/src/chip.spec.tsx
+++ b/packages/html-reporter/src/chip.spec.tsx
@@ -23,11 +23,11 @@ test('expand collapse', async ({ mount }) => {
const component = await mount(
Chip body
);
- await expect(component.locator('text=Chip body')).toBeVisible();
- await component.locator('text=Title').click();
- await expect(component.locator('text=Chip body')).not.toBeVisible();
- await component.locator('text=Title').click();
- await expect(component.locator('text=Chip body')).toBeVisible();
+ await expect(component.getByText('Chip body')).toBeVisible();
+ await component.getByText('Title').click();
+ await expect(component.getByText('Chip body')).not.toBeVisible();
+ await component.getByText('Title').click();
+ await expect(component.getByText('Chip body')).toBeVisible();
await expect(component).toHaveScreenshot();
});
@@ -37,7 +37,7 @@ test('render long title', async ({ mount }) => {
Chip body
);
await expect(component).toContainText('Extremely long title.');
- await expect(component.locator('text=Extremely long title.')).toHaveAttribute('title', title);
+ await expect(component.getByText('Extremely long title.')).toHaveAttribute('title', title);
await expect(component).toHaveScreenshot();
});
@@ -47,6 +47,6 @@ test('setExpanded is called', async ({ mount }) => {
setExpanded={(expanded: boolean) => expandedValues.push(expanded)}>
);
- await component.locator('text=Title').click();
+ await component.getByText('Title').click();
expect(expandedValues).toEqual([true]);
});
diff --git a/packages/html-reporter/src/headerView.spec.tsx b/packages/html-reporter/src/headerView.spec.tsx
index 363d3aebea..b37befd22e 100644
--- a/packages/html-reporter/src/headerView.spec.tsx
+++ b/packages/html-reporter/src/headerView.spec.tsx
@@ -29,11 +29,11 @@ test('should render counters', async ({ mount }) => {
ok: false,
duration: 100000
}} filterText='' setFilterText={() => {}}>);
- await expect(component.locator('a', { hasText: 'All' }).locator('.counter')).toHaveText('100');
- await expect(component.locator('a', { hasText: 'Passed' }).locator('.counter')).toHaveText('42');
- await expect(component.locator('a', { hasText: 'Failed' }).locator('.counter')).toHaveText('31');
- await expect(component.locator('a', { hasText: 'Flaky' }).locator('.counter')).toHaveText('17');
- await expect(component.locator('a', { hasText: 'Skipped' }).locator('.counter')).toHaveText('10');
+ await expect(component.get('a', { hasText: 'All' }).get('.counter')).toHaveText('100');
+ await expect(component.get('a', { hasText: 'Passed' }).get('.counter')).toHaveText('42');
+ await expect(component.get('a', { hasText: 'Failed' }).get('.counter')).toHaveText('31');
+ await expect(component.get('a', { hasText: 'Flaky' }).get('.counter')).toHaveText('17');
+ await expect(component.get('a', { hasText: 'Skipped' }).get('.counter')).toHaveText('10');
});
test('should toggle filters', async ({ page, mount: mount }) => {
@@ -51,14 +51,14 @@ test('should toggle filters', async ({ page, mount: mount }) => {
filterText=''
setFilterText={(filterText: string) => filters.push(filterText)}>
);
- await component.locator('a', { hasText: 'All' }).click();
- await component.locator('a', { hasText: 'Passed' }).click();
+ await component.get('a', { hasText: 'All' }).click();
+ await component.get('a', { hasText: 'Passed' }).click();
await expect(page).toHaveURL(/#\?q=s:passed/);
- await component.locator('a', { hasText: 'Failed' }).click();
+ await component.get('a', { hasText: 'Failed' }).click();
await expect(page).toHaveURL(/#\?q=s:failed/);
- await component.locator('a', { hasText: 'Flaky' }).click();
+ await component.get('a', { hasText: 'Flaky' }).click();
await expect(page).toHaveURL(/#\?q=s:flaky/);
- await component.locator('a', { hasText: 'Skipped' }).click();
+ await component.get('a', { hasText: 'Skipped' }).click();
await expect(page).toHaveURL(/#\?q=s:skipped/);
expect(filters).toEqual(['', 's:passed', 's:failed', 's:flaky', 's:skipped']);
});
diff --git a/packages/html-reporter/src/imageDiffView.spec.tsx b/packages/html-reporter/src/imageDiffView.spec.tsx
index bfd622d45d..f72b70dc3f 100644
--- a/packages/html-reporter/src/imageDiffView.spec.tsx
+++ b/packages/html-reporter/src/imageDiffView.spec.tsx
@@ -37,7 +37,7 @@ const imageDiff: ImageDiff = {
test('should render links', async ({ mount }) => {
const component = await mount();
- await expect(component.locator('a')).toHaveText([
+ await expect(component.get('a')).toHaveText([
'screenshot-actual.png',
'screenshot-expected.png',
'screenshot-diff.png',
@@ -46,11 +46,11 @@ test('should render links', async ({ mount }) => {
test('should show actual by default', async ({ mount }) => {
const component = await mount();
- const sliderElement = component.locator('data-testid=test-result-image-mismatch-grip');
+ const sliderElement = component.get('data-testid=test-result-image-mismatch-grip');
await expect.poll(() => sliderElement.evaluate(e => e.style.left), 'Actual slider is on the right').toBe('611px');
- const images = component.locator('img');
- const imageCount = await component.locator('img').count();
+ const images = component.get('img');
+ const imageCount = await component.get('img').count();
for (let i = 0; i < imageCount; ++i) {
const image = images.nth(i);
const box = await image.boundingBox();
@@ -60,12 +60,12 @@ test('should show actual by default', async ({ mount }) => {
test('should switch to expected', async ({ mount }) => {
const component = await mount();
- await component.locator('text="Expected"').click();
- const sliderElement = component.locator('data-testid=test-result-image-mismatch-grip');
+ await component.getByText('Expected', { exact: true }).click();
+ const sliderElement = component.get('data-testid=test-result-image-mismatch-grip');
await expect.poll(() => sliderElement.evaluate(e => e.style.left), 'Expected slider is on the left').toBe('371px');
- const images = component.locator('img');
- const imageCount = await component.locator('img').count();
+ const images = component.get('img');
+ const imageCount = await component.get('img').count();
for (let i = 0; i < imageCount; ++i) {
const image = images.nth(i);
const box = await image.boundingBox();
@@ -75,9 +75,9 @@ test('should switch to expected', async ({ mount }) => {
test('should switch to diff', async ({ mount }) => {
const component = await mount();
- await component.locator('text="Diff"').click();
+ await component.getByText('Diff', { exact: true }).click();
- const image = component.locator('img');
+ const image = component.get('img');
const box = await image.boundingBox();
expect(box).toEqual({ x: 400, y: 80, width: 200, height: 200 });
});
diff --git a/packages/html-reporter/src/testCaseView.spec.tsx b/packages/html-reporter/src/testCaseView.spec.tsx
index 3b9733ec33..f67a3f2cc5 100644
--- a/packages/html-reporter/src/testCaseView.spec.tsx
+++ b/packages/html-reporter/src/testCaseView.spec.tsx
@@ -63,13 +63,13 @@ const testCase: TestCase = {
test('should render test case', async ({ mount }) => {
const component = await mount();
- await expect(component.locator('text=Annotation text').first()).toBeVisible();
- await component.locator('text=Annotations').click();
- await expect(component.locator('text=Annotation text')).not.toBeVisible();
- await expect(component.locator('text=Outer step')).toBeVisible();
- await expect(component.locator('text=Inner step')).not.toBeVisible();
- await component.locator('text=Outer step').click();
- await expect(component.locator('text=Inner step')).toBeVisible();
- await expect(component.locator('text=test.spec.ts:42')).toBeVisible();
- await expect(component.locator('text=My test')).toBeVisible();
+ await expect(component.getByText('Annotation text', { exact: false }).first()).toBeVisible();
+ await component.getByText('Annotations').click();
+ await expect(component.getByText('Annotation text')).not.toBeVisible();
+ await expect(component.getByText('Outer step')).toBeVisible();
+ await expect(component.getByText('Inner step')).not.toBeVisible();
+ await component.getByText('Outer step').click();
+ await expect(component.getByText('Inner step')).toBeVisible();
+ await expect(component.getByText('test.spec.ts:42')).toBeVisible();
+ await expect(component.getByText('My test')).toBeVisible();
});
diff --git a/packages/playwright-core/src/client/frame.ts b/packages/playwright-core/src/client/frame.ts
index 1b254d6f2f..886a7b5b64 100644
--- a/packages/playwright-core/src/client/frame.ts
+++ b/packages/playwright-core/src/client/frame.ts
@@ -18,7 +18,8 @@
import { assert } from '../utils';
import type * as channels from '@protocol/channels';
import { ChannelOwner } from './channelOwner';
-import { FrameLocator, Locator, type LocatorOptions } from './locator';
+import { FrameLocator, Locator } from './locator';
+import type { ByRoleOptions, LocatorOptions } from './locator';
import { ElementHandle, convertSelectOptionValues, convertInputFiles } from './elementHandle';
import { assertMaxArguments, JSHandle, serializeArgument, parseResult } from './jsHandle';
import fs from 'fs';
@@ -298,6 +299,18 @@ export class Frame extends ChannelOwner implements api.Fr
return new Locator(this, selector, options);
}
+ get(selector: string, options?: LocatorOptions): Locator {
+ return this.locator(selector, options);
+ }
+
+ getByText(text: string | RegExp, options?: { exact?: boolean }): Locator {
+ return this.locator(Locator.getByTextSelector(text, options));
+ }
+
+ getByRole(role: string, options: ByRoleOptions = {}): Locator {
+ return this.locator(Locator.getByRoleSelector(role, options));
+ }
+
frameLocator(selector: string): FrameLocator {
return new FrameLocator(this, selector);
}
diff --git a/packages/playwright-core/src/client/locator.ts b/packages/playwright-core/src/client/locator.ts
index d50a9a8e9b..afd4143280 100644
--- a/packages/playwright-core/src/client/locator.ts
+++ b/packages/playwright-core/src/client/locator.ts
@@ -19,7 +19,7 @@ import type * as api from '../../types/types';
import type * as channels from '@protocol/channels';
import type { ParsedStackTrace } from '../utils/stackTrace';
import * as util from 'util';
-import { isRegExp, monotonicTime } from '../utils';
+import { isRegExp, isString, monotonicTime } from '../utils';
import { ElementHandle } from './elementHandle';
import type { Frame } from './frame';
import type { FilePayload, FrameExpectOptions, Rect, SelectOption, SelectOptionOptions, TimeoutOptions } from './types';
@@ -31,10 +31,50 @@ export type LocatorOptions = {
has?: Locator;
};
+export type ByRoleOptions = LocatorOptions & {
+ checked?: boolean;
+ disabled?: boolean;
+ expanded?: boolean;
+ includeHidden?: boolean;
+ level?: number;
+ name?: string | RegExp;
+ pressed?: boolean;
+ selected?: boolean;
+};
+
export class Locator implements api.Locator {
_frame: Frame;
_selector: string;
+ static getByTextSelector(text: string | RegExp, options?: { exact?: boolean }): string {
+ if (!isString(text))
+ return `text=${text}`;
+ const escaped = JSON.stringify(text);
+ const selector = options?.exact ? `text=${escaped}` : `text=${escaped.substring(1, escaped.length - 1)}`;
+ return selector;
+ }
+
+ static getByRoleSelector(role: string, options: ByRoleOptions = {}): string {
+ const props: string[][] = [];
+ if (options.checked !== undefined)
+ props.push(['checked', String(options.checked)]);
+ if (options.disabled !== undefined)
+ props.push(['disabled', String(options.disabled)]);
+ if (options.selected !== undefined)
+ props.push(['selected', String(options.selected)]);
+ if (options.expanded !== undefined)
+ props.push(['expanded', String(options.expanded)]);
+ if (options.includeHidden !== undefined)
+ props.push(['include-hidden', String(options.includeHidden)]);
+ if (options.level !== undefined)
+ props.push(['level', String(options.level)]);
+ if (options.name !== undefined)
+ props.push(['name', isString(options.name) ? escapeWithQuotes(options.name, '"') : String(options.name)]);
+ if (options.pressed !== undefined)
+ props.push(['pressed', String(options.pressed)]);
+ return `role=${role}${props.map(([n, v]) => `[${n}=${v}]`).join('')}`;
+ }
+
constructor(frame: Frame, selector: string, options?: LocatorOptions) {
this._frame = frame;
this._selector = selector;
@@ -132,6 +172,18 @@ export class Locator implements api.Locator {
return new Locator(this._frame, this._selector + ' >> ' + selector, options);
}
+ get(selector: string, options?: LocatorOptions): Locator {
+ return this.locator(selector, options);
+ }
+
+ getByText(text: string | RegExp, options?: { exact?: boolean }): Locator {
+ return this.locator(Locator.getByTextSelector(text, options));
+ }
+
+ getByRole(role: string, options: ByRoleOptions = {}): Locator {
+ return this.locator(Locator.getByRoleSelector(role, options));
+ }
+
frameLocator(selector: string): FrameLocator {
return new FrameLocator(this._frame, this._selector + ' >> ' + selector);
}
@@ -306,6 +358,18 @@ export class FrameLocator implements api.FrameLocator {
return new Locator(this._frame, this._frameSelector + ' >> control=enter-frame >> ' + selector, options);
}
+ get(selector: string, options?: LocatorOptions): Locator {
+ return this.locator(selector, options);
+ }
+
+ getByText(text: string | RegExp, options?: { exact?: boolean }): Locator {
+ return this.locator(Locator.getByTextSelector(text, options));
+ }
+
+ getByRole(role: string, options: ByRoleOptions = {}): Locator {
+ return this.locator(Locator.getByRoleSelector(role, options));
+ }
+
frameLocator(selector: string): FrameLocator {
return new FrameLocator(this._frame, this._frameSelector + ' >> control=enter-frame >> ' + selector);
}
diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts
index 2319b7a514..dc13cd37b6 100644
--- a/packages/playwright-core/src/client/page.ts
+++ b/packages/playwright-core/src/client/page.ts
@@ -45,7 +45,7 @@ import { Frame, verifyLoadState } from './frame';
import { HarRouter } from './harRouter';
import { Keyboard, Mouse, Touchscreen } from './input';
import { assertMaxArguments, JSHandle, parseResult, serializeArgument } from './jsHandle';
-import type { FrameLocator, Locator, LocatorOptions } from './locator';
+import type { ByRoleOptions, FrameLocator, Locator, LocatorOptions } from './locator';
import type { RouteHandlerCallback } from './network';
import { Response, Route, RouteHandler, validateHeaders, WebSocket } from './network';
import type { Request } from './network';
@@ -564,6 +564,18 @@ export class Page extends ChannelOwner implements api.Page
return this.mainFrame().locator(selector, options);
}
+ get(selector: string, options?: LocatorOptions): Locator {
+ return this.mainFrame().locator(selector, options);
+ }
+
+ getByText(text: string | RegExp, options?: { exact?: boolean }): Locator {
+ return this.mainFrame().getByText(text, options);
+ }
+
+ getByRole(role: string, options: ByRoleOptions = {}): Locator {
+ return this.mainFrame().getByRole(role, options);
+ }
+
frameLocator(selector: string): FrameLocator {
return this.mainFrame().frameLocator(selector);
}
diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts
index ec7130f690..81a831e48b 100644
--- a/packages/playwright-core/types/types.d.ts
+++ b/packages/playwright-core/types/types.d.ts
@@ -2434,6 +2434,32 @@ export interface Page {
*/
frames(): Array;
+ /**
+ * The method returns an element locator that can be used to perform actions on this page / frame. Locator is resolved to
+ * the element immediately before performing an action, so a series of actions on the same locator can in fact be performed
+ * on different DOM elements. That would happen if the DOM structure between those actions has changed.
+ *
+ * [Learn more about locators](https://playwright.dev/docs/locators).
+ * @param selector A selector to use when resolving DOM element. See [working with selectors](https://playwright.dev/docs/selectors) for more details.
+ * @param options
+ */
+ get(selector: string, options?: {
+ /**
+ * Matches elements containing an element that matches an inner locator. Inner locator is queried against the outer one.
+ * For example, `article` that has `text=Playwright` matches `
Playwright
`.
+ *
+ * Note that outer and inner locators must belong to the same frame. Inner locator must not contain [FrameLocator]s.
+ */
+ has?: Locator;
+
+ /**
+ * Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When passed a
+ * [string], matching is case-insensitive and searches for a substring. For example, `"Playwright"` matches
+ * `
Playwright
`.
+ */
+ hasText?: string|RegExp;
+ }): Locator;
+
/**
* Returns element attribute value.
* @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. See [working with selectors](https://playwright.dev/docs/selectors) for more details.
@@ -2456,6 +2482,90 @@ export interface Page {
timeout?: number;
}): Promise;
+ /**
+ * Allows locating elements by their [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 selector **does not replace**
+ * accessibility audits and conformance tests, but rather gives early feedback about the ARIA guidelines.
+ *
+ * Note that many html elements have an implicitly
+ * [defined role](https://w3c.github.io/html-aam/#html-element-role-mappings) that is recognized by the role selector. You
+ * can find all the [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not
+ * recommend** duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.
+ * @param role
+ * @param options
+ */
+ getByRole(role: string, options?: {
+ /**
+ * An attribute that is usually set by `aria-checked` or native `` controls. Available values for
+ * checked are `true`, `false` and `"mixed"`.
+ *
+ * Learn more about [`aria-checked`](https://www.w3.org/TR/wai-aria-1.2/#aria-checked).
+ */
+ checked?: boolean;
+
+ /**
+ * A boolean attribute that is usually set by `aria-disabled` or `disabled`.
+ *
+ * > NOTE: Unlike most other attributes, `disabled` is inherited through the DOM hierarchy. Learn more about
+ * [`aria-disabled`](https://www.w3.org/TR/wai-aria-1.2/#aria-disabled).
+ */
+ disabled?: boolean;
+
+ /**
+ * A boolean attribute that is usually set by `aria-expanded`.
+ *
+ * Learn more about [`aria-expanded`](https://www.w3.org/TR/wai-aria-1.2/#aria-expanded).
+ */
+ expanded?: boolean;
+
+ /**
+ * A boolean attribute that controls whether hidden elements are matched. By default, only non-hidden elements, as
+ * [defined by ARIA](https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion), are matched by role selector.
+ *
+ * Learn more about [`aria-hidden`](https://www.w3.org/TR/wai-aria-1.2/#aria-hidden).
+ */
+ includeHidden?: boolean;
+
+ /**
+ * A number attribute that is usually present for roles `heading`, `listitem`, `row`, `treeitem`, with default values for
+ * `
-
` elements.
+ *
+ * Learn more about [`aria-level`](https://www.w3.org/TR/wai-aria-1.2/#aria-level).
+ */
+ level?: number;
+
+ /**
+ * A string attribute that matches [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
+ *
+ * Learn more about [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
+ */
+ name?: string|RegExp;
+
+ /**
+ * An attribute that is usually set by `aria-pressed`. Available values for pressed are `true`, `false` and `"mixed"`.
+ *
+ * Learn more about [`aria-pressed`](https://www.w3.org/TR/wai-aria-1.2/#aria-pressed).
+ */
+ pressed?: boolean;
+
+ /**
+ * A boolean attribute that is usually set by `aria-selected`.
+ *
+ * Learn more about [`aria-selected`](https://www.w3.org/TR/wai-aria-1.2/#aria-selected).
+ */
+ selected?: boolean;
+ }): Locator;
+
+ /**
+ * Allows locating elements that contain given text.
+ * @param text
+ * @param options
+ */
+ getByText(text: string|RegExp, options?: {
+ exact?: boolean;
+ }): Locator;
+
/**
* Returns the main resource response. In case of multiple redirects, the navigation will resolve with the response of the
* last redirect. If can not go back, returns `null`.
@@ -2826,14 +2936,11 @@ export interface Page {
keyboard: Keyboard;
/**
- * The method returns an element locator that can be used to perform actions on the page. Locator is resolved to the
- * element immediately before performing an action, so a series of actions on the same locator can in fact be performed on
- * different DOM elements. That would happen if the DOM structure between those actions has changed.
+ * The method returns an element locator that can be used to perform actions on this page / frame. Locator is resolved to
+ * the element immediately before performing an action, so a series of actions on the same locator can in fact be performed
+ * on different DOM elements. That would happen if the DOM structure between those actions has changed.
*
* [Learn more about locators](https://playwright.dev/docs/locators).
- *
- * Shortcut for main frame's
- * [frame.locator(selector[, options])](https://playwright.dev/docs/api/class-frame#frame-locator).
* @param selector A selector to use when resolving DOM element. See [working with selectors](https://playwright.dev/docs/selectors) for more details.
* @param options
*/
@@ -5357,6 +5464,32 @@ export interface Frame {
*/
frameLocator(selector: string): FrameLocator;
+ /**
+ * The method returns an element locator that can be used to perform actions on this page / frame. Locator is resolved to
+ * the element immediately before performing an action, so a series of actions on the same locator can in fact be performed
+ * on different DOM elements. That would happen if the DOM structure between those actions has changed.
+ *
+ * [Learn more about locators](https://playwright.dev/docs/locators).
+ * @param selector A selector to use when resolving DOM element. See [working with selectors](https://playwright.dev/docs/selectors) for more details.
+ * @param options
+ */
+ get(selector: string, options?: {
+ /**
+ * Matches elements containing an element that matches an inner locator. Inner locator is queried against the outer one.
+ * For example, `article` that has `text=Playwright` matches `
Playwright
`.
+ *
+ * Note that outer and inner locators must belong to the same frame. Inner locator must not contain [FrameLocator]s.
+ */
+ has?: Locator;
+
+ /**
+ * Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When passed a
+ * [string], matching is case-insensitive and searches for a substring. For example, `"Playwright"` matches
+ * `
Playwright
`.
+ */
+ hasText?: string|RegExp;
+ }): Locator;
+
/**
* Returns element attribute value.
* @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. See [working with selectors](https://playwright.dev/docs/selectors) for more details.
@@ -5379,6 +5512,90 @@ export interface Frame {
timeout?: number;
}): Promise;
+ /**
+ * Allows locating elements by their [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 selector **does not replace**
+ * accessibility audits and conformance tests, but rather gives early feedback about the ARIA guidelines.
+ *
+ * Note that many html elements have an implicitly
+ * [defined role](https://w3c.github.io/html-aam/#html-element-role-mappings) that is recognized by the role selector. You
+ * can find all the [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not
+ * recommend** duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.
+ * @param role
+ * @param options
+ */
+ getByRole(role: string, options?: {
+ /**
+ * An attribute that is usually set by `aria-checked` or native `` controls. Available values for
+ * checked are `true`, `false` and `"mixed"`.
+ *
+ * Learn more about [`aria-checked`](https://www.w3.org/TR/wai-aria-1.2/#aria-checked).
+ */
+ checked?: boolean;
+
+ /**
+ * A boolean attribute that is usually set by `aria-disabled` or `disabled`.
+ *
+ * > NOTE: Unlike most other attributes, `disabled` is inherited through the DOM hierarchy. Learn more about
+ * [`aria-disabled`](https://www.w3.org/TR/wai-aria-1.2/#aria-disabled).
+ */
+ disabled?: boolean;
+
+ /**
+ * A boolean attribute that is usually set by `aria-expanded`.
+ *
+ * Learn more about [`aria-expanded`](https://www.w3.org/TR/wai-aria-1.2/#aria-expanded).
+ */
+ expanded?: boolean;
+
+ /**
+ * A boolean attribute that controls whether hidden elements are matched. By default, only non-hidden elements, as
+ * [defined by ARIA](https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion), are matched by role selector.
+ *
+ * Learn more about [`aria-hidden`](https://www.w3.org/TR/wai-aria-1.2/#aria-hidden).
+ */
+ includeHidden?: boolean;
+
+ /**
+ * A number attribute that is usually present for roles `heading`, `listitem`, `row`, `treeitem`, with default values for
+ * `
-
` elements.
+ *
+ * Learn more about [`aria-level`](https://www.w3.org/TR/wai-aria-1.2/#aria-level).
+ */
+ level?: number;
+
+ /**
+ * A string attribute that matches [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
+ *
+ * Learn more about [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
+ */
+ name?: string|RegExp;
+
+ /**
+ * An attribute that is usually set by `aria-pressed`. Available values for pressed are `true`, `false` and `"mixed"`.
+ *
+ * Learn more about [`aria-pressed`](https://www.w3.org/TR/wai-aria-1.2/#aria-pressed).
+ */
+ pressed?: boolean;
+
+ /**
+ * A boolean attribute that is usually set by `aria-selected`.
+ *
+ * Learn more about [`aria-selected`](https://www.w3.org/TR/wai-aria-1.2/#aria-selected).
+ */
+ selected?: boolean;
+ }): Locator;
+
+ /**
+ * Allows locating elements that contain given text.
+ * @param text
+ * @param options
+ */
+ getByText(text: string|RegExp, options?: {
+ exact?: boolean;
+ }): Locator;
+
/**
* Returns the main resource response. In case of multiple redirects, the navigation will resolve with the response of the
* last redirect.
@@ -5686,9 +5903,11 @@ export interface Frame {
}): Promise;
/**
- * The method returns an element locator that can be used to perform actions in the frame. Locator is resolved to the
- * element immediately before performing an action, so a series of actions on the same locator can in fact be performed on
- * different DOM elements. That would happen if the DOM structure between those actions has changed.
+ * The method returns an element locator that can be used to perform actions on this page / frame. Locator is resolved to
+ * the element immediately before performing an action, so a series of actions on the same locator can in fact be performed
+ * on different DOM elements. That would happen if the DOM structure between those actions has changed.
+ *
+ * [Learn more about locators](https://playwright.dev/docs/locators).
*
* [Learn more about locators](https://playwright.dev/docs/locators).
* @param selector A selector to use when resolving DOM element. See [working with selectors](https://playwright.dev/docs/selectors) for more details.
@@ -9630,6 +9849,31 @@ export interface Locator {
*/
frameLocator(selector: string): FrameLocator;
+ /**
+ * The method finds an element matching the specified selector in the locator's subtree. It also accepts filter options,
+ * similar to [locator.filter([options])](https://playwright.dev/docs/api/class-locator#locator-filter) method.
+ *
+ * [Learn more about locators](https://playwright.dev/docs/locators).
+ * @param selector A selector to use when resolving DOM element. See [working with selectors](https://playwright.dev/docs/selectors) for more details.
+ * @param options
+ */
+ get(selector: string, options?: {
+ /**
+ * Matches elements containing an element that matches an inner locator. Inner locator is queried against the outer one.
+ * For example, `article` that has `text=Playwright` matches `
Playwright
`.
+ *
+ * Note that outer and inner locators must belong to the same frame. Inner locator must not contain [FrameLocator]s.
+ */
+ has?: Locator;
+
+ /**
+ * Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When passed a
+ * [string], matching is case-insensitive and searches for a substring. For example, `"Playwright"` matches
+ * `
Playwright
`.
+ */
+ hasText?: string|RegExp;
+ }): Locator;
+
/**
* Returns element attribute value.
* @param name Attribute name to get the value for.
@@ -9645,6 +9889,90 @@ export interface Locator {
timeout?: number;
}): Promise;
+ /**
+ * Allows locating elements by their [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 selector **does not replace**
+ * accessibility audits and conformance tests, but rather gives early feedback about the ARIA guidelines.
+ *
+ * Note that many html elements have an implicitly
+ * [defined role](https://w3c.github.io/html-aam/#html-element-role-mappings) that is recognized by the role selector. You
+ * can find all the [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not
+ * recommend** duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.
+ * @param role
+ * @param options
+ */
+ getByRole(role: string, options?: {
+ /**
+ * An attribute that is usually set by `aria-checked` or native `` controls. Available values for
+ * checked are `true`, `false` and `"mixed"`.
+ *
+ * Learn more about [`aria-checked`](https://www.w3.org/TR/wai-aria-1.2/#aria-checked).
+ */
+ checked?: boolean;
+
+ /**
+ * A boolean attribute that is usually set by `aria-disabled` or `disabled`.
+ *
+ * > NOTE: Unlike most other attributes, `disabled` is inherited through the DOM hierarchy. Learn more about
+ * [`aria-disabled`](https://www.w3.org/TR/wai-aria-1.2/#aria-disabled).
+ */
+ disabled?: boolean;
+
+ /**
+ * A boolean attribute that is usually set by `aria-expanded`.
+ *
+ * Learn more about [`aria-expanded`](https://www.w3.org/TR/wai-aria-1.2/#aria-expanded).
+ */
+ expanded?: boolean;
+
+ /**
+ * A boolean attribute that controls whether hidden elements are matched. By default, only non-hidden elements, as
+ * [defined by ARIA](https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion), are matched by role selector.
+ *
+ * Learn more about [`aria-hidden`](https://www.w3.org/TR/wai-aria-1.2/#aria-hidden).
+ */
+ includeHidden?: boolean;
+
+ /**
+ * A number attribute that is usually present for roles `heading`, `listitem`, `row`, `treeitem`, with default values for
+ * `
-
` elements.
+ *
+ * Learn more about [`aria-level`](https://www.w3.org/TR/wai-aria-1.2/#aria-level).
+ */
+ level?: number;
+
+ /**
+ * A string attribute that matches [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
+ *
+ * Learn more about [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
+ */
+ name?: string|RegExp;
+
+ /**
+ * An attribute that is usually set by `aria-pressed`. Available values for pressed are `true`, `false` and `"mixed"`.
+ *
+ * Learn more about [`aria-pressed`](https://www.w3.org/TR/wai-aria-1.2/#aria-pressed).
+ */
+ pressed?: boolean;
+
+ /**
+ * A boolean attribute that is usually set by `aria-selected`.
+ *
+ * Learn more about [`aria-selected`](https://www.w3.org/TR/wai-aria-1.2/#aria-selected).
+ */
+ selected?: boolean;
+ }): Locator;
+
+ /**
+ * Allows locating elements that contain given text.
+ * @param text
+ * @param options
+ */
+ getByText(text: string|RegExp, options?: {
+ exact?: boolean;
+ }): Locator;
+
/**
* Highlight the corresponding element(s) on the screen. Useful for debugging, don't commit the code that uses
* [locator.highlight()](https://playwright.dev/docs/api/class-locator#locator-highlight).
@@ -9839,6 +10167,8 @@ export interface Locator {
/**
* The method finds an element matching the specified selector in the locator's subtree. It also accepts filter options,
* similar to [locator.filter([options])](https://playwright.dev/docs/api/class-locator#locator-filter) method.
+ *
+ * [Learn more about locators](https://playwright.dev/docs/locators).
* @param selector A selector to use when resolving DOM element. See [working with selectors](https://playwright.dev/docs/selectors) for more details.
* @param options
*/
@@ -14732,6 +15062,115 @@ export interface FrameLocator {
*/
frameLocator(selector: string): FrameLocator;
+ /**
+ * The method finds an element matching the specified selector in the locator's subtree. It also accepts filter options,
+ * similar to [locator.filter([options])](https://playwright.dev/docs/api/class-locator#locator-filter) method.
+ *
+ * [Learn more about locators](https://playwright.dev/docs/locators).
+ * @param selector A selector to use when resolving DOM element. See [working with selectors](https://playwright.dev/docs/selectors) for more details.
+ * @param options
+ */
+ get(selector: string, options?: {
+ /**
+ * Matches elements containing an element that matches an inner locator. Inner locator is queried against the outer one.
+ * For example, `article` that has `text=Playwright` matches `
Playwright
`.
+ *
+ * Note that outer and inner locators must belong to the same frame. Inner locator must not contain [FrameLocator]s.
+ */
+ has?: Locator;
+
+ /**
+ * Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When passed a
+ * [string], matching is case-insensitive and searches for a substring. For example, `"Playwright"` matches
+ * `
Playwright
`.
+ */
+ hasText?: string|RegExp;
+ }): Locator;
+
+ /**
+ * Allows locating elements by their [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 selector **does not replace**
+ * accessibility audits and conformance tests, but rather gives early feedback about the ARIA guidelines.
+ *
+ * Note that many html elements have an implicitly
+ * [defined role](https://w3c.github.io/html-aam/#html-element-role-mappings) that is recognized by the role selector. You
+ * can find all the [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not
+ * recommend** duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.
+ * @param role
+ * @param options
+ */
+ getByRole(role: string, options?: {
+ /**
+ * An attribute that is usually set by `aria-checked` or native `` controls. Available values for
+ * checked are `true`, `false` and `"mixed"`.
+ *
+ * Learn more about [`aria-checked`](https://www.w3.org/TR/wai-aria-1.2/#aria-checked).
+ */
+ checked?: boolean;
+
+ /**
+ * A boolean attribute that is usually set by `aria-disabled` or `disabled`.
+ *
+ * > NOTE: Unlike most other attributes, `disabled` is inherited through the DOM hierarchy. Learn more about
+ * [`aria-disabled`](https://www.w3.org/TR/wai-aria-1.2/#aria-disabled).
+ */
+ disabled?: boolean;
+
+ /**
+ * A boolean attribute that is usually set by `aria-expanded`.
+ *
+ * Learn more about [`aria-expanded`](https://www.w3.org/TR/wai-aria-1.2/#aria-expanded).
+ */
+ expanded?: boolean;
+
+ /**
+ * A boolean attribute that controls whether hidden elements are matched. By default, only non-hidden elements, as
+ * [defined by ARIA](https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion), are matched by role selector.
+ *
+ * Learn more about [`aria-hidden`](https://www.w3.org/TR/wai-aria-1.2/#aria-hidden).
+ */
+ includeHidden?: boolean;
+
+ /**
+ * A number attribute that is usually present for roles `heading`, `listitem`, `row`, `treeitem`, with default values for
+ * `
-
` elements.
+ *
+ * Learn more about [`aria-level`](https://www.w3.org/TR/wai-aria-1.2/#aria-level).
+ */
+ level?: number;
+
+ /**
+ * A string attribute that matches [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
+ *
+ * Learn more about [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
+ */
+ name?: string|RegExp;
+
+ /**
+ * An attribute that is usually set by `aria-pressed`. Available values for pressed are `true`, `false` and `"mixed"`.
+ *
+ * Learn more about [`aria-pressed`](https://www.w3.org/TR/wai-aria-1.2/#aria-pressed).
+ */
+ pressed?: boolean;
+
+ /**
+ * A boolean attribute that is usually set by `aria-selected`.
+ *
+ * Learn more about [`aria-selected`](https://www.w3.org/TR/wai-aria-1.2/#aria-selected).
+ */
+ selected?: boolean;
+ }): Locator;
+
+ /**
+ * Allows locating elements that contain given text.
+ * @param text
+ * @param options
+ */
+ getByText(text: string|RegExp, options?: {
+ exact?: boolean;
+ }): Locator;
+
/**
* Returns locator to the last matching frame.
*/
@@ -14740,6 +15179,8 @@ export interface FrameLocator {
/**
* The method finds an element matching the specified selector in the locator's subtree. It also accepts filter options,
* similar to [locator.filter([options])](https://playwright.dev/docs/api/class-locator#locator-filter) method.
+ *
+ * [Learn more about locators](https://playwright.dev/docs/locators).
* @param selector A selector to use when resolving DOM element. See [working with selectors](https://playwright.dev/docs/selectors) for more details.
* @param options
*/
diff --git a/tests/library/trace-viewer.spec.ts b/tests/library/trace-viewer.spec.ts
index 888e87c05a..93946f75c0 100644
--- a/tests/library/trace-viewer.spec.ts
+++ b/tests/library/trace-viewer.spec.ts
@@ -580,15 +580,15 @@ test('should include metainfo', async ({ showTraceViewer, browserName }) => {
const traceViewer = await showTraceViewer([traceFile]);
await traceViewer.page.locator('text=Metadata').click();
const callLine = traceViewer.page.locator('.call-line');
- await expect(callLine.locator('text=start time')).toHaveText(/start time: [\d/,: ]+/);
- await expect(callLine.locator('text=duration')).toHaveText(/duration: [\dms]+/);
- await expect(callLine.locator('text=engine')).toHaveText(/engine: [\w]+/);
- await expect(callLine.locator('text=platform')).toHaveText(/platform: [\w]+/);
- await expect(callLine.locator('text=width')).toHaveText(/width: [\d]+/);
- await expect(callLine.locator('text=height')).toHaveText(/height: [\d]+/);
- await expect(callLine.locator('text=pages')).toHaveText(/pages: 1/);
- await expect(callLine.locator('text=actions')).toHaveText(/actions: [\d]+/);
- await expect(callLine.locator('text=events')).toHaveText(/events: [\d]+/);
+ await expect(callLine.getByText('start time')).toHaveText(/start time: [\d/,: ]+/);
+ await expect(callLine.getByText('duration')).toHaveText(/duration: [\dms]+/);
+ await expect(callLine.getByText('engine')).toHaveText(/engine: [\w]+/);
+ await expect(callLine.getByText('platform')).toHaveText(/platform: [\w]+/);
+ await expect(callLine.getByText('width')).toHaveText(/width: [\d]+/);
+ await expect(callLine.getByText('height')).toHaveText(/height: [\d]+/);
+ await expect(callLine.getByText('pages')).toHaveText(/pages: 1/);
+ await expect(callLine.getByText('actions')).toHaveText(/actions: [\d]+/);
+ await expect(callLine.getByText('events')).toHaveText(/events: [\d]+/);
});
test('should open two trace files', async ({ context, page, request, server, showTraceViewer }, testInfo) => {
@@ -631,16 +631,16 @@ test('should open two trace files', async ({ context, page, request, server, sho
await traceViewer.page.locator('text=Metadata').click();
const callLine = traceViewer.page.locator('.call-line');
// Should get metadata from the context trace
- await expect(callLine.locator('text=start time')).toHaveText(/start time: [\d/,: ]+/);
+ await expect(callLine.getByText('start time')).toHaveText(/start time: [\d/,: ]+/);
// duration in the metatadata section
- await expect(callLine.locator('text=duration').first()).toHaveText(/duration: [\dms]+/);
- await expect(callLine.locator('text=engine')).toHaveText(/engine: [\w]+/);
- await expect(callLine.locator('text=platform')).toHaveText(/platform: [\w]+/);
- await expect(callLine.locator('text=width')).toHaveText(/width: [\d]+/);
- await expect(callLine.locator('text=height')).toHaveText(/height: [\d]+/);
- await expect(callLine.locator('text=pages')).toHaveText(/pages: 1/);
- await expect(callLine.locator('text=actions')).toHaveText(/actions: 6/);
- await expect(callLine.locator('text=events')).toHaveText(/events: [\d]+/);
+ await expect(callLine.getByText('duration').first()).toHaveText(/duration: [\dms]+/);
+ await expect(callLine.getByText('engine')).toHaveText(/engine: [\w]+/);
+ await expect(callLine.getByText('platform')).toHaveText(/platform: [\w]+/);
+ await expect(callLine.getByText('width')).toHaveText(/width: [\d]+/);
+ await expect(callLine.getByText('height')).toHaveText(/height: [\d]+/);
+ await expect(callLine.getByText('pages')).toHaveText(/pages: 1/);
+ await expect(callLine.getByText('actions')).toHaveText(/actions: 6/);
+ await expect(callLine.getByText('events')).toHaveText(/events: [\d]+/);
});
test('should include requestUrl in route.fulfill', async ({ page, runAndTrace, browserName }) => {
@@ -661,8 +661,8 @@ test('should include requestUrl in route.fulfill', async ({ page, runAndTrace, b
await traceViewer.selectAction('route.fulfill');
await traceViewer.page.locator('.tab-label', { hasText: 'Call' }).click();
const callLine = traceViewer.page.locator('.call-line');
- await expect(callLine.locator('text=status')).toContainText('200');
- await expect(callLine.locator('text=requestUrl')).toContainText('http://test.com');
+ await expect(callLine.getByText('status')).toContainText('200');
+ await expect(callLine.getByText('requestUrl')).toContainText('http://test.com');
});
test('should include requestUrl in route.continue', async ({ page, runAndTrace, server }) => {
@@ -677,8 +677,8 @@ test('should include requestUrl in route.continue', async ({ page, runAndTrace,
await traceViewer.selectAction('route.continue');
await traceViewer.page.locator('.tab-label', { hasText: 'Call' }).click();
const callLine = traceViewer.page.locator('.call-line');
- await expect(callLine.locator('text=requestUrl')).toContainText('http://test.com');
- await expect(callLine.locator('text=/^url: .*/')).toContainText(server.EMPTY_PAGE);
+ await expect(callLine.getByText('requestUrl')).toContainText('http://test.com');
+ await expect(callLine.getByText(/^url: .*/)).toContainText(server.EMPTY_PAGE);
});
test('should include requestUrl in route.abort', async ({ page, runAndTrace, server }) => {
@@ -693,7 +693,7 @@ test('should include requestUrl in route.abort', async ({ page, runAndTrace, ser
await traceViewer.selectAction('route.abort');
await traceViewer.page.locator('.tab-label', { hasText: 'Call' }).click();
const callLine = traceViewer.page.locator('.call-line');
- await expect(callLine.locator('text=requestUrl')).toContainText('http://test.com');
+ await expect(callLine.getByText('requestUrl')).toContainText('http://test.com');
});
test('should serve overridden request', async ({ page, runAndTrace, server }) => {
diff --git a/tests/page/locator-frame.spec.ts b/tests/page/locator-frame.spec.ts
index 83b8289529..e080cbbd72 100644
--- a/tests/page/locator-frame.spec.ts
+++ b/tests/page/locator-frame.spec.ts
@@ -68,7 +68,7 @@ async function routeAmbiguous(page: Page) {
it('should work for iframe @smoke', async ({ page, server }) => {
await routeIframe(page);
await page.goto(server.EMPTY_PAGE);
- const button = page.frameLocator('iframe').locator('button');
+ const button = page.frameLocator('iframe').get('button');
await button.waitFor();
expect(await button.innerText()).toBe('Hello iframe');
await expect(button).toHaveText('Hello iframe');
@@ -78,7 +78,7 @@ it('should work for iframe @smoke', async ({ page, server }) => {
it('should work for nested iframe', async ({ page, server }) => {
await routeIframe(page);
await page.goto(server.EMPTY_PAGE);
- const button = page.frameLocator('iframe').frameLocator('iframe').locator('button');
+ const button = page.frameLocator('iframe').frameLocator('iframe').get('button');
await button.waitFor();
expect(await button.innerText()).toBe('Hello nested iframe');
await expect(button).toHaveText('Hello nested iframe');
@@ -88,15 +88,15 @@ it('should work for nested iframe', async ({ page, server }) => {
it('should work for $ and $$', async ({ page, server }) => {
await routeIframe(page);
await page.goto(server.EMPTY_PAGE);
- const locator = page.frameLocator('iframe').locator('button');
+ const locator = page.frameLocator('iframe').get('button');
await expect(locator).toHaveText('Hello iframe');
- const spans = page.frameLocator('iframe').locator('span');
+ const spans = page.frameLocator('iframe').get('span');
await expect(spans).toHaveCount(2);
});
it('should wait for frame', async ({ page, server }) => {
await page.goto(server.EMPTY_PAGE);
- const error = await page.frameLocator('iframe').locator('span').click({ timeout: 1000 }).catch(e => e);
+ const error = await page.frameLocator('iframe').get('span').click({ timeout: 1000 }).catch(e => e);
expect(error.message).toContain('waiting for frame "iframe"');
});
@@ -237,3 +237,12 @@ it('locator.frameLocator should not throw on first/last/nth', async ({ page, ser
const button3 = page.locator('body').frameLocator('iframe').last().locator('button');
await expect(button3).toHaveText('Hello from iframe-3.html');
});
+
+it('role and text coverage', async ({ page, server }) => {
+ await routeIframe(page);
+ await page.goto(server.EMPTY_PAGE);
+ const button1 = page.frameLocator('iframe').getByRole('button');
+ const button2 = page.frameLocator('iframe').getByText('Hello');
+ await expect(button1).toHaveText('Hello iframe');
+ await expect(button2).toHaveText('Hello iframe');
+});
diff --git a/tests/page/locator-query.spec.ts b/tests/page/locator-query.spec.ts
index 29742c1531..6d729d4c90 100644
--- a/tests/page/locator-query.spec.ts
+++ b/tests/page/locator-query.spec.ts
@@ -172,3 +172,11 @@ it('should enforce same frame for has/leftOf/rightOf/above/below/near', async ({
expect(error.message).toContain(`Inner "${option}" locator must belong to the same frame.`);
}
});
+
+it('alias methods coverage', async ({ page }) => {
+ await page.setContent(``);
+ await expect(page.get('button')).toHaveCount(1);
+ await expect(page.get('div').get('button')).toHaveCount(1);
+ await expect(page.get('div').getByRole('button')).toHaveCount(1);
+ await expect(page.mainFrame().get('button')).toHaveCount(1);
+});
diff --git a/tests/page/selectors-role.spec.ts b/tests/page/selectors-role.spec.ts
index 4b024d466d..bd79fa2d61 100644
--- a/tests/page/selectors-role.spec.ts
+++ b/tests/page/selectors-role.spec.ts
@@ -25,25 +25,27 @@ test('should detect roles', async ({ page }) => {
Hello