Selenium 4 Relative Locator Examples
A few Selenium 4 relative locator examples in Ruby language
This article is included in my “How to in Selenium WebDriver” series.
Relative locators, introduced in Selenium 4, provide a new way to find element(s) based on the position of another element. There are five locators:
left
right
above
below
near
In this article, I will show some examples using relative locators in Selenium 4.
Table of Contents:
· Get the element on the right of a checkbox
· Get the element(s) on the left of a checkbox
· Near
· Above (in a table)
· Below (in a table)
· Summary
Get the element on the right of a checkbox
HTML:
<p><input type="checkbox" id="test_products_only_flag">
<span>Test automation products only</span></p>
Script with Relative (Right):
start_cell = driver.find_element(id: "test_products_only_flag")
elem_label = driver.find_element(relative: { tag_name: "span", right: start_cell })
expect(elem_label.text).to eq("Test automation products only")
XPath alternative
driver.find_element(xpath: "//input[@id='test_products_only_flag']/following-sibling::*")
Get the element(s) on the left of a checkbox
HTML:
<p><span>I accept Terms and Conditions</span>
<input type="checkbox" id="accept_terms" style="margin-bottom: 6px;"></p>
Script with Relative (Left):
start_cell = driver.find_element(id: "accept_terms")
elem_label = driver.find_element(relative: { tag_name: "span", left: start_cell })
expect(elem_label.text).to eq("I accept Terms and Conditions")
Find multiple
You can use `find_elements` with relative locators as well.
left_spans = driver.find_elements(relative: { tag_name: "span", left: start_cell })
left_span_texts = left_spans.collect{|x| x.text}
expect(left_span_texts).to eq(["I accept Terms and Conditions", "WhenWise", "TestWise",
"SiteWise CMS", "BuildWise", "ClinicWise"])
Please note the order of returned elements, the first one is the closest.
XPath alternative
driver.find_element(xpath: "//input[@id='accept_terms']/preceding-sibling::*")
Near
HTML:
<div><a href="" style="margin-right:100px;">First</a>
<a href="">Prev</a> <b id="current-page">2</b> <a href="">Next</a>
<a href="" style="margin-left: 200px;">Last</a>
</div>
Script with Relative (Near):
start_cell = driver.find_element(id: "current-page")
neighbours = driver.find_elements(relative: { tag_name: "a", near: start_cell })
expect(neighbours.collect { |x| x.text }).to eq(["Prev", "Next"])
With distance constraints.
the_cell = driver.find_elements(relative: { tag_name: "a",
near: { id: "current-page", distance: 20 } })
expect(the_cell.collect { |x| x.text }).to eq(["Prev", "Next"])
the_cell = driver.find_elements(relative: { tag_name: "a",
near: { id: "current-page", distance: 145 } })
expect(the_cell.collect { |x| x.text }).to eq(["Prev", "Next", "First"])
the_cell = driver.find_elements(relative: { tag_name: "a",
near: { id: "current-page", distance: 208 } })
# include the above as well
expect(the_cell.collect { |x| x.text }).to include("https://whenwise.com")
expect(the_cell.collect { |x| x.text }).not_to include("Last")
the_cell = driver.find_elements(relative: { tag_name: "a",
near: { id: "current-page", distance: 238 } })
expect(the_cell.collect { |x| x.text }).to include("Last")
Above (in a table)
HTML:
<tbody>
<tr>
<td data-value="ClinicWise"><span id="clinicwise">ClinicWise</span></td>
<td>2013</td> <!-- .... -->
</tr>
<tr>
<td data-value="BuildWise"><span id="buildwise">BuildWise</span></td>
<td>2010</td> <!-- .... -->
</tr>
<tr>
<td data-value="SiteWise CMS"><span id="sitewise">SiteWise CMS</span></td>
<td>2014</td> <!-- .... -->
</tr>
<tr>
<td data-value="TestWise"><span id="testwise">TestWise</span></td>
<td><span>2007</span></td> <!-- .... -->
</tr>
<tr>
<td data-value="TestWise"><span id="testwise">WhenWise</span></td>
<td>2018</td> <!-- .... -->
</tr>
</tbody>
Script with Relative (Above):
start_cell = driver.find_element(id: "sitewise")
the_above = driver.find_element(relative: { tag_name: "span", above: start_cell })
expect(the_above.text).to eq("BuildWise")
You can use find_elements
here as well. In this case, the returned texts are `[“BuildWise”, “ClinicWise”, …]`.
Below (in a table)
The below works the same way. Here I will show an example of the immature status of relative locators.
Let’s say we pick the “2014” as the starting cell (3rd row and 2nd column), the expected the below cell is “2007”, right? However, it is not the case.
start_cell = driver.find_element(xpath: "//tr[3]/td[2]")
expect(start_cell.text).to eq("2014")
the_below = driver.find_element(relative: { tag_name: "td", below: start_cell })
puts the_below.text # => 2018, expected 2007
The returned text is “2018”, the one further below. Why? Frankly, I don’t know. But I came up with one explanation after the following experiment. This time, I chose “2007” (4th row and 2nd column), instead of the tag td
, I used the tag span
inside the td
.
It worked. Some might think this is just a workaround, but the previous example is not correct. I agree. Remember, relative locators are still relatively new.
XPath alternative
driver.find_element(xpath: “//table[@id=’grid’]//tr[3]/td[2]”).text # => 2014
driver.find_element(xpath: “//table[@id=’grid’]//tr[2]/td[2]”).text # => 2010 (above)
driver.find_element(xpath: “//table[@id=’grid’]//tr[4]/td[2]”).text # => 2007 (below)
driver.find_element(xpath: “//table[@id=’grid’]//tr[3]/td[1]”).text # => SiteWise CMS (left)
driver.find_element(xpath: “//table[@id=’grid’]//tr[3]/td[3]”).text # => http://sitewisecms.com (right)
Summary
There was a hype of relative locators (firstly introduced in Sahi Pro). However, I have my reservations for three reasons:
The test automation attempts (I witnessed) using Sahi Pro, by my judgement, all failed.
I rescued a few of them by replacing Sahi with raw Selenium WebDriver (v2 and v3).I don’t recall the need to use relative locators, XPath is fine.
The proximity check (used in relative locators) does not always work as expected.
Here are the quotes from the Selenium 4 master course on TestGuild by Simon Stewart, Selenium project lead:
“the way that proximity works, things are looked like they should be found, sometimes aren’t”
“be aware that relative locators are very sensitive to page layout"“Relative locators do not guarantee that they will find only the right element. ” — https://chercher.tech/java/selenium-relative-locators
I won’t use relative locators in my projects, at least not yet. I am happy with XPath.
Please check out my book for more examples like the above: “Selenium WebDriver Recipes in Ruby”.