如何强制 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 中的行数由用户添加的数据量决定。
我的测试环境概览:
- 人偶版本:1.19.0
- 开玩笑版本:24.8.0
代码/到目前为止我尝试过的
下面你会看到,我尝试了几种方法来等待所有行数据显示,但都没有任何效果。
<!-- 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 的响应。
问题
问题总结:我正在编写几个测试程序uites(使用 Jest 和 Puppeteer)来自动测试我的 AngularJS 应用程序的主页页。 注意:我的一些 ui 组件由名为 AngularJS Material 的框架提供支持。我想自动化的测试之一是用户按下页面上的按钮重新加载 table。不幸的是,这个 table 用于显示大量数据,因此为了重新加载 table,客户端首先需要向我的服务器发出 GET 请求以提取 table的数据,才能在DOM中显示table。总而言之,整个过程大约需要一两秒钟。 所以这是我的问题: 我如何编写一些 Jest/Puppeteer 测试代码来等待 table 完全 loaded/displayed DOM(即显示所有 table 行数据)。
编辑澄清:
我不能预先确定table中会有多少行。我知道根据我提供的最小示例,我似乎可以做到。但不幸的是,table 中的行数由用户添加的数据量决定。
我的测试环境概览:
- 人偶版本:1.19.0
- 开玩笑版本:24.8.0
代码/到目前为止我尝试过的
下面你会看到,我尝试了几种方法来等待所有行数据显示,但都没有任何效果。
<!-- 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 的响应。