Элементы, локаторы и взаимодействие с DOM

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

Работа с элементами

В Protractor для работы с элементами используется объект element, который создаётся с помощью локаторов. element возвращает ElementFinder — объект, представляющий один элемент DOM. Для работы с коллекциями элементов используется element.all, возвращающий ElementArrayFinder.

let usernameInput = element(by.id('username'));
let allButtons = element.all(by.tagName('button'));

Ключевые методы ElementFinder:

  • click() — клик по элементу.
  • sendKeys() — ввод текста в поле ввода.
  • getText() — получение текста элемента.
  • getAttribute(attributeName) — получение значения атрибута.
  • isDisplayed() — проверка видимости элемента.
  • isEnabled() — проверка доступности элемента.
  • isPresent() — проверка наличия элемента в DOM.

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

allButtons.each(function(button) {
    button.getText().then(function(text) {
        console.log(text);
    });
});

Локаторы

Локаторы — это способ идентификации элементов на странице. Protractor расширяет стандартные локаторы Selenium и добавляет специфичные для Angular:

Стандартные локаторы:

  • by.id('id') — поиск по идентификатору.
  • by.css('selector') — поиск по CSS-селектору.
  • by.className('class') — поиск по классу.
  • by.tagName('tag') — поиск по тегу.
  • by.name('name') — поиск по атрибуту name.
  • by.linkText('text') — поиск по полному тексту ссылки.
  • by.partialLinkText('text') — поиск по части текста ссылки.
  • by.xpath('xpath') — поиск с помощью XPath.

Angular-специфичные локаторы:

  • by.model('modelName') — поиск по Angular-модели.
  • by.binding('bindingName') — поиск по Angular-привязке.
  • by.repeater('repeaterExpression') — поиск повторяющихся элементов, например, в ng-repeat.

Использование Angular-локаторов позволяет тестам быть более стабильными и не зависеть от структурных изменений HTML.

Взаимодействие с DOM

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

  • Ожидание видимости элемента:
let EC = protractor.ExpectedConditions;
let loginButton = element(by.id('login'));
browser.wait(EC.visibilityOf(loginButton), 5000);
loginButton.click();
  • Ожидание появления текста:
let message = element(by.css('.alert'));
browser.wait(EC.textToBePresentInElement(message, 'Успешно'), 5000);
  • Проверка состояния элементов:
let checkbox = element(by.id('agree'));
browser.wait(EC.elementToBeClickable(checkbox), 3000);
checkbox.click();

Использование ExpectedConditions позволяет сделать тесты более стабильными, избегая ошибок, связанных с асинхронностью.

Стратегии поиска элементов

Для повышения устойчивости тестов к изменениям DOM важно правильно выбирать локаторы:

  1. Уникальные идентификаторы (id) — лучший выбор для одиночных элементов.
  2. CSS-селекторы с классами и атрибутами — гибкий способ для сложной структуры.
  3. XPath — применяется при отсутствии других уникальных признаков, но менее устойчив к изменениям.
  4. Angular-локаторы — предпочтительны для Angular-приложений, так как автоматически учитывают асинхронные процессы.

Взаимодействие с коллекциями

ElementArrayFinder поддерживает фильтрацию и преобразование элементов:

let visibleButtons = allButtons.filter(function(button) {
    return button.isDisplayed();
});

visibleButtons.map(function(button) {
    return button.getText();
});

Методы first(), last(), get(index) позволяют обращаться к конкретным элементам коллекции.

Работа с iframe и окнами

Для взаимодействия с элементами внутри iframe необходимо переключиться на контекст:

let frame = element(by.tagName('iframe'));
browser.switchTo().frame(frame.getWebElement());
element(by.id('input')).sendKeys('тест');
browser.switchTo().defaultContent();

Для работы с окнами или вкладками используется getAllWindowHandles и switchTo().window(handle).

События и действия пользователя

Protractor позволяет имитировать действия пользователя:

  • click() — клик.
  • sendKeys() — ввод текста.
  • clear() — очистка поля ввода.
  • mouseMove() — перемещение мыши.
  • dragAndDrop() — перетаскивание элементов.

Для сложных действий используется browser.actions():

browser.actions()
    .mouseMove(element(by.id('drag')))
    .dragAndDrop(element(by.id('drop')))
    .perform();

Это позволяет эмулировать реальные сценарии пользователя, включая drag-and-drop, hover и комбинированные клики.

Асинхронность и управление промисами

Protractor работает с Promise и асинхронными операциями. Методы getText(), isDisplayed() возвращают промисы. Использование async/await упрощает синхронное восприятие кода:

let text = await element(by.id('message')).getText();
if (text === 'Готово') {
    await element(by.id('continue')).click();
}

Асинхронность требует внимательного подхода к последовательности действий и ожиданиям элементов.

Резюме ключевых принципов

  • Использование уникальных локаторов обеспечивает стабильность тестов.
  • Angular-специфичные локаторы повышают устойчивость к асинхронным изменениям.
  • ExpectedConditions необходимы для надежного взаимодействия с динамическим DOM.
  • Работа с коллекциями, iframe и окнами расширяет возможности тестов.
  • Асинхронная модель требует применения async/await для корректного порядка действий.

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