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

В веб-приложениях элементы <select> используются для предоставления пользователю списка вариантов, из которых можно выбрать один или несколько. В автоматизированном тестировании такие элементы требуют особого подхода, так как взаимодействие с ними отличается от работы с обычными полями ввода. Protractor предоставляет инструменты для управления <select> и его <option>.


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

Для начала необходимо получить ссылку на сам элемент <select>. Используются стандартные локаторы Protractor:

const selectElement = element(by.id('country'));

Каждый <option> внутри select также можно найти через element.all():

const options = selectElement.all(by.tagName('option'));

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


Выбор значения по видимому тексту

Наиболее читаемый и часто используемый способ — выбор опции по её видимому тексту. В Protractor это делается с использованием метода filter и click:

const desiredText = 'Россия';
options.filter(async (option) => {
    const text = await option.getText();
    return text === desiredText;
}).first().click();

Важно использовать async/await, чтобы дождаться получения текста каждой опции перед сравнением.


Выбор значения по атрибуту value

Иногда удобнее выбирать опцию по значению атрибута value, особенно если текст может изменяться или содержит пробелы:

const desiredValue = 'RU';
options.filter(async (option) => {
    const value = await option.getAttribute('value');
    return value === desiredValue;
}).first().click();

Метод getAttribute('value') извлекает значение value, указанное в HTML, что делает выбор более стабильным при изменениях интерфейса.


Выбор по индексу

Если порядок опций известен и фиксирован, можно выбрать элемент по индексу:

await options.get(2).click(); // выбираем третью опцию, индексация с нуля

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


Работа с множественным выбором

Если <select> поддерживает атрибут multiple, можно выбирать несколько значений:

const multipleSelect = element(by.id('fruits'));
const fruitsToSelect = ['Яблоко', 'Банан'];

for (const fruit of fruitsToSelect) {
    await multipleSelect.all(by.tagName('option'))
        .filter(async (option) => {
            const text = await option.getText();
            return text === fruit;
        }).first().click();
}

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


Проверка выбранной опции

Для тестирования важно убедиться, что нужный элемент выбран. У каждого <option> есть свойство selected:

const selectedOption = await selectElement.element(by.css('option:checked')).getText();
console.log(selectedOption); // выводит текст выбранной опции

Использование CSS-селектора option:checked позволяет получить активный элемент, что удобно для проверок в тестах.


Использование вспомогательных функций

Для упрощения работы с <select> полезно создавать утилитарные функции:

async function selectByText(selectElem, text) {
    const options = selectElem.all(by.tagName('option'));
    await options.filter(async (option) => {
        return await option.getText() === text;
    }).first().click();
}

async function selectByValue(selectElem, value) {
    const options = selectElem.all(by.tagName('option'));
    await options.filter(async (option) => {
        return await option.getAttribute('value') === value;
    }).first().click();
}

Это повышает читаемость тестов и уменьшает дублирование кода.


Взаимодействие с динамическими select

Иногда опции <select> подгружаются динамически через AJAX. В таких случаях необходимо дождаться появления элементов перед выбором:

const EC = protractor.ExpectedConditions;
await browser.wait(EC.presenceOf(selectElement), 5000);
await selectByText(selectElement, 'Россия');

Использование ExpectedConditions гарантирует, что тест не упадет из-за отсутствия элемента в DOM на момент взаимодействия.


Особенности Protractor с Angular

Protractor изначально разработан для Angular-приложений, поэтому он автоматически синхронизируется с Angular-зоной. Для <select> это означает, что обычно нет необходимости использовать browser.sleep(), если приложение полностью основано на Angular. Однако при работе с внешними плагинами или нестандартными компонентами <select> иногда требуется явное ожидание.


Выбор нестандартных компонентов

Некоторые библиотеки UI заменяют обычный <select> кастомными компонентами. В этом случае взаимодействие через option невозможно, необходимо кликать по элементам, имитирующим опции:

const dropdown = element(by.css('.custom-dropdown'));
await dropdown.click();
const optionToSelect = dropdown.element(by.cssContainingText('.dropdown-item', 'Россия'));
await optionToSelect.click();

Такая реализация универсальна для любых кастомных селекторов, не зависящих от стандартного <select>.


Советы по стабильности тестов

  • Использовать filter вместо поиска через element(by.cssContainingText()), если требуется точное совпадение.
  • Предпочитать выбор по value, если текст может меняться.
  • Добавлять ожидания появления элементов при динамическом наполнении списка.
  • В случае множественного выбора — убедиться, что выбранные опции действительно активны с помощью проверки selected.

Работа с <select> в Protractor требует внимательности к асинхронности и правильного ожидания элементов. Следуя описанным подходам, можно надежно управлять как стандартными, так и кастомными выпадающими списками в автоматизированных тестах.