Sponsored Link
Looking for a unified way to  viewmonitor, and  debug Playwright Test Automation runs?
Try the Playwright Dashboard by today. Use the coupon code PWSOL10 for a 12 month 10% discount.

How Do You Scroll To The Bottom Of An Infinite Scrolling Page In A Playwright Test?

This week our team had a renewed focus on addressing some of our flakey tests, and one of the things we ran across was an interesting page interaction.

We wanted to utilize our drop down to limit the amount of responses within a page. The options for filtering  are 5, 10, 25, and 50. The drop down is at the bottom of the page, and we had some flakey tests where 1 in every 5 test runs, the screen would get in a state where the drop down is clicked and partially viewable but not scrolled down to the element to interact with it. See the video below.

0:00
/

The logs from the html report show where it attempted to scroll down but because the way the element is built, I guess it couldn't.

// html report error message

TimeoutError: locator.click: Timeout 10000ms exceeded.
=========================== logs ===========================
waiting for getByRole('option', { name: '25', exact: true })
  locator resolved to <mat-option role="option" id="mat-option-3" aria-selected="f…>…</mat-option>
attempting click action
  waiting for element to be visible, enabled and stable
    element is not stable - waiting...
  element is visible, enabled and stable
  scrolling into view if needed
  done scrolling
  element is outside of the viewport
retrying click action, attempt #1
  waiting for element to be visible, enabled and stable
  element is visible, enabled and stable
  scrolling into view if needed
  done scrolling
  element is outside of the viewport
...
// 22 more attempts were made before hitting the timeout threshold

Set click() to force true

As I thought about how to solve this problem, 2 ways came to mind. The first being just set force:true on the click action. This can be seen below. This is a good solution but I wanted to have the page scrolled like a user would rather than automagically clicking the button. Also note that I am using this.page syntax as I am running this code from a Class ( my page object).

// sharedPage.ts (page object)

 async selectItemsPerPage(howManyItems) {
    await this.itemsPerPage.locator(".mat-mdc-select-trigger").click();

    await this.page
      .getByRole("option", { name: howManyItems, exact: true })
      .click({ force: true });
  }

Scroll to the bottom of the page

So I started looking at how to implement ways to scroll to the bottom of the page. I started by actually trying to just use the .scrollIntoViewIfNeeded() function playwright, but I guess the browser thought the element was already in view much like the html report error logs showed it attempted to scroll into view.

My work around was to use page.evaluate, grab the document.body.scrollHeight and use window.scrollTo() the value. This function will also take into account any lazy loading pages that may grow as you scroll, this was not a concern of mine but the code will handle it, though you may need to adjust the delay.

// sharedPage.ts (page object)

 async selectItemsPerPage(howManyItems) {
    await this.itemsPerPage.locator(".mat-mdc-select-trigger").click();

    // Scroll to the bottom of the page
    await this.page.evaluate(async () => {
      const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
      for (let i = 0; i < document.body.scrollHeight; i += 100) {
        window.scrollTo(0, i);
        await delay(100);
      }
    });
    await this.page
      .getByRole("option", { name: howManyItems, exact: true })
      .click(;
  }

The above code was listed as a part of a solution to a GitHub question, if your looking for other alternatives or examples there are a few in the comments.


Thanks for reading! If you found this helpful, reach out and let me know on LinkedIn or consider buying me a cup of coffee. If you want more content delivered to you in your inbox subscribe below, and be sure to leave a ❤️ to show some love.