Работа с Angular повторителями

Protractor — специализированный инструмент для end-to-end тестирования Angular-приложений. Одной из ключевых особенностей Angular является директива ng-repeat (или современный аналог *ngFor в Angular 2+), которая позволяет динамически генерировать списки элементов на основе массива данных. В тестировании важно уметь корректно взаимодействовать с такими повторяющимися элементами.

Локаторы для повторителей

Protractor предоставляет встроенный локатор by.repeater, который позволяет находить элементы, созданные с помощью директив ng-repeat. Основной синтаксис:

element.all(by.repeater('item in items'))

где 'item in items' — это выражение, указанное в ng-repeat или *ngFor.

  • element.all() возвращает объект ElementArrayFinder, который позволяет работать с набором элементов.
  • element(by.repeater(...)) возвращает первый найденный элемент повторителя (ElementFinder).

Примеры:

// Найти все элементы повторителя
let rows = element.all(by.repeater('user in users'));

// Найти первый элемент повторителя
let firstRow = element(by.repeater('user in users'));

Получение текста и атрибутов

Элементы повторителя часто используются для проверки содержимого таблиц и списков. ElementArrayFinder предоставляет методы для работы с набором элементов.

// Получение всех текстовых значений элементов повторителя
rows.getText().then(function(texts) {
    console.log(texts);
});

// Получение значения атрибута 'class' первого элемента
firstRow.getAttribute('class').then(function(attr) {
    console.log(attr);
});

Метод get(index) позволяет получить конкретный элемент из повторителя по индексу:

let thirdRow = rows.get(2); // Индексация с 0
thirdRow.getText().then(text => console.log(text));

Вложенные повторители

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

let nestedElements = element.all(by.repeater('user in users'))
                            .all(by.repeater('order in user.orders'));

В этом примере для каждого пользователя извлекаются его заказы. Методы all() можно комбинировать для любого уровня вложенности.

Фильтрация и сортировка

Локатор by.repeater поддерживает уточнение выражений:

  • .row(index) — получение конкретной строки повторителя.
  • .column('user.name') — извлечение данных конкретного свойства для всех элементов повторителя.

Пример:

// Получить значения поля 'name' для всех пользователей
rows.column('user.name').then(function(names) {
    console.log(names);
});

// Получить текст из второй строки
rows.get(1).getText().then(text => console.log(text));

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

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

В Angular-приложениях данные повторителей часто обновляются асинхронно. Для корректного тестирования важно использовать ожидания:

let EC = protractor.ExpectedConditions;
let rows = element.all(by.repeater('user in users'));

// Ждать, пока появятся хотя бы 3 элемента
browser.wait(EC.presenceOf(rows.get(2)), 5000);

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

Взаимодействие с элементами повторителя

Protractor позволяет не только получать данные, но и взаимодействовать с элементами:

// Нажать на кнопку в третьей строке повторителя
rows.get(2).element(by.buttonText('Редактировать')).click();

// Ввести текст в input внутри повторителя
rows.get(0).element(by.model('user.name')).sendKeys('Новый текст');

Использование element внутри ElementArrayFinder позволяет работать с конкретными элементами повторителя, независимо от того, сколько их на странице.

Использование функций для удобства

Для упрощения работы с повторителями часто создают функции-обертки:

function getUserRow(index) {
    return element.all(by.repeater('user in users')).get(index);
}

function getUserName(index) {
    return getUserRow(index).element(by.binding('user.name')).getText();
}

getUserName(1).then(name => console.log(name));

Такой подход повышает читаемость тестов и позволяет легко изменять локаторы в одном месте.

Особенности Angular 2+ (*ngFor)

Protractor также поддерживает современные версии Angular с директивой *ngFor. Локатор by.repeater работает, если указано точное выражение итератора:

element.all(by.repeater('let user of users'));

Все методы для работы с массивами элементов (get, getText, element, all) сохраняются. Отличие только в синтаксисе повторителя.

Резюме ключевых методов

  • element.all(by.repeater('item in items')) — получение всех элементов повторителя
  • .get(index) — доступ к элементу по индексу
  • .getText() — извлечение текста
  • .getAttribute(attr) — получение атрибута
  • .element(locator) — поиск вложенного элемента внутри повторителя
  • .column('binding') — получение данных конкретного свойства для всех элементов
  • Использование ExpectedConditions для работы с динамическими списками

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