По итогам обсуждения проблем взаимодействия с Select2 компонентом, хочу поделиться соответствующим рецептом на Java.
Более детальный разбор изложен в блоге.
Select2
представляет из себя обертку над стандартным select
компонентом, с поддержкой множества интересных фич, включая динамическую фильтрацию выпадающего списка. Но постоянное обновление состояния по мере ввода текста может в итоге привести ко всем известному StaleElementReferenceException при взаимодействии с WebDriver
. И как видно из предыдущего обсуждения, WebDriverWait
далеко не всегда может помочь.
В идеале, конечно же мы всегда должны стремиться к повторению действий реального пользователя, который наверняка вначале раскроет список, затем начнет вводить какой-то текст, и в конце концов - выберет искомый отфильтрованный элемент. Но с другой стороны, мы ведь тестируем не 3rd party Select2
, ведь так? Какая нам разница, как именно будет выбран элемент списка?
В этой статье мы рассмотрим один из возможных вариантов взаимодействия с Select2
при помощи его нативных API и JavascriptExecutor’а.
За основу будет взят код одной из предыдущих статей, где мы создавали кастомные компоненты. Теперь же, по аналогии, мы напишем обертку над Select2
.
В качестве подопытного сайта возьмем сборник соответствующих примеров. Для начала рассмотрим структуру среднестатистического селекта:
Те, кто уже успел пощупать предыдущую версию компонента, могут быть удивлены тому факту, что при подсветке искомого элемента мы попадаем на какой-то div
, а не select
. Причем сам селект в боевых условиях по-дефолту будет еще и скрыт от мира. Собственно с div
’ом нам и придется в итоге взаимодействовать.
Поигравшись в консоли с jQuery
и Select2
API, можно обнаружить достаточно предсказуемые рычаги. К примеру, получив значение искомой опции по отображаемому тексту, можно в последствии обратиться к нативному методу select2('val', value)
, чтобы выбрать именно то, что нам нужно.
В целом, этой информации вполне достаточно для написания базовой версии обертки.
public class Select2 extends HTMLElement {
public Select2(final WebDriver driver, final SearchBy elementSearchCriteria, final String elementValue) {
super(driver, elementSearchCriteria, elementValue);
}
public void selectByVisibleText(final String text) {
executeJS("var value = $(\"" + getElementValue() + "+select\").find('option:contains(\"" + text + "\")').val();" +
"$(\"" + getElementValue() + "\").select2(\"val\", value);");
}
public String getSelectedText() {
return (String) executeJS("return $(\"" + getElementValue() + "\").select2('data').text;");
}
}
По сути, мы просим WebDriver
выполнить выше рассмотренные команды с помощью JSExecutor
’а.
А наследование HTMLElement
’а позволяет использовать новоиспеченную обертку на уровне PageObject
’ов без явной инициализации.
public class HomePage extends BasePage {
@HTML(searchBy = CSS_SELECTOR, value = "div[id*=listBox1]")
private Select2 listBoxWeekDay;
public HomePage selectWeekDay(final String day) {
listBoxWeekDay.selectByVisibleText(day);
return this;
}
public String getSelectedWeekDay() {
return listBoxWeekDay.getSelectedText();
}
}
Как видите, никакой магии. Конечно же существуют и другие обходные пути взаимодействия. К примеру, можно делать вложенный select
видимым перед использованием. Хотя, визуально это будет выглядеть больше, как хак (особенно на скриншотах).
Полную версию исходников можно найти на GitHub.
Не забываем о лайках, если приведенная информация оказалась вам полезной.