Relative Locators in Selenium 4

Salute, Khabrovsk. In anticipation of the start of the course “Java QA Engineer” prepared a translation of interesting material for you.


RELATIVE LOCATORS

Selenium 4 brought us relative locators – Relative Locators (originally called Friendly Locators). This functionality has been added to help you find items that are next to other items.

Available options:

  • above (): The item you are looking for is above the item
  • below (): The item you are searching for is below the item
  • toLeftOf (): The item to find is to the left of the specified item
  • toRightOf (): The item to find is to the right of the specified item
  • near (): The item you’re looking for is within 50 pixels of the specified item. There is also an overloaded method to specify the distance.

All of these methods are overloaded to accept By or WebElement.

USING RELATIVE LOCATORS

For example this bookstore app, we want to check that the book to the left of Advanced Selenium in Java is Java For Testers. Relative locators allow us to do this.


Here is a DOM fragment for books


“Advanced Selenium in Java” is represented in the DOM by pid6, and “Java For Testers” by pid5.

Method WebDriver::findElement can take a method withTagName()that returns an object RelativeLocator.RelativeBy (a descendant of By).

driver.findElement(withTagName("li")

Here I can already specify relative locators. I know that “Java For Testers” is to the left of “Advanced Selenium in Java” (pid6) and is below “Test Automation in the Real World” (pid1). So, I can point out from both:

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

And I get 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");
}

We can use methods above() and toRightOf()to find “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");
}

HOW IT WORKS?

I could not detect Java For Testers with just a call toLeftOf (By.id (“pid6″)). Alone, he will return Test Automation in the Real World (pid1). It’s because driver.findElement() searches from the root of the DOM, and the first element <li> to the left of Advanced Selenium in Java is Test Automation in the Real World.

Selenium Uses JavaScript Function getBoundingClientRect() to search for relative elements. This function returns element properties, such as right, left, bottom, and top.

Looking at the properties of these three books, we see that both Test Automation in the Real World (pid1) and Java For Testers (pid5) both have the same x-axis position.

Thus, they are both to the left of Java For Testers, with Test Automation in the Real World (pid1) being the first one found.

Another point to keep in mind is changes to the application viewports. If I ran the tests mentioned above on my application in the mobile view, then naturally they would have failed because the books are no longer on the left / right of other books:

I also ran into a problem with friendly locators when I tried to use them in this application Todo.

I tried to use the method toLeftOf()to find the input switch next to the goodbye world element. Visually, this input switch is located to the left of the “goodbye world” label. Here is this div in the DOM:

Here is the code I used to find the input element to the left of the label:

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

However, I came across an exception:

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

It seems that although this <input> located to the left of <label> visually, this is actually not the case. I called a function getBoundingClientRect() for both of these elements, and they actually overlap. Note that they both have position x 838, so technically <input> not to the left of <label>.

And when I select an element <label>I now see that it really overlaps the element <input>.

Note: This is an alpha version of Selenium WebDriver. I spoke with Selenium project manager Simon Stewart and found out that the implementation may change depending on the feedback.

That's all. See you on course!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *