by.repeater() — специфичный для Angular

by.repeater() является локатором в Protractor, предназначенным для работы с элементами, созданными с помощью директивы ng-repeat в AngularJS. Эта директива позволяет автоматически генерировать списки элементов на основе массивов или коллекций объектов в модели Angular. Использование by.repeater() обеспечивает точное и удобное взаимодействие с повторяющимися элементами без необходимости вручную прописывать сложные CSS или XPath-селекторы.


Основные принципы работы

by.repeater() принимает строку, которая соответствует синтаксису ng-repeat. Формат обычно следующий:

ng-repeat="item in items"

Для локатора в Protractor нужно передавать часть после in или полное выражение. Например:

element.all(by.repeater('item in items'))
  • element.all() возвращает массив всех элементов, соответствующих повторителю.
  • Каждый элемент в массиве можно использовать для проверки текста, кликов, ввода данных и других взаимодействий.

Получение конкретного элемента

Для работы с отдельным элементом из коллекции используется метод get(index), где index — порядковый номер элемента (нумерация с нуля):

let firstItem = element.all(by.repeater('item in items')).get(0);
let thirdItem = element.all(by.repeater('item in items')).get(2);

Это позволяет точно взаимодействовать с конкретным повторяющимся элементом, не затрагивая остальные.


Получение определённых колонок

Если повторитель создаёт таблицу или список с несколькими полями, можно обращаться к отдельным колонкам с помощью метода row().column():

let firstRowSecondColumn = element(by.repeater('item in items').row(0).column('name'));
  • row(index) возвращает строку с указанным индексом.
  • column('fieldName') позволяет получить конкретное поле внутри этой строки по имени из модели Angular (item.name).

Это особенно удобно для тестирования таблиц и списков, где важна структура данных.


Проверка количества элементов

Для проверки длины коллекции, сгенерированной ng-repeat, используется свойство count():

let itemsCount = element.all(by.repeater('item in items')).count();
expect(itemsCount).toEqual(5);

count() возвращает Promise, поэтому в современных версиях Protractor рекомендуется использовать async/await:

const count = await element.all(by.repeater('item in items')).count();
if (count !== 5) {
    throw new Error(`Ожидалось 5 элементов, найдено ${count}`);
}

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

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

let filteredItems = element.all(by.repeater('item in items').row(0))
                           .filter(async (elem) => {
                               const text = await elem.getText();
                               return text.includes('Active');
                           });
  • filter() принимает функцию с проверкой каждого элемента.
  • Возвращает массив элементов, удовлетворяющих условию.

Также возможен поиск текста конкретного повторителя:

const itemText = await element(by.repeater('item in items').row(1)).getText();

Особенности использования

  • Локатор by.repeater() работает только с AngularJS. В Angular 2+ и выше используется *ngFor, для которого Protractor не предоставляет встроенного локатора.
  • При работе с асинхронной загрузкой данных важно использовать ожидания (ExpectedConditions) или методы browser.wait(), чтобы элементы успели отобразиться перед взаимодействием:
const items = element.all(by.repeater('item in items'));
browser.wait(protractor.ExpectedConditions.visibilityOf(items.get(0)), 5000);
  • by.repeater() позволяет тестировать динамические списки, где количество и содержимое элементов может меняться во время работы приложения.

Примеры практического применения

Клик по определённому элементу:

await element.all(by.repeater('item in items'))
             .get(2)
             .click();

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

const texts = await element.all(by.repeater('item in items')).getText();
expect(texts).toEqual(['Item 1', 'Item 2', 'Item 3']);

Работа с таблицей:

const secondRowFirstColumn = await element(by.repeater('user in users').row(1).column('username')).getText();
expect(secondRowFirstColumn).toBe('john_doe');

Заключение по особенностям

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