Обработка отложенных задач

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 и пользовательских событий, что критично при тестировании сложных веб-приложений.