繁体   English   中英

为什么当我使用“waitForSelector”时 Puppeeteer 导致我的测试套件挂起 30 秒,即使我在页面和浏览器上调用“关闭”?

[英]Why does Puppeeteer cause my test suite to hang for 30 seconds when I use "waitForSelector" even though I'm calling "close" on the page and browser?

我有一个 Node.js Mocha 测试套件(我根据我试图为其创建自动化测试的真实应用程序创建了一个最小复制)。

package.json :

{
  "name": "puppeteer-mocha-hang-repro",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "chai": "4.3.7",
    "express": "4.18.2",
    "mocha": "10.2.0",
    "puppeteer": "19.6.2"
  }
}

index.spec.js

const expect = require('chai').expect;
const express = require('express');
const puppeteer = require('puppeteer');

const webServerPort = 3001;

describe('test suite', function () {
    this.timeout(10000);

    let webServer;
    let browser;

    beforeEach(async () => {
        // Start web server using Express
        const app = express();
        app.get('/', (_, res) => {
            res.send('<html>Hello, World from the <span id="source">Express web server</span>!</html>');
        });
        webServer = app.listen(webServerPort, () => {
            console.log(`Web server listening on port ${webServerPort}.`);
        });

        // Start browser using Puppeteer
        browser = await puppeteer.launch();
        console.log('Browser launched.');
    });

    afterEach(async () => {
        // Stop browser
        await browser.close();
        console.log('Browser closed.');

        // Stop web server
        await webServer.close();
        console.log('Web server closed.');
    });

    it('should work', async () => {
        const page = await browser.newPage();

        await page.goto(`http://localhost:${webServerPort}/`);
        console.log('Went to root page of web server via Puppeteer.');

        if (process.env['PARSE_PAGE'] === 'true') {
            const sel = await page.waitForSelector('#source');
            const text = await sel.evaluate(el => el.textContent);
            console.log('According to Puppeteer, the text content of the #source element on the page is:', text);
            expect(text).eql('Express web server');
        }

        await page.close();
        console.log('Page closed.');
    });
});

如果我使用命令npx mocha index.spec.js运行测试套件,这会导致跳过第 45-48 行,测试套件通过并且 Mocha 进程快速结束:

$ time npx mocha index.spec.js


  test suite
Web server listening on port 3001.
Browser launched.
Went to root page of web server via Puppeteer.
Page closed.
    ✔ should work (70ms)
Browser closed.
Web server closed.


  1 passing (231ms)


real    0m0.679s
user    0m0.476s
sys     0m0.159s

请注意,它在 0.679 秒内完成。

如果我改为使用命令PARSE_PAGE=true npx mocha index.spec.js运行它,这不会导致我的代码被跳过,测试会很快通过,但过程会挂起大约 30 秒:

$ time PARSE_PAGE=true npx mocha index.spec.js


  test suite
Web server listening on port 3001.
Browser launched.
Went to root page of web server via Puppeteer.
According to Puppeteer, the text content of the #source element on the page is: Express web server
Page closed.
    ✔ should work (79ms)
Browser closed.
Web server closed.


  1 passing (236ms)


real    0m30.631s
user    0m0.582s
sys     0m0.164s

请注意,它在 30.631 秒内完成。

我怀疑这意味着我让事情保持开放状态,忘记调用close之类的函数。 但是,我在 Express web 服务器、Puppeteer 浏览器和 Puppeteer 页面上调用close 当我不跳过任何代码时,我尝试在我使用的对象上调用close ,它们是seltext 但是如果我尝试这样做,我会收到错误消息,告诉我这些对象没有这样的功能。

系统详情:

$ node --version
v18.13.0
$ npm --version
9.4.0
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.1 LTS
Release:        22.04
Codename:       jammy
$ uname -r
5.10.16.3-microsoft-standard-WSL

更新:此行为是由#9612修复并部署为19.6.3的回归。 要解决此问题,请升级到 19.6.3(如果您出于某种原因使用较旧的 Puppeteer,则降级到 <= 19.6.0)。

请参阅下面的原始答案。


即使没有摩卡,我也能够重现挂起。 这似乎是 Puppeteer 版本 19.6.1 和 19.6.2 中的错误。 这是一个最小的例子:

const puppeteer = require("puppeteer"); // 19.6.1 or 19.6.2

const html = `<!DOCTYPE html><html><body><p>hi</p></body></html>`;

let browser;
(async () => {
  browser = await puppeteer.launch();
  const [page] = await browser.pages();
  await page.setContent(html);
  const el = await page.waitForSelector("p");
  console.log(await el.evaluate(el => el.textContent));
})()
  .catch(err => console.error(err))
  .finally(async () => {
    await browser?.close();
    console.log("browser closed");
  });

罪魁祸首是page.waitForSelector ,它似乎在解析后仍运行其完整的 30 秒默认超时,以某种方式阻止进程退出。 我已经在 Puppeteer 的 GitHub 回购协议中打开了问题#9610

可能的解决方法:

  • 降级到 19.6.0。
  • 避免使用waitForSelector ,因为您想要的数据在 static HTML 中(尽管可能不适用于您的实际页面)。
  • 使用page.waitForSelector("#source", {timeout: 0})调用似乎可以解决问题,如果在脚本中使用则有永远停止的风险(mocha 不是问题,因为测试会超时)。
  • 使用page.waitForSelector("#source", {timeout: 1000})调用可减少延迟的影响,如果元素加载时间超过一秒,则存在误报风险。 这似乎没有叠加,所以如果你在许多测试中使用 1-3 秒的延迟,mocha 应该在所有测试完成后的几秒钟内退出,而不是所有waitForSelector调用的所有延迟的总和。 不过,这在大多数脚本中并不实用。
  • 运行npx mocha --exit index.spec.js 不推荐——这会抑制问题。

我不确定该行为是否特定于waitForTimeout或者它是否适用于其他waitFor系列方法。

顺便说一句,您的服务器监听和关闭调用在技术上是竞争条件,因此:

await new Promise(resolve =>
  webServer = app.listen(webServerPort, () => {
    console.log(`Web server listening on port ${webServerPort}.`);
    resolve();
  })
);

await new Promise(resolve => webServer.close(() => resolve()));

系统详情:

$ node --version
v18.7.0
$ npm --version
9.3.0
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.1 LTS
Release:    22.04
Codename:   jammy
$ uname -r
5.15.0-56-generic

我还确认了 Windows 10 上的行为。

我不确定它会有多大帮助,但你可以试试这个:

if (process.env['PARSE_PAGE'] === 'true') {
   const sel = await page.waitForSelector('#source');
   const text = await page.evaluate(el => el.textContent, sel);
   console.log('According to Puppeteer, the text content of the #source element on the page is:', text);
   expect(text).eql('Express web server');
}

另外,检查全局挂钩!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM