Ожидание Angular готовности

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


Основной принцип работы Protractor с Angular

Protractor расширяет Selenium WebDriver, добавляя интеграцию с Angular. Основная идея заключается в том, что тесты автоматически ожидают завершения всех асинхронных операций Angular перед взаимодействием с элементами страницы. Это достигается через механизм waiting for Angular, который использует следующие методы Angular:

  • $browser.notifyWhenNoOutstandingRequests – оповещает, когда нет активных HTTP-запросов или таймеров.
  • digest/apply – следит за завершением циклов проверки изменений (digest cycle).

Когда Protractor открывает страницу Angular, он автоматически активирует эти механизмы, если включен режим синхронизации.


Синхронизация с Angular

По умолчанию Protractor синхронизируется с Angular на каждой странице. Этот процесс управляется флагом:

browser.waitForAngularEnabled(true);

Примеры использования:

// Включение ожидания Angular
browser.waitForAngularEnabled(true);

// Отключение ожидания (например, для не-Angular страниц)
browser.waitForAngularEnabled(false);

Важно помнить, что для страниц, не использующих Angular, автоматическое ожидание приведет к зависанию теста, так как Protractor будет ожидать событий Angular, которых нет.


Ожидания элементов

Protractor использует ExpectedConditions для проверки состояния элементов, но внутри Angular-страниц можно полагаться на автоматическое ожидание готовности Angular:

const EC = protractor.ExpectedConditions;
let button = element(by.id('submit'));

browser.wait(EC.elementToBeClickable(button), 5000);
button.click();

Однако, если страница активно взаимодействует с асинхронными сервисами Angular, это может быть недостаточно. В таких случаях используется browser.waitForAngular():

// Явное ожидание завершения всех задач Angular
browser.waitForAngular().then(() => {
    button.click();
});

Метод waitForAngular() гарантирует, что все $http-запросы и таймеры завершены, и состояние приложения стабильно.


Работа с не-Angular частями страницы

Часто в приложении есть страницы или виджеты, не зависящие от Angular. В таких ситуациях необходимо временно отключать синхронизацию:

browser.waitForAngularEnabled(false);
browser.get('https://example.com/non-angular');
element(by.css('.login')).click();

После работы с не-Angular страницей синхронизация возвращается:

browser.waitForAngularEnabled(true);

Это предотвращает зависания и ошибки тестов, вызванные ожиданием событий Angular там, где их нет.


Отслеживание асинхронных операций

Angular использует промисы и внутренние таймеры, что иногда требует явного ожидания в тестах:

element(by.model('username')).sendKeys('user');
element(by.model('password')).sendKeys('pass');

// Явное ожидание Angular перед кликом
browser.waitForAngular().then(() => {
    element(by.id('login')).click();
});

Protractor также автоматически отслеживает промисы, возвращаемые Angular-сервисами, что снижает необходимость ручного управления асинхронностью.


Настройка таймаутов

Важным аспектом работы с ожиданием Angular является таймаут ожидания. Он настраивается через allScriptsTimeout в конфигурации Protractor:

exports.config = {
    allScriptsTimeout: 11000,  // Время ожидания выполнения скриптов Angular
    getPageTimeout: 10000
};
  • allScriptsTimeout – максимальное время ожидания завершения всех асинхронных операций Angular.
  • getPageTimeout – максимальное время ожидания загрузки страницы.

Правильная настройка этих параметров снижает вероятность ошибок из-за долгих HTTP-запросов или тяжелого рендеринга.


Подводные нюансы

  1. Приложения с отложенной загрузкой модулей: Если Angular загружает модули динамически, иногда требуется дополнительное ожидание элементов или вызов browser.waitForAngular() после загрузки модуля.
  2. Angular 2+ и Zone.js: Для современных версий Angular Protractor использует Zone.js для отслеживания всех асинхронных операций, включая таймеры, HTTP и события. Применение waitForAngular() здесь особенно важно.
  3. Интеграция с сторонними библиотеками: Если приложение активно использует jQuery или другие фреймворки для AJAX-запросов, Protractor не сможет автоматически отслеживать их завершение. В таких случаях необходимы явные ожидания или проверка состояния элементов через ExpectedConditions.

Практические рекомендации

  • Всегда включать waitForAngularEnabled(true) для страниц Angular.
  • Для сторонних или не-Angular страниц временно отключать синхронизацию.
  • Использовать browser.waitForAngular() перед действиями с элементами, если приложение активно загружает данные асинхронно.
  • Настраивать allScriptsTimeout и getPageTimeout в зависимости от скорости приложения.
  • Комбинировать автоматическое ожидание Angular с ExpectedConditions для надежной работы тестов.

Эффективное управление ожиданием Angular обеспечивает стабильность тестов, предотвращает ошибки взаимодействия с элементами и позволяет полностью использовать преимущества Protractor при автоматизации современных Angular-приложений.