Салют, хабровчане. В преддверии старта курса «Java QA Engineer» подготовили для вас перевод интересного материала.





Относительные локаторы


Selenium 4 принес нам относительные локаторы — Relative Locators (первоначально называвшиеся Friendly Locators). Этот функционал был добавлен, чтобы помочь вам найти элементы, которые находятся рядом с другими элементами.

Доступные варианты:

  • above(): искомый элемент находится над указанным элементом
  • below(): искомый элемент находится ниже указанного элемента
  • toLeftOf(): искомый элемент находится слева от указанного элемента
  • toRightOf(): искомый элемент находится справа от указанного элемента
  • near(): искомый элемент находится на расстоянии не более 50 пикселей от указанного элемента. Существует также перегруженный метод, позволяющий указать расстояние.

Все эти методы перегружены, чтобы принимать By или WebElement.

Использование относительных локаторов


На примере этого приложения книжного магазина, мы хотим проверить, что книга слева от «Advanced Selenium in Java» — это «Java For Testers». Относительные локаторы позволяют нам это сделать.


Вот DOM фрагмент для книг


«Advanced Selenium in Java» представлен в DOM идентификатором pid6, а «Java For Testers» — pid5.

Метод WebDriver::findElement может принимать метод withTagName(), который возвращает объект RelativeLocator.RelativeBy (потомок By).

driver.findElement(withTagName("li")


Здесь я уже могу указать относительные локаторы. Я знаю, что «Java For Testers» находится слева от «Advanced Selenium in Java» (pid6) и находится ниже «Test Automation in the Real World» (pid1). Итак, я могу указать из обоих:

driver.findElement(withTagName("li")
                .toLeftOf(By.id("pid6"))
                .below(By.id("pid1")));

И получу «Java For Testers» (pid5).

@Test
public void test_book5_is_left_of_book6_and_below_book1(){
    String id = driver.findElement(withTagName("li")
            .toLeftOf(By.id("pid6"))
            .below(By.id("pid1")))
            .getAttribute("id");
 
    assertEquals(id, "pid5");
}

Мы можем использовать методы above() и toRightOf(), чтобы найти «Experiences of Test Automation» (pid2):

@Test
public void test_book2_is_above_book6_and_right_of_book1(){
    String id = driver.findElement(withTagName("li")
                    .above(By.id("pid6"))
                    .toRightOf(By.id("pid1")))
            .getAttribute("id");
 
    assertEquals(id, "pid2");
}

Как это работает?


Я не смогла обнаружить «Java For Testers» только с помощью вызова toLeftOf(By.id(“pid6”)). Один он вернет «Test Automation in the Real World» (pid1). Это потому, что driver.findElement() выполняет поиск от корня DOM, а первый элемент <li> слева от «Advanced Selenium в Java» — «Test Automation in the Real World».

Selenium использует функцию JavaScript getBoundingClientRect() для поиска относительных элементов. Эта функция возвращает свойства элемента, такие как right, left, bottom и top.

Рассматривая свойства этих трех книг, мы видим, что и «Test Automation in the Real World» (pid1), и «Java For Testers» (pid5) обе имеют одинаковую позицию по оси x.



Таким образом, они обе слева от «Java For Testers», причем «Test Automation in the Real World» (pid1) является первой найденной.

Еще один момент, которую следует учитывать, — это изменения во вьюпортах приложений. Если бы я запустила тесты, указанные выше, на своем приложении в мобильном представлении, то, естественно, они потерпели бы неудачу, так как книги больше не находятся слева/справа от других книг:



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



Я попытался использовать метод toLeftOf(), чтобы найти переключатель ввода рядом с элементом «goodbye world». Визуально этот переключатель ввода находится слева от ярлыка «goodbye world». Вот этот div в DOM:

<div class="view" data-reactid=".0.1.2.$645c3b67-884e-4e4f-aecd-8f9367e670f8.0">
    <input class="toggle" type="checkbox" data-reactid=".0.1.2.$645c3b67-884e-4e4f-aecd-8f9367e670f8.0.0">
    <label data-reactid=".0.1.2.$645c3b67-884e-4e4f-aecd-8f9367e670f8.0.1">goodbye world</label>
    <button class="destroy" data-reactid=".0.1.2.$645c3b67-884e-4e4f-aecd-8f9367e670f8.0.2"></button>
</div>

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

driver.findElement(withTagName("input")
      .toLeftOf(By.xpath("//label[text()='goodbye world']")))
      .click();

Однако я наткнулась на исключение:

org.openqa.selenium.NoSuchElementException: Cannot locate an element using [unknown locator]

Кажется, что хотя этот <input> находится слева от <label> визуально, на самом деле это не так. Я вызвала функцию getBoundingClientRect() для обоих этих элементов, и они фактически перекрываются. Обратите внимание, что они оба имеют позицию x 838, так что технически <input> не слева от <label>.



И когда я выделяю элемент <label>, я теперь вижу, что он действительно перекрывает элемент <input>.

<img src=«habrastorage.org/webt/hj/aa/46/hjaa46ps-of5_hhlplqaldclc7s.png» /
Примечание: Это альфа-версия Selenium WebDriver. Я разговаривала с руководителем проекта Selenium Саймоном Стюартом, и узнала, что реализация может измениться в зависимости от отзывов.

На этом все. До встречи на курсе!