Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Make Locator iterable #8680

Closed
mhils opened this issue Sep 3, 2021 · 2 comments
Closed

[Feature] Make Locator iterable #8680

mhils opened this issue Sep 3, 2021 · 2 comments

Comments

@mhils
Copy link
Contributor

mhils commented Sep 3, 2021

Problem

I'm using the Locator API a lot with lists. While evaluateAll is nice, the callback is executed in the page context where I don't have access to the rest of my codebase. Instead, I would like to iterate over elements in the client's context.

Current Approach

const tabs = page.locator(".tab");
for await (const tab of iterateLocator(tabs)) {
    await doFoo(tab);
}

// helper function
async function* iterateLocator(l: Locator): AsyncIterable<Locator> {
    const n = await l.count();
    for (let i = 0; i < n; i++) {
        yield l.nth(i);
    }
}

Proposal

It would be sweet if Locator implemented Symbol.asyncIterator with something similar to my helper above so that the following code would work:

const tabs = page.locator(".tab");
for await (const tab of tabs) {
    await doFoo(tab);
}

This probably has a worse performance than evaluateAll, but crucially allows me to keep things client-side.

Alternatives

One could also think of a Locator.list() function that would look like this:

for (const tab of await tabs.list()) {
    await doFoo(tab);
}

// Locator.list
async function locatorList(): Promise<Locator[]> {
    const n = await this.count();
    return [...Array(n)].map((_, i) => this.nth(i));
}

That approach is a bit more verbose, but maybe also a bit more explicit and flexible.


Just throwing this out here as an idea as I haven't seen in mentioned anywhere on the issue tracker. If this doesn't align with your vision for locators, please feel free to discard. :)

@yury-s
Copy link
Member

yury-s commented Sep 3, 2021

Thanks for filing and explaining this in details! I see some issues with implementing this as part of the locator API though.

evaluateAll does take an argument which you can use to pass some state from your app into the page, would that work?

It would be sweet if Locator implemented Symbol.asyncIterator with something similar to my helper above so that the following code would work:

The problem is that the implementation would have to call await l.count(); and the count may change over time so it's unclear which value should be used. When this is done in the client's code the user knows the logic of the app and can make assumptions that the count won't change. In general case we cannot assume that. With locators the idea is that they are resolved to the actual element every time an action needs to be done whereas with the iterator it would split into two steps: getting count and resolving individual item selector which is what we wanted to avoid with locators (compared to element handles). It is unlikely that we'll implement such api for that reason.

@yury-s yury-s closed this as completed Sep 3, 2021
@mxschmitt
Copy link
Member

Related to #12336.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants