Работа с динамически загружаемыми данными

Protractor — это инструмент для автоматизированного тестирования приложений на Angular и AngularJS, построенный поверх WebDriverJS. Одной из ключевых особенностей современных веб-приложений является динамическая загрузка данных через AJAX, WebSocket или другие асинхронные механизмы. Тестирование таких компонентов требует особого подхода, поскольку элементы на странице могут появляться или изменяться с задержкой.

Ожидания элементов (Explicit Waits)

Для работы с динамическим контентом используется ожидание появления элементов. Protractor предоставляет объект ExpectedConditions (EC), позволяющий создавать условия для ожидания:

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

browser.wait(EC.presenceOf(elementToWait), 5000, 'Элемент не появился в течение 5 секунд');

Ключевые моменты:

  • presenceOf проверяет, что элемент существует в DOM, независимо от того, видим он или нет.
  • visibilityOf проверяет, что элемент видим пользователю.
  • textToBePresentInElement позволяет ожидать появления определённого текста.

Использование асинхронных операций

Protractor интегрирован с async/await, что позволяет писать последовательные и читаемые тесты. Пример проверки динамического текста:

const dynamicElement = element(by.css('.dynamic-text'));

await browser.wait(EC.textToBePresentInElement(dynamicElement, 'Загружено'), 5000);
const text = await dynamicElement.getText();
console.log(text); // Ожидаемый результат: "Загружено"

Обработка элементов, которые появляются после AJAX-запросов

Если данные подгружаются через AJAX, важно не использовать жесткие таймауты browser.sleep(), так как это делает тесты медленными и нестабильными. Вместо этого применяются условия ожидания:

const listItem = element(by.repeater('item in items').row(0));
await browser.wait(EC.presenceOf(listItem), 10000);

Использование repeater позволяет работать с коллекциями Angular, динамически обновляемыми на странице.

Проверка динамических таблиц и списков

Для таблиц и списков, где элементы могут меняться после загрузки, Protractor позволяет работать с коллекциями:

const items = element.all(by.css('.list-item'));
await browser.wait(async () => {
    const count = await items.count();
    return count > 0;
}, 5000, 'Список не загрузился');

Особенности:

  • Метод element.all возвращает коллекцию, которая автоматически обновляется при изменении DOM.
  • Асинхронная функция внутри browser.wait позволяет гибко проверять условие до его выполнения.

Работа с динамическими атрибутами

В приложениях на Angular часто элементы получают значения через привязку ng-model или ng-bind. Для проверки этих значений:

const inputField = element(by.model('user.name'));
await browser.wait(EC.presenceOf(inputField), 5000);
await inputField.sendKeys('Тестовое имя');
const value = await inputField.getAttribute('value');
console.log(value); // "Тестовое имя"

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

Protractor автоматически ожидает завершения асинхронных операций Angular через механизм Angular Zone. Однако для нестандартных AJAX-запросов вне Angular нужно отключить автоматическую синхронизацию:

browser.waitForAngularEnabled(false);

После выполнения теста можно снова включить синхронизацию:

browser.waitForAngularEnabled(true);

Это особенно важно при работе с внешними библиотеками или REST API, которые не интегрированы с Angular.

Логирование и дебаггинг динамических элементов

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

  • browser.sleep(ms) только как временное средство для проверки, что элемент появляется.
  • browser.takeScreenshot() для фиксирования состояния страницы в момент ошибки.
  • Консольные выводы через console.log с результатами getText() или getAttribute() для проверки правильности данных.

Оптимизация тестов

  1. Избегать жёстких таймаутов, использовать explicit waits.
  2. Минимизировать вызовы browser.sleep().
  3. Проверять состояние элементов перед взаимодействием (isPresent(), isDisplayed()).
  4. Использовать коллекции element.all для динамических списков и таблиц.
  5. Синхронизировать тесты с Angular или отключать синхронизацию для внешних запросов.

Примеры реальных сценариев

  • Подтверждение появления уведомления после отправки формы:
const notification = element(by.css('.notification-success'));
await browser.wait(EC.visibilityOf(notification), 5000);
const message = await notification.getText();
expect(message).toEqual('Форма успешно отправлена');
  • Проверка добавления нового элемента в список:
const addButton = element(by.buttonText('Добавить'));
await addButton.click();

const newItem = element(by.cssContainingText('.list-item', 'Новый элемент'));
await browser.wait(EC.presenceOf(newItem), 5000);

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