如何强制 Puppeteer 等到非常大的 HTML table 中的所有行都已完全加载并显示在 DOM 中

How to force Puppeteer to wait until all rows in very large HTML table are fully loaded and displayed in the DOM

问题

问题总结:我正在编写几个测试程序uites(使用 Jest 和 Puppeteer)来自动测试我的 AngularJS 应用程序的主页页。 注意:我的一些 ui 组件由名为 AngularJS Material 的框架提供支持。我想自动化的测试之一是用户按下页面上的按钮重新加载 table。不幸的是,这个 table 用于显示大量数据,因此为了重新加载 table,客户端首先需要向我的服务器发出 GET 请求以提取 table的数据,才能在DOM中显示table。总而言之,整个过程大约需要一两秒钟。 所以这是我的问题: 我如何编写一些 Jest/Puppeteer 测试代码来等待 table 完全 loaded/displayed DOM(即显示所有 table 行数据)。

编辑澄清:

不能预先确定table中会有多少行。我知道根据我提供的最小示例,我似乎可以做到。但不幸的是,table 中的行数由用户添加的数据量决定。

我的测试环境概览:

代码/到目前为止我尝试过的

下面你会看到,我尝试了几种方法来等待所有行数据显示,但都没有任何效果。

<!-- index.html -->
<html>
  <body ng-app="myApp" ng-controller="myCtrl">
    <md-content class="tableContainer">
      <md-content class="table">
        <!-- UI component provided by Angular JS Material, appears while table is loading -->
        <md-progress-linear md-mode="indeterminate"></md-progress-linear>
        <table>
          <thead><!-- my table header --></thead>
          <tbody><!-- displays tons of data --></tbody>
        </table>
      </md-content>
    </md-content>
    <button id="reloadTableBtn" ng-click="myCtrl.reloadTableData()">Reload Table</button>
  </body>
</html>
// index.spec.js
test('reload table', async() => {

  let reloadTableBtnSelector = 'button[id="reloadTableBtn"]';
  await page.waitForSelector(reloadTableBtnSelector, {visible: true, timeout: globals.timeouts.selector});
  await page.click(reloadTableBtnSelector);

  /* attempt #1: wait for progress bar to disappear from display - fails
     for unknown reason perhaps because the progress bar disappears when
     the client gets response from the server, instead of when
     all data has been rendered 
  */
  let progressLinearSelector = 'md-content.mdtTable md-progress-linear';
  await page.waitForSelector(progressLinearSelector, {hidden: true, timeout: 3000});
  await page.waitFor(2000);

  /* attempt #2: wait for tbody to be added to the DOM - fails 
     b/c tbody is added to the DOM before all rows have been rendered
  */
  await page.waitForFunction(() => document.querySelector('table tbody'));

  /* attempt #3: wait to tbody to be displayed in the DOM - fails. 
     Jest throws Timeout Error for unknown reason
  */
  await page.waitForSelector('table tbody', {visible: true, timeout: 3000});

  /* attempt #4: just wait n milliseconds for the full table to be displayed 
     - not reliable (and prone to failure) b/c table might take more or less
     time than n seconds to load (depending on how much data is being rendered)
  */
  await page.waitFor(2000);
});

另一个可能的解决方案是等待所有网络连接完成。我有另一个测试通过以下方式执行此操作: await page.goto('https://my-website.com', {waitUntil: 'networkidle0'}); ...但是所有具有 waitUntil 选项的页面方法都涉及导航到不同的 webpages/reloading 网页,这不是我想要的。

结论

如果你们中的任何 Jest/Puppeteer 专家知道解决此问题的方法,我将非常感谢您的建议:)

等待 table 填满

最简单的方法可能是使用 page.waitForFunction 等待 table 填充足够的行。我想你知道大概有多少 table 行,所以你可以使用以下代码:

await page.waitForFunction(() => document.querySelectorAll('#table-selector tr').length >= 1000);

这会暂停脚本,直到 table 内至少有 1000 行。

正如您所说,条件是 "at least one row or a specific sentence",您可以将其更改为:

await page.waitForFunction(
  () => !!document.querySelector('#table-selector tr') || document.querySelector('#noresults-selector').innerText.includes('no results')
);

这会等到 table 至少有一行或直到给定选择器中有文本 no results

等待网络响应

我建议不要等到没有更多的网络流量,因为您的脚本可能仍需要几毫秒才能在下载数据后用数据填充 table。如果您仍然想尝试一下,我建议您在继续之前指定要等待的响应:

await page.waitForResponse(response => response.url().includes('/url-to-wait-for'));

使用 page.waitForResponse,代码等待直到收到特定 URL 的响应。