mirror of
https://github.com/microsoft/playwright.git
synced 2025-01-05 19:04:43 +03:00
feat: allow chaining locators with Locator.locator(anotherLocator) (#21391)
This commit is contained in:
parent
d904a6129f
commit
0c5d46bb94
@ -196,7 +196,7 @@ Returns locator to the last matching frame.
|
||||
|
||||
%%-template-locator-locator-%%
|
||||
|
||||
### param: FrameLocator.locator.selector = %%-find-selector-%%
|
||||
### param: FrameLocator.locator.selectorOrLocator = %%-find-selector-or-locator-%%
|
||||
* since: v1.17
|
||||
|
||||
### option: FrameLocator.locator.-inline- = %%-locator-options-list-v1.14-%%
|
||||
|
@ -959,7 +959,7 @@ var locator = page.FrameLocator("iframe").GetByText("Submit");
|
||||
await locator.ClickAsync();
|
||||
```
|
||||
|
||||
### param: Locator.frameLocator.selector = %%-find-selector-%%
|
||||
### param: Locator.frameLocator.selectorOrLocator = %%-find-selector-%%
|
||||
* since: v1.17
|
||||
|
||||
## async method: Locator.getAttribute
|
||||
@ -1389,7 +1389,7 @@ var banana = await page.GetByRole(AriaRole.Listitem).Last(1);
|
||||
|
||||
%%-template-locator-locator-%%
|
||||
|
||||
### param: Locator.locator.selector = %%-find-selector-%%
|
||||
### param: Locator.locator.selectorOrLocator = %%-find-selector-or-locator-%%
|
||||
* since: v1.14
|
||||
|
||||
### option: Locator.locator.-inline- = %%-locator-options-list-v1.14-%%
|
||||
|
@ -134,6 +134,11 @@ A selector to query for.
|
||||
|
||||
A selector to use when resolving DOM element.
|
||||
|
||||
## find-selector-or-locator
|
||||
- `selectorOrLocator` <[string]|[Locator]>
|
||||
|
||||
A selector or locator to use when resolving DOM element.
|
||||
|
||||
## wait-for-selector-state
|
||||
- `state` <[WaitForSelectorState]<"attached"|"detached"|"visible"|"hidden">>
|
||||
|
||||
|
@ -1044,6 +1044,44 @@ await product
|
||||
.ClickAsync();
|
||||
```
|
||||
|
||||
You can also chain two locators together, for example to find a "Save" button inside a particular dialog:
|
||||
|
||||
```js
|
||||
const saveButton = page.getByRole('button', { name: 'Save' });
|
||||
// ...
|
||||
const dialog = page.getByTestId('settings-dialog');
|
||||
await dialog.locator(saveButton).click();
|
||||
```
|
||||
|
||||
```python async
|
||||
save_button = page.get_by_role("button", name="Save")
|
||||
# ...
|
||||
dialog = page.get_by_test_id("settings-dialog")
|
||||
await dialog.locator(save_button).click()
|
||||
```
|
||||
|
||||
```python sync
|
||||
save_button = page.get_by_role("button", name="Save")
|
||||
# ...
|
||||
dialog = page.get_by_test_id("settings-dialog")
|
||||
dialog.locator(save_button).click()
|
||||
```
|
||||
|
||||
```java
|
||||
Locator saveButton = page.getByRole(AriaRole.BUTTON,
|
||||
new Page.GetByRoleOptions().setName("Save"));
|
||||
// ...
|
||||
Locator dialog = page.getByTestId("settings-dialog");
|
||||
dialog.locator(saveButton).click();
|
||||
```
|
||||
|
||||
```csharp
|
||||
var saveButton = page.GetByRole(AriaRole.Button, new() { Name = "Save" });
|
||||
// ...
|
||||
var dialog = page.GetByTestId("settings-dialog");
|
||||
await dialog.Locator(saveButton).ClickAsync();
|
||||
```
|
||||
|
||||
## Lists
|
||||
|
||||
### Count items in a list
|
||||
|
@ -18,7 +18,7 @@ import type * as structs from '../../types/structs';
|
||||
import type * as api from '../../types/types';
|
||||
import type * as channels from '@protocol/channels';
|
||||
import * as util from 'util';
|
||||
import { monotonicTime } from '../utils';
|
||||
import { isString, monotonicTime } from '../utils';
|
||||
import { ElementHandle } from './elementHandle';
|
||||
import type { Frame } from './frame';
|
||||
import type { FilePayload, FrameExpectOptions, Rect, SelectOption, SelectOptionOptions, TimeoutOptions } from './types';
|
||||
@ -128,8 +128,12 @@ export class Locator implements api.Locator {
|
||||
return this._frame._highlight(this._selector);
|
||||
}
|
||||
|
||||
locator(selector: string, options?: LocatorOptions): Locator {
|
||||
return new Locator(this._frame, this._selector + ' >> ' + selector, options);
|
||||
locator(selectorOrLocator: string | Locator, options?: LocatorOptions): Locator {
|
||||
if (isString(selectorOrLocator))
|
||||
return new Locator(this._frame, this._selector + ' >> ' + selectorOrLocator, options);
|
||||
if (selectorOrLocator._frame !== this._frame)
|
||||
throw new Error(`Locators must belong to the same frame.`);
|
||||
return new Locator(this._frame, this._selector + ' >> ' + selectorOrLocator._selector, options);
|
||||
}
|
||||
|
||||
getByTestId(testId: string | RegExp): Locator {
|
||||
@ -336,8 +340,12 @@ export class FrameLocator implements api.FrameLocator {
|
||||
this._frameSelector = selector;
|
||||
}
|
||||
|
||||
locator(selector: string, options?: { hasText?: string | RegExp }): Locator {
|
||||
return new Locator(this._frame, this._frameSelector + ' >> internal:control=enter-frame >> ' + selector, options);
|
||||
locator(selectorOrLocator: string | Locator, options?: LocatorOptions): Locator {
|
||||
if (isString(selectorOrLocator))
|
||||
return new Locator(this._frame, this._frameSelector + ' >> internal:control=enter-frame >> ' + selectorOrLocator, options);
|
||||
if (selectorOrLocator._frame !== this._frame)
|
||||
throw new Error(`Locators must belong to the same frame.`);
|
||||
return new Locator(this._frame, this._frameSelector + ' >> internal:control=enter-frame >> ' + selectorOrLocator._selector, options);
|
||||
}
|
||||
|
||||
getByTestId(testId: string | RegExp): Locator {
|
||||
|
8
packages/playwright-core/types/types.d.ts
vendored
8
packages/playwright-core/types/types.d.ts
vendored
@ -11392,10 +11392,10 @@ export interface Locator {
|
||||
* method.
|
||||
*
|
||||
* [Learn more about locators](https://playwright.dev/docs/locators).
|
||||
* @param selector A selector to use when resolving DOM element.
|
||||
* @param selectorOrLocator A selector or locator to use when resolving DOM element.
|
||||
* @param options
|
||||
*/
|
||||
locator(selector: string, options?: {
|
||||
locator(selectorOrLocator: string|Locator, 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 `<article><div>Playwright</div></article>`.
|
||||
@ -16969,10 +16969,10 @@ export interface FrameLocator {
|
||||
* method.
|
||||
*
|
||||
* [Learn more about locators](https://playwright.dev/docs/locators).
|
||||
* @param selector A selector to use when resolving DOM element.
|
||||
* @param selectorOrLocator A selector or locator to use when resolving DOM element.
|
||||
* @param options
|
||||
*/
|
||||
locator(selector: string, options?: {
|
||||
locator(selectorOrLocator: string|Locator, 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 `<article><div>Playwright</div></article>`.
|
||||
|
@ -154,3 +154,20 @@ it('locator.count should work with deleted Map in main world', async ({ page })
|
||||
await expect(page.locator('#searchResultTableDiv .x-grid3-row')).toHaveCount(0);
|
||||
});
|
||||
|
||||
it('Locator.locator() and FrameLocator.locator() should accept locator', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<div><input value=outer></div>
|
||||
<iframe srcdoc="<div><input value=inner></div>"></iframe>
|
||||
`);
|
||||
|
||||
const inputLocator = page.locator('input');
|
||||
expect(await inputLocator.inputValue()).toBe('outer');
|
||||
expect(await page.locator('div').locator(inputLocator).inputValue()).toBe('outer');
|
||||
expect(await page.frameLocator('iframe').locator(inputLocator).inputValue()).toBe('inner');
|
||||
expect(await page.frameLocator('iframe').locator('div').locator(inputLocator).inputValue()).toBe('inner');
|
||||
|
||||
const divLocator = page.locator('div');
|
||||
expect(await divLocator.locator('input').inputValue()).toBe('outer');
|
||||
expect(await page.frameLocator('iframe').locator(divLocator).locator('input').inputValue()).toBe('inner');
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user