Кастомные условия ожидания

Puppeteer предоставляет множество встроенных методов для ожидания элементов на странице, таких как waitForSelector, waitForTimeout, и waitForFunction. Однако в некоторых случаях требуется более специфичная логика ожидания, которая не подходит под стандартные методы. В таких ситуациях можно создавать кастомные условия ожидания. Это особенно полезно при тестировании динамических страниц, где требуется дождаться выполнения специфичных условий, не ограничиваясь простым наличием элемента или тайм-аутом.

Основы кастомных условий ожидания

Для создания кастомного условия ожидания в Puppeteer используется метод waitForFunction. Он позволяет указать функцию, которая будет выполняться в контексте браузера до тех пор, пока не вернёт истинное значение. Важной особенностью является то, что waitForFunction принимает два аргумента:

  1. Функция – это условие, которое должно быть выполнено на странице (например, элемент должен появиться, данные должны быть загружены и т.д.).
  2. Опции – объект с параметрами, такими как тайм-аут, периодичность опроса и другие.

Пример кастомного условия ожидания

Одним из распространённых примеров кастомного условия является ожидание изменения текста на странице. Вместо того чтобы ожидать появления элемента, можно проверять, что его текст стал таким, как требуется.

await page.waitForFunction(
  &
  { timeout: 5000 }
);

В данном примере Puppeteer будет проверять на странице, что текст элемента с id element стал равен “Ожидаемый текст”. Если за 5 секунд это условие не выполнится, будет выброшена ошибка.

Ожидание изменений в данных

В некоторых случаях необходимо дождаться, пока на странице произойдут изменения, такие как обновление данных, например, загрузка новой информации с сервера через AJAX. Для этого можно использовать кастомные условия ожидания.

await page.waitForFunction(
  'window.someDataLoaded === true',
  { timeout: 10000 }
);

Здесь someDataLoaded — это переменная на глобальном объекте window, которая устанавливается в true, когда данные были успешно загружены. Puppeteer будет проверять это условие каждую секунду в течение 10 секунд.

Ожидание события на странице

В некоторых случаях бывает полезно дождаться не просто выполнения какого-либо условия, а события на странице. Например, это может быть клик по элементу, отправка формы или завершение анимации.

await page.waitForFunction(() => {
  return document.querySelector('#submitButton').disabled === false;
}, { timeout: 5000 });

В этом примере Puppeteer будет ожидать, пока кнопка с id submitButton не станет активной (не будет отключена), что может быть полезно при автоматическом тестировании форм или других интерактивных элементов.

Ожидание сетевых запросов

Порой необходимо дождаться завершения сетевого запроса или отклика сервера перед выполнением дальнейших шагов в тестах. Для таких случаев Puppeteer предоставляет метод waitForResponse, однако можно создать кастомное условие с использованием функции waitForFunction.

Пример ожидания завершения сетевого запроса:

await page.waitForFunction(() => {
  return window.performance.getEntriesByType('resource')
    .some(entry => entry.name.includes('data.json') && entry.responseEnd > 0);
}, { timeout: 10000 });

Этот код позволяет дождаться завершения запроса к ресурсу data.json и его полной загрузки.

Тайм-ауты и периодичность

В процессе работы с кастомными условиями часто бывает полезно настроить тайм-ауты и периодичность, с которой будет проверяться выполнение условия. Для этого используются параметры timeout и polling в методе waitForFunction.

  • timeout – максимальное время ожидания перед выбросом исключения, если условие не выполнится.
  • polling – частота, с которой проверяется условие. Можно использовать значения как в миллисекундах (100), так и строки типа raf (requestAnimationFrame) или mutation (для отслеживания изменений DOM).

Пример использования с настройкой периодичности:

await page.waitForFunction(
  'document.querySelector("#spinner").style.display === "none"',
  { timeout: 10000, polling: 'mutation' }
);

В этом примере Puppeteer будет ожидать, что элемент с id spinner станет скрытым, при этом проверка будет происходить не через интервалы времени, а при каждом изменении DOM-дерева.

Использование кастомных условий в асинхронных операциях

В некоторых ситуациях необходимо дожидаться выполнения асинхронных операций, таких как загрузка данных через API или другие операции, которые требуют выполнения кода на сервере. Для этого можно интегрировать кастомные ожидания с асинхронными функциями.

await page.waitForFunction(async () => {
  const response = await fetch('/api/status');
  const data = await response.json();
  return data.status === 'ready';
}, { timeout: 15000 });

В данном примере Puppeteer будет ожидать, пока сервер не вернёт статус ready через API.

Совмещение с другими методами ожидания

Кастомные условия ожидания могут быть использованы в сочетании с другими встроенными методами Puppeteer, такими как waitForSelector, waitForTimeout, и т.д. Это позволяет более гибко настраивать тесты, комбинируя различные типы ожиданий.

Пример комбинирования с ожиданием появления элемента:

await page.waitForSelector('#element');
await page.waitForFunction(
  'document.querySelector("#element").textContent === "Ожидаемый текст"',
  { timeout: 5000 }
);

В данном случае сначала ожидается, пока элемент с id element появится на странице, а затем проверяется, что его текст стал равным “Ожидаемый текст”.

Заключение

Кастомные условия ожидания в Puppeteer позволяют значительно повысить гибкость тестов и автоматизации. Использование методов, таких как waitForFunction, даёт возможность детально настроить логику ожидания и синхронизации с динамическими изменениями на странице. Важно учитывать, что использование кастомных условий должно быть оправдано реальными требованиями, так как неправильно настроенные условия могут привести к излишним задержкам и неэффективности тестов.