🤡

Playwrightで無限スクロールに対応し特定の要素が出るまで執拗にスクロールし続ける

2022/11/01に公開
  1. スクロール
  2. fetchして5つ要素が返ってくる
  3. スクロール
  4. fetchして5つ要素が返ってくる

みたいなよくあるInfinitieScrollを自前で実装してる部分があり、例えば5回くらいスクロールした結果降ってくる要素をexpectしたい

ググる

https://github.com/microsoft/playwright/issues/5052

https://stackoverflow.com/questions/62467373/scrolling-with-puppeteer-playwright-not-working-for-sapui5-app

適当なワードでggるとここら辺が出てくる

elementHandle.scrollIntoViewIfNeeded([options])
https://playwright.dev/docs/api/class-elementhandle#element-handle-scroll-into-view-if-needed

こんなものがあったりするが弊環境では正常に動作しなかった

解決方法

引き続きggってるとこんなものを見つけた

mouse.wheel(deltaX, deltaY)
https://playwright.dev/docs/api/class-mouse#mouse-wheel

幸い、今回はページ全体の縦スクロールとは別に特定の要素のみがoverflor-y: scroll;かつ無限スクロールな感じなのではなく、ページ全体のスクロールに連動して無限スクロールが発火するモノだったのでこちらで行けた

    let target = await page.$$('text=hogehoge')
    while (!target.length) {
      await page.mouse.wheel(0, 10000) // TODO: ここの数値ちゃんとする
      target = await page.$$('text=hogehoge')
    }

これで特定の要素がAPIから返してもらえるまで一生スクロールできる

ちなみにlocatorを使うとLocator型がつくけどこいつはlengthに対応してなく、また、存在の有無を真偽値で取る方法が見当たらなかった

/**
 * Locators are the central piece of Playwright's auto-waiting and retry-ability. In a nutshell, locators represent a way
 * to find element(s) on the page at any moment. Locator can be created with the
 * [page.locator(selector[, options])](https://playwright.dev/docs/api/class-page#page-locator) method.
 *
 * [Learn more about locators](https://playwright.dev/docs/locators).
 */
export interface Locator {
  /**
   * Returns the return value of `pageFunction`.
   *
   * This method passes this handle as the first argument to `pageFunction`.
   *
   * If `pageFunction` returns a [Promise], then `handle.evaluate` would wait for the promise to resolve and return its
   * value.
   *
   * Examples:
   *
   * ```js
   * const tweets = page.locator('.tweet .retweets');
   * expect(await tweets.evaluate(node => node.innerText)).toBe('10 retweets');
   * ```
   *
   * @param pageFunction Function to be evaluated in the page context.
   * @param arg Optional argument to pass to `pageFunction`.
   * @param options
   */
  evaluate<R, Arg, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(pageFunction: PageFunctionOn<E, Arg, R>, arg: Arg, options?: {
    timeout?: number;
  }): Promise<R>;

なのでpage.locator('text=hogehoge')でなく、page.$$('text=hogehoge')で対応した

  /**
   * > NOTE: The use of [ElementHandle] is discouraged, use [Locator] objects and web-first assertions instead.
   *
   * The method finds all elements matching the specified selector within the page. If no elements match the selector, the
   * return value resolves to `[]`.
   *
   * Shortcut for main frame's [frame.$$(selector)](https://playwright.dev/docs/api/class-frame#frame-query-selector-all).
   * @param selector A selector to query for. See [working with selectors](https://playwright.dev/docs/selectors) for more details.
   */
  $$(selector: string): Promise<ElementHandle<SVGElement | HTMLElement>[]>;

あまり良いやり方ではないので詳しい方いたらご教授くださいまし〜〜〜〜〜

Discussion