Навигация по элементам

Protractor — это инструмент для end-to-end тестирования приложений на Angular и не только. Ключевой задачей является взаимодействие с элементами страницы: поиск, навигация, ожидание и выполнение действий. В основе работы лежит WebDriverJS, что обеспечивает синхронизацию с браузером и управляемую асинхронность через async/await.


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

Поиск элементов осуществляется через объект element и локаторы из модуля by.

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

const usernameInput = element(by.id('username'));
const submitButton = element(by.css('.submit-button'));
const menuItems = element.all(by.tagName('li'));
const link = element(by.linkText('Home'));
const partialLink = element(by.partialLinkText('Hom'));
  • by.id — уникальный идентификатор элемента.
  • by.css — CSS-селектор для гибкой навигации.
  • by.tagName — поиск по имени тега.
  • by.className — поиск по имени класса.
  • by.name — поиск по атрибуту name.
  • by.linkText и by.partialLinkText — поиск ссылок по тексту.

Для множественных элементов используется element.all() вместо element(), что возвращает ElementArrayFinder. Он поддерживает методы фильтрации и индексирования.

const buttons = element.all(by.css('.btn'));
const firstButton = buttons.first();
const lastButton = buttons.last();
const thirdButton = buttons.get(2);

Навигация по элементам

После нахождения элемента можно выполнять цепочку действий и навигацию:

  1. Доступ к дочерним элементам:
const form = element(by.css('form#loginForm'));
const input = form.element(by.css('input[name="email"]'));
  1. Фильтрация и поиск внутри коллекций:
const items = element.all(by.css('.item'));
const activeItems = items.filter(async (elem) => {
    const cls = await elem.getAttribute('class');
    return cls.includes('active');
});
  1. Выбор конкретного элемента по индексу:
const secondItem = items.get(1); // нумерация с нуля
  1. Поиск с использованием xpath:
const header = element(by.xpath('//h1[contains(text(), "Welcome")]'));

XPath полезен для сложной логики поиска, когда CSS-селекторы ограничены.


Работа с асинхронностью

Protractor построен на WebDriverJS, который возвращает промисы. Для синхронного кода применяется async/await:

const username = element(by.id('username'));
await username.sendKeys('user123');

const button = element(by.css('.submit-button'));
await button.click();

Методы getText(), getAttribute(), isDisplayed() и другие также возвращают промисы:

const text = await element(by.css('.message')).getText();
const visible = await element(by.css('.alert')).isDisplayed();

Ожидания элементов

Эффективная навигация требует ожидания состояния элементов. Protractor предоставляет ExpectedConditions:

const EC = protractor.ExpectedConditions;
const submitButton = element(by.css('.submit-button'));

// Ожидание, пока кнопка станет кликабельной
await browser.wait(EC.elementToBeClickable(submitButton), 5000);
await submitButton.click();

Основные условия:

  • elementToBeClickable(element) — элемент доступен для клика.
  • visibilityOf(element) — элемент видим.
  • presenceOf(element) — элемент присутствует в DOM.
  • textToBePresentInElement(element, text) — проверка текста.

Комбинирование действий и навигации

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

const rows = element.all(by.css('table#users tr'));
const activeUserNames = rows.filter(async (row) => {
    const status = await row.element(by.css('.status')).getText();
    return status === 'Active';
}).map(async (row) => {
    return row.element(by.css('.name')).getText();
});

В этом примере используется фильтрация по состоянию и извлечение текста для коллекции.


Работа с вложенными фреймами

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

const frame = element(by.css('iframe#editor'));
await browser.switchTo().frame(frame.getWebElement());

const editor = element(by.css('.editor-content'));
await editor.sendKeys('Текст в iframe');

await browser.switchTo().defaultContent(); // возвращение к основному DOM

Рекомендации по надежной навигации

  • Предпочтительно использовать уникальные идентификаторы и CSS-селекторы.
  • Для динамических страниц применять ExpectedConditions вместо sleep.
  • Для множественных элементов использовать element.all() и методы фильтрации.
  • Минимизировать использование XPath там, где можно обойтись CSS, чтобы повысить читаемость.
  • Всегда учитывать асинхронность методов и применять await.

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