Ожидание JavaScript выполнения

Protractor — инструмент для автоматизации тестирования веб-приложений на основе Angular и AngularJS, но его возможности распространяются и на обычные веб-приложения. Одной из ключевых особенностей Protractor является умение работать с асинхронными процессами и ожидать завершения выполнения JavaScript в браузере. Это критично для корректного взаимодействия с динамическим контентом и современными SPA (Single Page Application).

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

Protractor изначально создан для Angular-приложений. Он использует механизм automatic synchronization, который позволяет тесту ожидать завершения всех асинхронных операций Angular перед выполнением действий. В основе лежит метод browser.waitForAngular(), который автоматически вызывается перед выполнением команд, таких как element.click() или element.getText().

Пример:

element(by.css('.submit-button')).click();
// Перед кликом Protractor проверяет, что все $http и $timeout завершены

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

browser.waitForAngularEnabled(false);

После этого Protractor перестает ждать автоматические завершения Angular и управление полностью переходит к ручным ожиданиям.

Использование browser.executeScript()

Для выполнения кастомного JavaScript кода в браузере используется метод browser.executeScript(). Он возвращает Promise, который разрешается после завершения выполнения скрипта. Это позволяет интегрировать произвольные проверки или манипуляции с DOM.

Пример:

browser.executeScript('return document.readyState')
    .then(state => {
        console.log('Текущее состояние страницы:', state);
    });

Этот метод особенно полезен для проверки готовности нестандартных элементов или выполнения скриптов, которые Protractor не отслеживает автоматически.

Явные ожидания с browser.wait()

Для случаев, когда необходимо дождаться выполнения определённого условия на странице, используется browser.wait(). Метод принимает функцию-условие и таймаут:

const EC = protractor.ExpectedConditions;

const button = element(by.id('load-data'));
browser.wait(EC.elementToBeClickable(button), 5000);
button.click();

Здесь ExpectedConditions предоставляет набор готовых проверок, таких как видимость элемента, кликабельность или присутствие текста. Таймаут указывается в миллисекундах, и если условие не выполнено, тест завершится ошибкой.

Ожидание завершения асинхронных операций

Иногда требуется дождаться выполнения конкретного JavaScript-кода, например, AJAX-запроса или завершения анимации. Для этого можно использовать browser.executeAsyncScript():

browser.executeAsyncScript(function(callback) {
    setTimeout(() => {
        callback('готово');
    }, 2000);
}).then(result => {
    console.log(result); // 'готово'
});

Отличие от executeScript() заключается в том, что функция принимает callback, который должен быть вызван после завершения асинхронного кода. Protractor автоматически ожидает вызова этого callback, что позволяет синхронизировать тест с динамическими процессами.

Обработка нестабильных элементов

В современных веб-приложениях элементы часто появляются или исчезают динамически. Использование комбинации ExpectedConditions и browser.wait() позволяет минимизировать ошибки:

const EC = protractor.ExpectedConditions;
const dynamicElement = element(by.css('.dynamic-element'));

browser.wait(EC.presenceOf(dynamicElement), 10000)
       .then(() => dynamicElement.getText())
       .then(text => console.log(text));

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

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

Protractor позволяет глобально и локально настраивать таймауты ожидания Jav * aScript:

  • Глобальный таймаут ожидания Angular:
browser.manage().timeouts().implicitlyWait(5000); // 5 секунд
  • Таймаут для отдельных ожиданий:
browser.wait(EC.visibilityOf(element(by.id('message'))), 7000);

Использование правильных таймаутов критично для балансировки между стабильностью тестов и их скоростью выполнения.

Рекомендации по синхронизации

  1. Для Angular-приложений использовать автоматическую синхронизацию.
  2. Для нестандартных асинхронных процессов применять executeAsyncScript() с callback.
  3. Для динамических элементов комбинировать ExpectedConditions и browser.wait().
  4. Избегать использования browser.sleep(), так как это снижает стабильность тестов и увеличивает время их выполнения.

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