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] Locator.all: get list of Locator#nth(index) for each index #11909

Closed
YusukeIwaki opened this issue Feb 7, 2022 · 5 comments
Closed

Comments

@YusukeIwaki
Copy link
Contributor

#11865 (comment) shows a good example of how to use locator just like page.$$
I often use this approarch for scraping some search results, our company's daily reports, and so on.

async function handleItem(item) {
  // handling an item of search result
  ...
}

const itemsLocator = page.locator('.result-item');
const itemLocators = [];
for (let index = 0; index < await (itemsLocator.count()); index++)
  itemLocators.push(itemsLocator.nth(index));

// Handle search result concurrently
await Promise.all(itemLocators.map(item => handleItem(item)))

It would be cool if we can write the logic for collecting nth(index) of locator like const itemLocators = page.locator('.result-item').all()
I used Page.$$ before Locator is introduced, howevert ElementHandle is now discouraged. I want a method alternatives to Page.$$ for Locator.

@pavelfeldman
Copy link
Member

For scraping, you would more likely to do something like:

page.locator('.result-item').evaluateAll(ee => ee.forEach(e => ...))

No need to go back and forth between the browser and playwright...

@YusukeIwaki
Copy link
Contributor Author

No need to go back and forth between the browser and playwright...

It is right. In most cases, locator.evaluateAll is enough and brings better performance.
A bit of worry is that we can't use page/section object nor useful Playwright selectors in evaluateAll for handling the list with complex DOM structure at the cost of better performace.

class DailyReportItemSection {
  constructor(itemRoot) {
    this.title = itemRoot.locator('h2')
    this.description = itemRoot.locator('p')
    this.likes = itemRoot.locator('liked_container .like').all().map(loc => LikeItemSection(loc))
  }


class LikeItemSection {
  constructor(likeItemRoot) {
    this.avatar = likeItemRoot.locator('img')
    this.displayName = likeItemRoot.locator('span.name')
  }

I know $$ and ElementHandle is enough in this case, however it is already marked as "discouraged" in https://playwright.dev/docs/api/class-elementhandle .
So I just expected the alternative method of Locator that works like $$.

@dgozman
Copy link
Contributor

dgozman commented Feb 14, 2022

Note that locator.all() cannot be synchronous, because it does not know how many elements are in the page without asynchronously checking with the page. Therefore, it is no better than your snippet at the top that uses locator.count() and a for-loop.

However, you can work with lists instead of wrapping each locator individually if you'd like:

class LikeItemSections {
  constructor(likeItemRoots) {
    this.avatars = likeItemRoots.locator('img')
    this.displayNames = likeItemRoots.locator('span.name')
  }
}

// ...
this.likes = new LikeItemSections(itemRoot.locator('liked_container .like'))

At any moment, you can use a "list" locator to retrieve data or verify something with locator.allTextContents(), locator.evaluateAll(), expect(locator).toHaveText([...]) and the likes. This avoids the issues described here.

@dgozman
Copy link
Contributor

dgozman commented Feb 18, 2022

Closing because locator.all() is inherently flaky and goes against the philosophy of locators.

Two suggested workarounds:

  • use await locator.count() and iterate;
  • work with "list locators" all the way, instead of individual elements.

@dgozman dgozman closed this as completed Feb 18, 2022
@YusukeIwaki
Copy link
Contributor Author

Implemented in Playwright 1.29! Thank you!
#19461

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