Комбинирование локаторов

В Protractor работа с элементами страницы строится на использовании локаторов. Иногда одного локатора недостаточно для точного выбора элемента, особенно если структура DOM сложная или элементы имеют динамические атрибуты. Комбинирование локаторов позволяет создавать более гибкие и надежные селекторы.


Основы локаторов

Protractor наследует все возможности WebDriverJS, предоставляя стандартные локаторы:

  • by.id('value') — выбор по уникальному идентификатору.
  • by.css('selector') — выбор по CSS-селектору.
  • by.className('value') — выбор по классу.
  • by.tagName('tag') — выбор по имени тега.
  • by.name('value') — выбор по атрибуту name.
  • by.xpath('expression') — выбор с использованием XPath.
  • by.binding('angularBinding') — выбор Angular-биндинга.
  • by.model('modelName') — выбор по Angular-модели.

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


Последовательное применение локаторов (element внутри element)

Protractor позволяет находить дочерние элементы через метод element:

let form = element(by.css('form.login-form'));
let usernameInput = form.element(by.name('username'));
let passwordInput = form.element(by.name('password'));

В этом примере сначала выбирается форма с классом login-form, затем внутри формы выбираются поля username и password. Такой подход уменьшает риск выбора неправильного элемента при наличии нескольких одинаковых полей на странице.


Использование all и фильтрация

Метод element.all() возвращает коллекцию элементов. Для комбинирования локаторов часто используют цепочки методов:

let visibleButtons = element.all(by.css('.btn')).filter(function(btn) {
    return btn.isDisplayed();
});

let firstVisibleButton = visibleButtons.first();
  • filter() позволяет отфильтровать элементы по условию.
  • first(), last() и get(index) позволяют выбрать конкретный элемент из коллекции.

Такой подход особенно полезен при работе с динамическими списками.


Комбинация CSS-селекторов

CSS-селекторы сами по себе поддерживают комбинации:

  • Дочерние элементы: parent > child
  • Потомки: ancestor descendant
  • Комбинация классов: .class1.class2
  • Атрибуты: [attribute='value'], [attribute^='start'], [attribute$='end'], [attribute*='contains']

Пример:

let submitButton = element(by.css('form.login-form button.btn.primary'));

Здесь сразу объединены форма, тег и классы, что делает локатор точным и читаемым.


Комбинирование XPath

XPath позволяет строить сложные выражения для выбора элементов:

let loginButton = element(by.xpath("//form[@class='login-form']//button[contains(@class, 'btn-primary')]"));

XPath особенно полезен, когда CSS не может выразить необходимую логику, например, выбор элемента по тексту:

let cancelButton = element(by.xpath("//button[text()='Cancel']"));

Использование by.cssContainingText

Protractor предоставляет удобный локатор для поиска текста внутри элементов:

let menuItem = element(by.cssContainingText('ul.menu li', 'Settings'));

Можно комбинировать его с другими локаторами для точного выбора:

let submenuItem = element(by.css('ul.menu')).element(by.cssContainingText('li', 'Settings'));

Локаторы Angular (by.model, by.binding, by.repeater)

Для Angular-приложений Protractor поддерживает локаторы специфичные для фреймворка:

let todoInput = element(by.model('newTodo'));
let firstTodo = element.all(by.repeater('todo INTOdos')).first();
let todoText = element(by.binding('todo.text'));

Комбинируя Angular-локаторы с CSS, можно точно выбрать элемент внутри повторяющихся блоков:

let completedTodo = element.all(by.repeater('todo INTOdos'))
                        .filter(todo => todo.element(by.model('todo.completed')).isSelected())
                        .first();

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

  1. Начинать с более конкретного локатора, затем уточнять дочерними элементами через element или all.
  2. Использовать фильтры и индексы, чтобы избежать случайного выбора неверного элемента.
  3. Предпочитать CSS-селекторы там, где это возможно, из-за их производительности и читаемости.
  4. XPath применять только при необходимости выбора по тексту или сложной структуре.
  5. Комбинировать локаторы Angular с CSS для надежного тестирования динамического контента.

Пример комплексного выбора элемента

let firstActiveUserEditButton = element.all(by.css('div.user-list div.user.active'))
                                     .filter(user => user.element(by.css('span.status')).getText().then(text => text === 'Active'))
                                     .first()
                                     .element(by.css('button.edit'));

В этом примере:

  • Сначала выбираются все активные пользователи.
  • Фильтруется по тексту статуса.
  • Берется первый подходящий элемент.
  • Внутри него выбирается кнопка редактирования.

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


Комбинирование локаторов — ключ к написанию устойчивых и читаемых тестов в Protractor. Оно позволяет точно выбирать элементы в любых структурах DOM, учитывая динамику приложения и особенности Angular.