mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(api): introduce Locator.all, enumerate (#19461)
This commit is contained in:
parent
a1bb1dd94f
commit
17a0074459
@ -6,6 +6,40 @@ a way to find element(s) on the page at any moment. Locator can be created with
|
|||||||
|
|
||||||
[Learn more about locators](../locators.md).
|
[Learn more about locators](../locators.md).
|
||||||
|
|
||||||
|
## async method: Locator.all
|
||||||
|
* since: v1.14
|
||||||
|
- returns: <[Array]<[Locator]>>
|
||||||
|
|
||||||
|
When locator points to a list of elements, returns array of locators, pointing
|
||||||
|
to respective elements.
|
||||||
|
|
||||||
|
**Usage**
|
||||||
|
|
||||||
|
```js
|
||||||
|
for (const li of await page.getByRole('listitem').all())
|
||||||
|
await li.click();
|
||||||
|
```
|
||||||
|
|
||||||
|
```python async
|
||||||
|
for li in await page.get_by_role('listitem').all():
|
||||||
|
await li.click();
|
||||||
|
```
|
||||||
|
|
||||||
|
```python sync
|
||||||
|
for li in page.get_by_role('listitem').all():
|
||||||
|
li.click();
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
for (Locator li : page.getByRole('listitem').all())
|
||||||
|
li.click();
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
foreach (var li in await page.GetByRole('listitem').AllAsync())
|
||||||
|
await li.ClickAsync();
|
||||||
|
```
|
||||||
|
|
||||||
## async method: Locator.allInnerTexts
|
## async method: Locator.allInnerTexts
|
||||||
* since: v1.14
|
* since: v1.14
|
||||||
- returns: <[Array]<[string]>>
|
- returns: <[Array]<[string]>>
|
||||||
@ -424,6 +458,36 @@ Resolves given locator to the first matching DOM element. If no elements matchin
|
|||||||
|
|
||||||
Resolves given locator to all matching DOM elements.
|
Resolves given locator to all matching DOM elements.
|
||||||
|
|
||||||
|
## async method: Locator.enumerate
|
||||||
|
* since: v1.14
|
||||||
|
* langs: js, python, csharp
|
||||||
|
- returns: <[Array]<[Tuple]<[Locator],[int]>>>
|
||||||
|
|
||||||
|
When locator points to a list of elements, returns array of (locator, index) pairs,
|
||||||
|
pointing to respective elements.
|
||||||
|
|
||||||
|
**Usage**
|
||||||
|
|
||||||
|
```js
|
||||||
|
for (const [li, i] of await page.getByRole('listitem').enumerate())
|
||||||
|
await li.click();
|
||||||
|
```
|
||||||
|
|
||||||
|
```python async
|
||||||
|
for (li, index) in await page.get_by_role('listitem').enumerate():
|
||||||
|
await li.click();
|
||||||
|
```
|
||||||
|
|
||||||
|
```python sync
|
||||||
|
for (li, index) in page.get_by_role('listitem').enumerate():
|
||||||
|
li.click();
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
foreach (var (li, index) in await page.GetByRole('listitem').AllAsync())
|
||||||
|
await li.ClickAsync();
|
||||||
|
```
|
||||||
|
|
||||||
## async method: Locator.evaluate
|
## async method: Locator.evaluate
|
||||||
* since: v1.14
|
* since: v1.14
|
||||||
- returns: <[Serializable]>
|
- returns: <[Serializable]>
|
||||||
|
@ -1360,34 +1360,58 @@ You should now have a "screenshot.png" file in your project's root directory.
|
|||||||
|
|
||||||
### Rare use cases
|
### Rare use cases
|
||||||
|
|
||||||
#### Get All text contents
|
#### Do something with each element in the list
|
||||||
|
|
||||||
|
Iterate elements:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const rows = page.getByRole('listitem');
|
for (const row of await page.getByRole('listitem').all())
|
||||||
const texts = await rows.allTextContents();
|
console.log(await row.textContent());
|
||||||
```
|
```
|
||||||
|
|
||||||
```python async
|
```python async
|
||||||
rows = page.get_by_role("listitem")
|
for row in await page.get_by_role("listitem").all():
|
||||||
texts = await rows.all_text_contents()
|
print(await row.text_content())
|
||||||
```
|
```
|
||||||
|
|
||||||
```python sync
|
```python sync
|
||||||
rows = page.get_by_role("listitem")
|
for row in page.get_by_role("listitem").all():
|
||||||
texts = rows.all_text_contents()
|
print(row.text_content())
|
||||||
```
|
```
|
||||||
|
|
||||||
```java
|
```java
|
||||||
Locator rows = page.getByRole(AriaRole.LISTITEM);
|
for (Locator row : page.getByRole(AriaRole.LISTITEM).all())
|
||||||
List<String> texts = rows.allTextContents();
|
System.out.println(row.textContent());
|
||||||
```
|
```
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
var rows = page.GetByRole(AriaRole.Listitem);
|
foreach (var row in await page.GetByRole(AriaRole.Listitem).AllAsync())
|
||||||
var texts = await rows.AllTextContentsAsync();
|
Console.WriteLine(await row.TextContentAsync());
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Do something with each element in the list
|
Iterate elements with their respective indexes:
|
||||||
|
|
||||||
|
```js
|
||||||
|
for (const [row, index] of await page.getByRole('listitem').enumerate())
|
||||||
|
console.log(index, await row.textContent());
|
||||||
|
```
|
||||||
|
|
||||||
|
```python async
|
||||||
|
for (row, index) in await page.get_by_role('listitem').enumerate():
|
||||||
|
print(index, await row.text_content())
|
||||||
|
```
|
||||||
|
|
||||||
|
```python sync
|
||||||
|
for (row, index) in page.get_by_role('listitem').enumerate():
|
||||||
|
print(index, row.text_content())
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
foreach (var (row, index) in await page.GetByRole('listitem').AllAsync())
|
||||||
|
Console.WriteLine(index + ' ' + await row.TextContentAsync());
|
||||||
|
```
|
||||||
|
|
||||||
|
Iterate using regular for loop:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const rows = page.getByRole('listitem');
|
const rows = page.getByRole('listitem');
|
||||||
|
@ -292,6 +292,14 @@ export class Locator implements api.Locator {
|
|||||||
return this._frame.uncheck(this._selector, { strict: true, ...options });
|
return this._frame.uncheck(this._selector, { strict: true, ...options });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async all(): Promise<Locator[]> {
|
||||||
|
return new Array(await this.count()).fill(0).map((e, i) => this.nth(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
async enumerate(): Promise<[Locator, number][]> {
|
||||||
|
return new Array(await this.count()).fill(0).map((e, i) => [this.nth(i), i]);
|
||||||
|
}
|
||||||
|
|
||||||
async allInnerTexts(): Promise<string[]> {
|
async allInnerTexts(): Promise<string[]> {
|
||||||
return this._frame.$$eval(this._selector, ee => ee.map(e => (e as HTMLElement).innerText));
|
return this._frame.$$eval(this._selector, ee => ee.map(e => (e as HTMLElement).innerText));
|
||||||
}
|
}
|
||||||
|
29
packages/playwright-core/types/types.d.ts
vendored
29
packages/playwright-core/types/types.d.ts
vendored
@ -21,6 +21,8 @@ import { ReadStream } from 'fs';
|
|||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from './structs';
|
import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from './structs';
|
||||||
|
|
||||||
|
type Tuple<A,B> = [A,B];
|
||||||
|
|
||||||
type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & {
|
type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & {
|
||||||
state?: 'visible'|'attached';
|
state?: 'visible'|'attached';
|
||||||
};
|
};
|
||||||
@ -9803,6 +9805,19 @@ export interface Locator {
|
|||||||
elementHandle(options?: {
|
elementHandle(options?: {
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
}): Promise<null|ElementHandle<SVGElement | HTMLElement>>;
|
}): Promise<null|ElementHandle<SVGElement | HTMLElement>>;
|
||||||
|
/**
|
||||||
|
* When locator points to a list of elements, returns array of locators, pointing to respective elements.
|
||||||
|
*
|
||||||
|
* **Usage**
|
||||||
|
*
|
||||||
|
* ```js
|
||||||
|
* for (const li of await page.getByRole('listitem').all())
|
||||||
|
* await li.click();
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
all(): Promise<Array<Locator>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of `node.innerText` values for all matching nodes.
|
* Returns an array of `node.innerText` values for all matching nodes.
|
||||||
*/
|
*/
|
||||||
@ -10239,6 +10254,20 @@ export interface Locator {
|
|||||||
*/
|
*/
|
||||||
elementHandles(): Promise<Array<ElementHandle>>;
|
elementHandles(): Promise<Array<ElementHandle>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When locator points to a list of elements, returns array of (locator, index) pairs, pointing to respective
|
||||||
|
* elements.
|
||||||
|
*
|
||||||
|
* **Usage**
|
||||||
|
*
|
||||||
|
* ```js
|
||||||
|
* for (const [li, i] of await page.getByRole('listitem').enumerate())
|
||||||
|
* await li.click();
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
enumerate(): Promise<Array<Tuple<Locator, number>>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the return value of `pageFunction` as a [JSHandle].
|
* Returns the return value of `pageFunction` as a [JSHandle].
|
||||||
*
|
*
|
||||||
|
35
tests/page/locator-list.spec.ts
Normal file
35
tests/page/locator-list.spec.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { test as it, expect } from './pageTest';
|
||||||
|
|
||||||
|
it('locator.all should work', async ({ page }) => {
|
||||||
|
await page.setContent(`<div><p>A</p><p>B</p><p>C</p></div>`);
|
||||||
|
const texts = [];
|
||||||
|
for (const p of await page.locator('div >> p').all())
|
||||||
|
texts.push(await p.textContent());
|
||||||
|
expect(texts).toEqual(['A', 'B', 'C']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('locator.enumerate should work', async ({ page }) => {
|
||||||
|
await page.setContent(`<div><p>0</p><p>1</p><p>2</p><p>3</p></div>`);
|
||||||
|
let items = 0;
|
||||||
|
for (const [p, i] of await page.locator('div >> p').enumerate()) {
|
||||||
|
++items;
|
||||||
|
expect(await p.textContent()).toBe(String(i));
|
||||||
|
}
|
||||||
|
expect(items).toBe(4);
|
||||||
|
});
|
2
utils/generate_types/overrides.d.ts
vendored
2
utils/generate_types/overrides.d.ts
vendored
@ -20,6 +20,8 @@ import { ReadStream } from 'fs';
|
|||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from './structs';
|
import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from './structs';
|
||||||
|
|
||||||
|
type Tuple<A,B> = [A,B];
|
||||||
|
|
||||||
type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & {
|
type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & {
|
||||||
state?: 'visible'|'attached';
|
state?: 'visible'|'attached';
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user