У меня есть набор тестов 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.info(`Web server listening on port ${webServerPort}.`);
});
// Start browser using Puppeteer
browser = await puppeteer.launch();
console.info('Browser launched.');
});
afterEach(async () => {
// Stop browser
await browser.close();
console.info('Browser closed.');
// Stop web server
await webServer.close();
console.info('Web server closed.');
});
it('should work', async () => {
const page = await browser.newPage();
await page.goto(`http://localhost:${webServerPort}/`);
console.info('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.info('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.info('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
. Но я звоню close
на веб-сервер Express, в браузер Puppeteer и на страницу Puppeteer. Я пробовал вызывать close
для объектов, которые я использую, когда я не пропускаю ни один из этих кодов, то есть sel
и text
. Но если я попытаюсь это сделать, я получу сообщение об ошибке, говорящее мне, что эти объекты не имеют таких функций.
Детали системы:
$ 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
В моем коде уже есть операторы console.info
после каждой операции, которая делает что-то значимое. Последний — «Веб-сервер закрыт». который печатается после закрытия веб-сервера Express в хуке afterEach
. Я понимаю, что это означает, что каждая из функций, которые я определил в своем коде, выполнялась быстро (хук beforeEach
, хук afterEach
, мой тест it
и мой набор describe
).
есть задержка 30 секунд или они все печатают быстро?
Все строки console.info
в моем коде печатаются быстро. Для завершения всего процесса Mocha требуется около 30 секунд (после чего он заканчивается с кодом состояния 0).
попробуйте указать тайм-аут здесь: const sel = await page.waitForSelector('#source');
Я не уверен, насколько это будет полезно, но вы можете попробовать это:
if (process.env['PARSE_PAGE'] === 'true') {
const sel = await page.waitForSelector('#source');
const text = await page.evaluate(el => el.textContent, sel);
console.info('According to Puppeteer, the text content of the #source element on the page is:', text);
expect(text).eql('Express web server');
}
Кроме того, проверьте наличие глобальных хуков!
Это не помогло. Можете ли вы уточнить, что вы подразумеваете под глобальными хуками? Вы имеете в виду что-то конкретное либо для Puppeteer, либо для Mocha, либо для Node.js в целом?
У меня нет большого опыта работы с Puppeteer, но я некоторое время пользовался Playwright. В Playwright вы можете создать файл global-setup.js/ts для настройки действий до и после тестирования.
Также попробуйте закрыть браузер после всех тестов. Это может сэкономить вам время. Bcoz, вам не придется заново открывать браузер для каждого теста. Вы просто продолжите тестирование в том же браузере, который был открыт изначально.
Ничто в этом ответе точно не определяет проблему и не предлагает ее решение, просто немного перетасовывает синтаксис. Глобальная настройка и использование одного браузера в нескольких тестах — хорошие предложения, но, насколько я могу судить, совершенно не связанные с проблемой.
Я ценю предложение повторно использовать браузер в нескольких тестах. Это может быть полезно для тех, кто сталкивается с этим вопросом. В моем случае я специально использовал новый браузер для каждого теста, потому что, на мой взгляд, это устраняло еще один класс проблем (повторно используемое состояние браузера). Моя цель публикации этого состояла в том, чтобы иметь минимальное воспроизведение. Моя цель позже будет состоять в том, чтобы оптимизировать мой набор тестов, как только я решу эту проблему.
Обновление: это поведение является регрессией, исправленной #9612 и развернутой как 19.6.3. Чтобы решить эту проблему, обновитесь до 19.6.3 (или откатитесь до <= 19.6.0, если по какой-то причине вы используете более старый Puppeteer).
Смотрите оригинальный ответ ниже.
У меня получается воспроизвести зависание даже без мокко. Кажется, это ошибка в версиях 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.info(await el.evaluate(el => el.textContent));
})()
.catch(err => console.error(err))
.finally(async () => {
await browser?.close();
console.info("browser closed");
});
Виновником является page.waitForSelector
, который, кажется, запускает свой полный 30-секундный тайм-аут по умолчанию даже после разрешения, каким-то образом предотвращая выход процесса. Я открыл выпуск #9610 в репозитории Puppeteer на GitHub.
Возможные обходные пути:
waitForSelector
, так как нужные вам данные находятся в статическом HTML (хотя это может не относиться к вашей фактической странице).page.waitForSelector("#source", {timeout: 0})
, который, кажется, решает проблему, с риском зависания навсегда, если используется в скрипте (это не проблема с мокко, так как время теста истекает).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.info(`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.
Спасибо, что попробовали мой код и воспроизвели зависание. Ваше предложение использовать статический HTML-код страницы не сработает, когда я решу эту проблему, потому что в моем случае это просто минимальное воспроизведение с целью публикации этого в Stack Overflow, а позже я буду использовать DOM гораздо больше, ожидание событий и т. д., чтобы создать набор тестов для моих реальных приложений (которые включают в себя внутренний сервер Express, интерфейсный SPA и сторонний сервер, с которым взаимодействуют как внутренний, так и внешний интерфейс) .
Я собираюсь попробовать другие ваши предложения позже, когда у меня будет время, чтобы попытаться разобраться, правильно ли я звоню waitForSelector
и т. д. И затем я могу создать отчет об ошибке, если сделаю вывод, что правильно использую библиотеку и думаю это баг с библиотекой.
Да, я упоминал об этом в посте. Это одно из четырех предложений, добавленных только для полноты вариантов использования, которым не нужно ждать. Вы можете игнорировать его, если этот вариант не относится к вам. Вариант {timeout: 0}
— это основное предложение, которое я предлагаю здесь на данный момент. Любопытно услышать, как у вас дела, и рад видеть ссылку на проблему, если вы ее откроете.
Я посмотрел на это немного больше. Переход на 19.6.0 устраняет проблему для меня, так что это моя текущая рекомендация. Я открыла выпуск GH.
Спасибо. Это будет моим планом действий на данный момент. Я приму этот ответ, так как решения нет, и нам нужно выпустить исправление ошибки.
Звучит хорошо, спасибо за супер ясный вопрос. Интересно, проблема в этом изменении в WaitTask, которое более строго охраняет clearTimeout
?
Кажется, это все. Я открыл PR, так что это должно быть решено в следующей версии.
19.6.3 развернут с исправлением.
добавьте некоторый оператор печати, чтобы выяснить, какая функция работает медленно.