Как взаимодействовать с Select2 при помощи WebDriver

По итогам обсуждения проблем взаимодействия с 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 видимым перед использованием. Хотя, визуально это будет выглядеть больше, как хак (особенно на скриншотах). :smile:

Полную версию исходников можно найти на GitHub.
Не забываем о лайках, если приведенная информация оказалась вам полезной. :wink:

7 лайков