最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin1浏览0评论

Problem

Summary of problem: I'm writing several test suites (using Jest and Puppeteer) to automate tests of my AngularJS app's home page. Note: Some of my ui ponents are powered by a framework called AngularJS Material. One of the tests I would like to automate is a user pressing a button on the page that reloads a table. Unfortunately, this table is used to display a large amount of data, so in order for the table to be reloaded, the client first needs to make GET request to my server to extract the table data from the db, and only then can the table be displayed in the DOM. All in all, this whole process takes about a second or two. So here's my question: how can I write some Jest/Puppeteer test code to WAIT for the table to be fully loaded/displayed in the DOM (i.e. ALL table row data is displayed).

Edit for clarification:

I cannot predetermine how many rows there will be in the table. I know like it may seem like I can based on the minimal example I provided. But unfortunately, the number of rows in the table is determined by how much data the user adds.

Overview of my testing environment:

  • Puppeteer version: 1.19.0
  • Jest version: 24.8.0

Code/ What I've tried so far

Below you will see that I've tried several methods to wait for all row data to be displayed, but nothing has worked yet.

<!-- index.html -->
<html>
  <body ng-app="myApp" ng-controller="myCtrl">
    <md-content class="tableContainer">
      <md-content class="table">
        <!-- UI ponent 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);
});

Another potential solution to this would be to wait for all the network connections to finish. I have another test that does this via: await page.goto('', {waitUntil: 'networkidle0'}); ... but all the page methods that have the waitUntil option available to them involve navigating to different webpages/reloading webpages, and that's not what I want.

Conclustion

If any of you Jest/Puppeteer experts out there know of a solution for this, I'd really appreciate your advice :)

Problem

Summary of problem: I'm writing several test suites (using Jest and Puppeteer) to automate tests of my AngularJS app's home page. Note: Some of my ui ponents are powered by a framework called AngularJS Material. One of the tests I would like to automate is a user pressing a button on the page that reloads a table. Unfortunately, this table is used to display a large amount of data, so in order for the table to be reloaded, the client first needs to make GET request to my server to extract the table data from the db, and only then can the table be displayed in the DOM. All in all, this whole process takes about a second or two. So here's my question: how can I write some Jest/Puppeteer test code to WAIT for the table to be fully loaded/displayed in the DOM (i.e. ALL table row data is displayed).

Edit for clarification:

I cannot predetermine how many rows there will be in the table. I know like it may seem like I can based on the minimal example I provided. But unfortunately, the number of rows in the table is determined by how much data the user adds.

Overview of my testing environment:

  • Puppeteer version: 1.19.0
  • Jest version: 24.8.0

Code/ What I've tried so far

Below you will see that I've tried several methods to wait for all row data to be displayed, but nothing has worked yet.

<!-- index.html -->
<html>
  <body ng-app="myApp" ng-controller="myCtrl">
    <md-content class="tableContainer">
      <md-content class="table">
        <!-- UI ponent 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);
});

Another potential solution to this would be to wait for all the network connections to finish. I have another test that does this via: await page.goto('https://my-website.', {waitUntil: 'networkidle0'}); ... but all the page methods that have the waitUntil option available to them involve navigating to different webpages/reloading webpages, and that's not what I want.

Conclustion

If any of you Jest/Puppeteer experts out there know of a solution for this, I'd really appreciate your advice :)

Share Improve this question edited Aug 26, 2019 at 18:05 ElsaInSpirit asked Aug 15, 2019 at 19:14 ElsaInSpiritElsaInSpirit 3416 silver badges17 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 7

Waiting until the table is filled

The easiest way is probably to use page.waitForFunction to wait until the table is filled with enough rows. I imagine you know how many table rows are roughly expected, so you could use the following code:

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

This pauses the script until there are at least 1000 rows inside of the table.

As you mentioned, that the condition is "at least one row or a specific sentence", you could change it to this:

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

This waits until the table has at least one row or until there is the text no results inside the given selector.

Waiting for the network response

I remend to not wait until there is no more network traffic, as your script might still need a few milliseconds to populate the table with data after downloading the data. In case, you still want to give it a try I remend to specify which response to wait for before continuing:

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

Using page.waitForResponse, the code waits until the response of a specific URL is received.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论