feat(api): introduce Locator.all, enumerate (#19461)

This commit is contained in:
Pavel Feldman 2022-12-14 16:42:50 -08:00 committed by GitHub
parent a1bb1dd94f
commit 17a0074459
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 174 additions and 12 deletions

View File

@ -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).
## 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
* since: v1.14
- 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.
## 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
* since: v1.14
- returns: <[Serializable]>

View File

@ -1360,34 +1360,58 @@ You should now have a "screenshot.png" file in your project's root directory.
### Rare use cases
#### Get All text contents
#### Do something with each element in the list
Iterate elements:
```js
const rows = page.getByRole('listitem');
const texts = await rows.allTextContents();
for (const row of await page.getByRole('listitem').all())
console.log(await row.textContent());
```
```python async
rows = page.get_by_role("listitem")
texts = await rows.all_text_contents()
for row in await page.get_by_role("listitem").all():
print(await row.text_content())
```
```python sync
rows = page.get_by_role("listitem")
texts = rows.all_text_contents()
for row in page.get_by_role("listitem").all():
print(row.text_content())
```
```java
Locator rows = page.getByRole(AriaRole.LISTITEM);
List<String> texts = rows.allTextContents();
for (Locator row : page.getByRole(AriaRole.LISTITEM).all())
System.out.println(row.textContent());
```
```csharp
var rows = page.GetByRole(AriaRole.Listitem);
var texts = await rows.AllTextContentsAsync();
foreach (var row in await page.GetByRole(AriaRole.Listitem).AllAsync())
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
const rows = page.getByRole('listitem');

View File

@ -292,6 +292,14 @@ export class Locator implements api.Locator {
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[]> {
return this._frame.$$eval(this._selector, ee => ee.map(e => (e as HTMLElement).innerText));
}

View File

@ -21,6 +21,8 @@ import { ReadStream } from 'fs';
import { Protocol } from './protocol';
import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from './structs';
type Tuple<A,B> = [A,B];
type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & {
state?: 'visible'|'attached';
};
@ -9803,6 +9805,19 @@ export interface Locator {
elementHandle(options?: {
timeout?: number;
}): 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.
*/
@ -10239,6 +10254,20 @@ export interface Locator {
*/
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].
*

View 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);
});

View File

@ -20,6 +20,8 @@ import { ReadStream } from 'fs';
import { Protocol } from './protocol';
import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from './structs';
type Tuple<A,B> = [A,B];
type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & {
state?: 'visible'|'attached';
};