Skip to content

Commit 6a3f867

Browse files
authored
Allow RelativeBy to start with any locator, not just tag name (#9273)
1 parent 9623950 commit 6a3f867

File tree

2 files changed

+119
-7
lines changed

2 files changed

+119
-7
lines changed

java/client/src/org/openqa/selenium/support/locators/RelativeLocator.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public class RelativeLocator {
7272

7373
private static final Json JSON = new Json();
7474
private static final String FIND_ELEMENTS;
75+
7576
static {
7677
try {
7778
String location = String.format(
@@ -87,14 +88,31 @@ public class RelativeLocator {
8788
throw new UncheckedIOException(e);
8889
}
8990
}
91+
9092
private static final int CLOSE_IN_PIXELS = 100;
9193

9294
/**
9395
* Start of a relative locator, finding elements by tag name.
9496
*/
95-
public static RelativeBy withTagName(String tagName) {
97+
98+
public static RelativeBy with(By by) {
99+
Require.nonNull("By to look for", by);
100+
return new RelativeBy(by);
101+
}
102+
103+
public static By tagName(String tagName) {
96104
Require.nonNull("Tag name to look for", tagName);
97-
return new RelativeBy(By.tagName(tagName));
105+
return By.tagName(tagName);
106+
}
107+
108+
public static By xpath(String xpathExpression) {
109+
Require.nonNull("xpath to look for", xpathExpression);
110+
return By.xpath(xpathExpression);
111+
}
112+
113+
public static By cssSelector(String cssSelectorExpression) {
114+
Require.nonNull("css selector to look for", cssSelectorExpression);
115+
return By.cssSelector(cssSelectorExpression);
98116
}
99117

100118
public static class RelativeBy extends By implements By.Remotable {

java/client/test/org/openqa/selenium/support/locators/RelativeLocatorTest.java

Lines changed: 99 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,36 +28,130 @@
2828
import static java.util.Collections.singletonList;
2929
import static org.assertj.core.api.Assertions.assertThat;
3030
import static org.openqa.selenium.support.locators.RelativeLocator.withTagName;
31+
import static org.openqa.selenium.support.locators.RelativeLocator.withXpath;
32+
import static org.openqa.selenium.support.locators.RelativeLocator.withCssSelector;
33+
3134

3235
public class RelativeLocatorTest extends JUnit4TestBase {
3336

3437
@Test
35-
public void shouldBeAbleToFindElementsAboveAnother() {
38+
public void shouldBeAbleToFindElementsAboveAnotherWithTagName() {
3639
driver.get(appServer.whereIs("relative_locators.html"));
3740

3841
WebElement lowest = driver.findElement(By.id("below"));
3942

40-
List<WebElement> elements = driver.findElements(withTagName("p").above(lowest));
43+
List<WebElement> elements = driver.findElements(with(tagName("p")).above(lowest));
4144
List<String> ids = elements.stream().map(e -> e.getAttribute("id")).collect(Collectors.toList());
4245

4346
assertThat(ids).containsExactly("mid", "above");
47+
48+
}
49+
50+
@Test
51+
public void shouldBeAbleToFindElementsAboveAnotherWithXpath() {
52+
driver.get(appServer.whereIs("relative_locators.html"));
53+
54+
WebElement lowest = driver.findElement(By.id("seventh"));
55+
56+
List<WebElement> seen = driver.findElements(with(xpath("//td[1]")).above(lowest));
57+
58+
List<String> ids = seen.stream().map(e -> e.getAttribute("id")).collect(Collectors.toList());
59+
60+
assertThat(ids).containsExactly("fourth", "first");
61+
}
62+
63+
@Test
64+
public void shouldBeAbleToFindElementsAboveAnotherwithCssSelector() {
65+
driver.get(appServer.whereIs("relative_locators.html"));
66+
67+
WebElement lowest = driver.findElement(By.id("below"));
68+
69+
List<WebElement> elements = driver.findElements(with(cssSelector("p")).above(lowest));
70+
List<String> ids = elements.stream().map(e -> e.getAttribute("id")).collect(Collectors.toList());
71+
72+
assertThat(ids).containsExactly("mid", "above");
73+
4474
}
4575

4676
@Test
4777
public void shouldBeAbleToCombineFilters() {
4878
driver.get(appServer.whereIs("relative_locators.html"));
4979

50-
List<WebElement> seen = driver.findElements(withTagName("td").above(By.id("center")).toRightOf(By.id("second")));
80+
List<WebElement> seen = driver.findElements(with(tagName("td")).above(By.id("center")).toRightOf(By.id("second")));
5181

5282
List<String> ids = seen.stream().map(e -> e.getAttribute("id")).collect(Collectors.toList());
83+
5384
assertThat(ids).containsExactly("third");
5485
}
5586

5687
@Test
57-
public void exerciseNearLocator() {
88+
public void shouldBeAbleToCombineFiltersWithXpath() {
89+
driver.get(appServer.whereIs("relative_locators.html"));
90+
91+
List<WebElement> seen = driver.findElements(with(xpath("//td[1]")).below(By.id("second")).above(By.id("seventh")));
92+
93+
List<String> ids = seen.stream().map(e -> e.getAttribute("id")).collect(Collectors.toList());
94+
95+
assertThat(ids).containsExactly("fourth");
96+
97+
}
98+
99+
@Test
100+
public void shouldBeAbleToCombineFiltersWithCssSelector() {
101+
driver.get(appServer.whereIs("relative_locators.html"));
102+
103+
104+
List<WebElement> seen = driver.findElements(with(cssSelector("td")).above(By.id("center")).toRightOf(By.id("second")));
105+
106+
List<String> ids = seen.stream().map(e -> e.getAttribute("id")).collect(Collectors.toList());
107+
108+
assertThat(ids).containsExactly("third");
109+
}
110+
111+
@Test
112+
public void exerciseNearLocatorWithTagName() {
113+
driver.get(appServer.whereIs("relative_locators.html"));
114+
115+
List<WebElement> seen = driver.findElements(with(tagName("td")).near(By.id("center")));
116+
117+
// Elements are sorted by proximity and then DOM insertion order.
118+
// Proximity is determined using distance from center points, so
119+
// we expect the order to be:
120+
// 1. Directly above (short vertical distance, first in DOM)
121+
// 2. Directly below (short vertical distance, later in DOM)
122+
// 3. Directly left (slight longer distance horizontally, first in DOM)
123+
// 4. Directly right (slight longer distance horizontally, later in DOM)
124+
// 5-8. Diagonally close (pythagorus sorting, with top row first
125+
// because of DOM insertion order)
126+
List<String> ids = seen.stream().map(e -> e.getAttribute("id")).collect(Collectors.toList());
127+
assertThat(ids).containsExactly("second", "eighth", "fourth", "sixth", "first", "third", "seventh", "ninth");
128+
}
129+
130+
@Test
131+
public void exerciseNearLocatorWithXpath() {
132+
driver.get(appServer.whereIs("relative_locators.html"));
133+
134+
List<WebElement> seen = driver.findElements(with(xpath("//td")).near(By.id("center")));
135+
136+
// Elements are sorted by proximity and then DOM insertion order.
137+
// Proximity is determined using distance from center points, so
138+
// we expect the order to be:
139+
// 1. Directly above (short vertical distance, first in DOM)
140+
// 2. Directly below (short vertical distance, later in DOM)
141+
// 3. Directly left (slight longer distance horizontally, first in DOM)
142+
// 4. Directly right (slight longer distance horizontally, later in DOM)
143+
// 5-8. Diagonally close (pythagorus sorting, with top row first
144+
// because of DOM insertion order)
145+
List<String> ids = seen.stream().map(e -> e.getAttribute("id")).collect(Collectors.toList());
146+
147+
assertThat(ids).containsExactly("second", "eighth", "fourth", "sixth", "first", "third", "seventh", "ninth");
148+
}
149+
150+
@Test
151+
public void exerciseNearLocatorWithCssSelector() {
58152
driver.get(appServer.whereIs("relative_locators.html"));
59153

60-
List<WebElement> seen = driver.findElements(withTagName("td").near(By.id("center")));
154+
List<WebElement> seen = driver.findElements(with(cssSelector("td")).near(By.id("center")));
61155

62156
// Elements are sorted by proximity and then DOM insertion order.
63157
// Proximity is determined using distance from center points, so

0 commit comments

Comments
 (0)