Puppeteer предоставляет мощный инструмент для автоматизации браузера на базе Chrome/Chromium, включая обработку асинхронных событий и отложенных задач. В тестировании веб-приложений нередко возникает необходимость дождаться завершения определённых процессов на странице — загрузки данных, анимаций, AJAX-запросов или выполнения таймеров. Puppeteer позволяет контролировать такие моменты с высокой точностью.
Любое взаимодействие с DOM в Puppeteer является асинхронным. Для работы с отложенными задачами используются методы ожидания, которые позволяют “заморозить” выполнение скрипта до наступления нужного состояния.
page.waitForSelector
Метод waitForSelector позволяет ожидать появления элемента
в DOM или его видимость. Используется, когда необходимо дождаться
загрузки контента, который создаётся динамически:
await page.waitForSelector(&
Параметр visible: true гарантирует, что элемент не только
существует, но и отображается на странице. Без него метод сработает даже
если элемент скрыт.
page.waitForFunction
Для более сложных условий, когда необходимо дождаться выполнения
определённого JavaScript-кода на странице, применяется
waitForFunction:
await page.waitForFunction(() => window.dataLoaded === true);
Этот метод проверяет указанную функцию в цикле до тех пор, пока она не
вернёт true или не истечёт тайм-аут. По умолчанию тайм-аут
равен 30 секундам, его можно изменить через параметр { timeout:
10000 }.
page.waitForTimeout
Иногда необходимо просто приостановить выполнение на фиксированное
время. Для этого существует waitForTimeout:
await page.waitForTimeout(2000); // пауза 2 секунды
Использовать его стоит минимально, так как фиксированная задержка может
быть менее надёжной при изменении скорости загрузки страниц.
Отложенные задачи через таймеры и асинхронные события
Веб-приложения часто используют setTimeout,
setInterval и асинхронные запросы. Puppeteer
предоставляет средства для контроля таких задач:
Отслеживание сетевых запросов
Метод page.waitForResponse позволяет дождаться ответа
определённого сетевого запроса:
await page.waitForResponse(response =>
response.url().includes('/api/data') && response.status() === 200
);
Такой подход особенно полезен при работе с SPA и динамическим контентом.
Ждём завершения всех сетевых запросов
Иногда необходимо дождаться полного завершения всех текущих сетевых
запросов перед взаимодействием с элементами:
await page.waitForNetworkIdle({ idleTime: 500, timeout: 10000 });
idleTime — период без сетевой активности, по истечении
которого считается, что загрузка завершена. Это важно при тестировании
страниц с множеством AJAX-вызовов.
Контроль таймеров и отложенных операций внутри страницы
Для тонкой настройки можно работать напрямую с таймерами внутри
страницы:
await page.evaluate(() => {
return new Promise(resolve => {
setTimeout(() => {
console.log('Таймер завершён');
resolve();
}, 3000);
});
});
Использование page.evaluate позволяет запускать код в
контексте страницы, что делает возможным ожидание выполнения отложенных
операций, создаваемых самим приложением.
Обработка долгих анимаций и переходов
В случае анимаций, слайдеров или других визуальных эффектов, ожидание
через селекторы или функции может быть недостаточным. Puppeteer
предоставляет возможность синхронизировать тест с анимациями:
await page.evaluate(() => {
return new Promise(resolve => {
const element = document.querySelector('#slider');
element.addEventListener('transitionend', resolve);
});
});
Это гарантирует, что действия с элементом выполняются только после
завершения визуального изменения.
Практические рекомендации
-
Использовать селекторы и функции вместо фиксированных
таймаутов. Это повышает стабильность тестов.
-
Комбинировать методы ожидания для сложных сценариев:
сначала дождаться сети, затем селектора, затем состояния элемента.
-
Настраивать тайм-ауты под конкретный тест, чтобы
избежать ложных срабатываний или зависаний.
-
Обрабатывать исключения: если элемент не появился или
сеть не успела завершить работу, важно использовать try/catch и выводить
информативные ошибки.
Примеры комплексного ожидания
try {
await page.waitForResponse(response => response.url().includes('/api/data') && response.status() === 200, { timeout: 5000 });
await page.waitForSelector('#results', { visible: true });
await page.waitForFunction(() => window.dataReady === true, { timeout: 3000 });
} catch (error) {
console.error('Не удалось дождаться всех отложенных задач:', error);
}
Такой подход обеспечивает последовательное ожидание сетевых, DOM и
пользовательских событий, что критично при тестировании сложных
веб-приложений.