Skip to content

Commit dd0b2ba

Browse files
[dotnet] Fix RelativeBy.Near and empty list return (#14737)
1 parent 0a724b1 commit dd0b2ba

File tree

2 files changed

+215
-17
lines changed

2 files changed

+215
-17
lines changed

Diff for: dotnet/src/webdriver/RelativeBy.cs

+23-3
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,28 @@ public override ReadOnlyCollection<IWebElement> FindElements(ISearchContext cont
108108
filterParameters["filters"] = this.filters;
109109
parameters["relative"] = filterParameters;
110110
object rawElements = js.ExecuteScript(wrappedAtom, parameters);
111-
ReadOnlyCollection<IWebElement> elements = rawElements as ReadOnlyCollection<IWebElement>;
112-
return elements;
111+
112+
if (rawElements is ReadOnlyCollection<IWebElement> elements)
113+
{
114+
return elements;
115+
}
116+
117+
// De-serializer quirk - if the response is empty then the de-serializer will not know we're getting back elements
118+
// We will have a ReadOnlyCollection<object>
119+
120+
if (rawElements is ReadOnlyCollection<object> elementsObj)
121+
{
122+
if (elementsObj.Count == 0)
123+
{
124+
#if NET8_0_OR_GREATER
125+
return ReadOnlyCollection<IWebElement>.Empty;
126+
#else
127+
return new List<IWebElement>().AsReadOnly();
128+
#endif
129+
}
130+
}
131+
132+
throw new WebDriverException($"Could not de-serialize element list response{Environment.NewLine}{rawElements}");
113133
}
114134

115135
/// <summary>
@@ -288,7 +308,7 @@ private RelativeBy Near(object locator, int atMostDistanceInPixels)
288308

289309
Dictionary<string, object> filter = new Dictionary<string, object>();
290310
filter["kind"] = "near";
291-
filter["args"] = new List<object>() { GetSerializableObject(locator), "distance", atMostDistanceInPixels };
311+
filter["args"] = new List<object>() { GetSerializableObject(locator), atMostDistanceInPixels };
292312
this.filters.Add(filter);
293313

294314
return new RelativeBy(this.root, this.filters);

Diff for: dotnet/test/common/RelativeLocatorTest.cs

+192-14
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using OpenQA.Selenium.Environment;
2222
using System.Collections.Generic;
2323
using System.Collections.ObjectModel;
24+
using System.Linq;
2425

2526
namespace OpenQA.Selenium
2627
{
@@ -29,21 +30,42 @@ namespace OpenQA.Selenium
2930
public class RelativeLocatorTest : DriverTestFixture
3031
{
3132
[Test]
32-
public void ShouldBeAbleToFindElementsAboveAnother()
33+
public void ShouldBeAbleToFindElementsAboveAnotherWithTagName()
3334
{
3435
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));
3536

3637
IWebElement lowest = driver.FindElement(By.Id("below"));
3738

3839
ReadOnlyCollection<IWebElement> elements = driver.FindElements(RelativeBy.WithLocator(By.TagName("p")).Above(lowest));
39-
List<string> elementIds = new List<string>();
40-
foreach (IWebElement element in elements)
41-
{
42-
string id = element.GetAttribute("id");
43-
elementIds.Add(id);
44-
}
4540

46-
Assert.That(elementIds, Is.EquivalentTo(new List<string>() { "above", "mid" }));
41+
var values = elements.Select(element => element.GetDomAttribute("id"));
42+
Assert.That(values, Is.EquivalentTo(new List<string>() { "above", "mid" }));
43+
}
44+
45+
[Test]
46+
public void ShouldBeAbleToFindElementsAboveAnotherWithXpath()
47+
{
48+
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));
49+
50+
IWebElement lowest = driver.FindElement(By.Id("seventh"));
51+
52+
var elements = driver.FindElements(RelativeBy.WithLocator(By.XPath("//td[1]")).Above(lowest));
53+
54+
var values = elements.Select(element => element.GetDomAttribute("id"));
55+
Assert.That(values, Is.EquivalentTo(new List<string> { "fourth", "first" }));
56+
}
57+
58+
[Test]
59+
public void ShouldBeAbleToFindElementsAboveAnotherWithCssSelector()
60+
{
61+
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));
62+
63+
IWebElement lowest = driver.FindElement(By.Id("below"));
64+
65+
var elements = driver.FindElements(RelativeBy.WithLocator(By.CssSelector("p")).Above(lowest));
66+
67+
var values = elements.Select(element => element.GetDomAttribute("id"));
68+
Assert.That(values, Is.EquivalentTo(new List<string> { "mid", "above" }));
4769
}
4870

4971
[Test]
@@ -53,14 +75,170 @@ public void ShouldBeAbleToCombineFilters()
5375

5476
ReadOnlyCollection<IWebElement> seen = driver.FindElements(RelativeBy.WithLocator(By.TagName("td")).Above(By.Id("center")).RightOf(By.Id("second")));
5577

56-
List<string> elementIds = new List<string>();
57-
foreach (IWebElement element in seen)
78+
var elementIds = seen.Select(element => element.GetDomAttribute("id"));
79+
Assert.That(elementIds, Is.EquivalentTo(new List<string>() { "third" }));
80+
}
81+
82+
83+
[Test]
84+
public void ShouldBeAbleToCombineFiltersWithXpath()
85+
{
86+
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));
87+
88+
ReadOnlyCollection<IWebElement> seen = driver.FindElements(RelativeBy.WithLocator(By.XPath("//td[1]")).Below(By.Id("second")).Above(By.Id("seventh")));
89+
90+
var values = seen.Select(element => element.GetDomAttribute("id"));
91+
Assert.That(values, Is.EquivalentTo(new List<string> { "fourth" }));
92+
}
93+
94+
[Test]
95+
public void ShouldBeAbleToCombineFiltersWithCssSelector()
96+
{
97+
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));
98+
99+
ReadOnlyCollection<IWebElement> seen = driver.FindElements(
100+
RelativeBy.WithLocator(By.CssSelector("td")).Above(By.Id("center")).RightOf(By.Id("second")));
101+
102+
var values = seen.Select(element => element.GetDomAttribute("id"));
103+
Assert.That(values, Is.EquivalentTo(new List<string> { "third" }));
104+
}
105+
106+
[Test]
107+
public void ExerciseNearLocatorWithTagName()
108+
{
109+
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));
110+
111+
ReadOnlyCollection<IWebElement> seen = driver.FindElements(RelativeBy.WithLocator(By.TagName("td")).Near(By.Id("center")));
112+
113+
// Elements are sorted by proximity and then DOM insertion order.
114+
// Proximity is determined using distance from center points, so
115+
// we expect the order to be:
116+
// 1. Directly above (short vertical distance, first in DOM)
117+
// 2. Directly below (short vertical distance, later in DOM)
118+
// 3. Directly left (slight longer distance horizontally, first in DOM)
119+
// 4. Directly right (slight longer distance horizontally, later in DOM)
120+
// 5-8. Diagonally close (pythagoras sorting, with top row first
121+
// because of DOM insertion order)
122+
var values = seen.Select(element => element.GetDomAttribute("id"));
123+
Assert.That(values, Is.EquivalentTo(new List<string> { "second", "eighth", "fourth", "sixth", "first", "third", "seventh", "ninth" }));
124+
}
125+
126+
[Test]
127+
public void ExerciseNearLocatorWithXpath()
128+
{
129+
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));
130+
131+
ReadOnlyCollection<IWebElement> seen = driver.FindElements(RelativeBy.WithLocator(By.XPath("//td")).Near(By.Id("center")));
132+
133+
// Elements are sorted by proximity and then DOM insertion order.
134+
// Proximity is determined using distance from center points, so
135+
// we expect the order to be:
136+
// 1. Directly above (short vertical distance, first in DOM)
137+
// 2. Directly below (short vertical distance, later in DOM)
138+
// 3. Directly left (slight longer distance horizontally, first in DOM)
139+
// 4. Directly right (slight longer distance horizontally, later in DOM)
140+
// 5-8. Diagonally close (pythagoras sorting, with top row first
141+
// because of DOM insertion order)
142+
var values = seen.Select(element => element.GetDomAttribute("id"));
143+
Assert.That(values, Is.EquivalentTo(new List<string> { "second", "eighth", "fourth", "sixth", "first", "third", "seventh", "ninth" }));
144+
}
145+
146+
[Test]
147+
public void ExerciseNearLocatorWithCssSelector()
148+
{
149+
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));
150+
151+
ReadOnlyCollection<IWebElement> seen = driver.FindElements(RelativeBy.WithLocator(By.CssSelector("td")).Near(By.Id("center")));
152+
153+
// Elements are sorted by proximity and then DOM insertion order.
154+
// Proximity is determined using distance from center points, so
155+
// we expect the order to be:
156+
// 1. Directly above (short vertical distance, first in DOM)
157+
// 2. Directly below (short vertical distance, later in DOM)
158+
// 3. Directly left (slight longer distance horizontally, first in DOM)
159+
// 4. Directly right (slight longer distance horizontally, later in DOM)
160+
// 5-8. Diagonally close (pythagoras sorting, with top row first
161+
// because of DOM insertion order)
162+
var values = seen.Select(element => element.GetDomAttribute("id"));
163+
Assert.That(values, Is.EquivalentTo(new List<string> { "second", "eighth", "fourth", "sixth", "first", "third", "seventh", "ninth" }));
164+
}
165+
166+
[Test]
167+
public void EnsureNoRepeatedElements()
168+
{
169+
driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage()
170+
.WithTitle("Repeated Elements")
171+
.WithStyles(
172+
"""
173+
.c {
174+
position: absolute;
175+
border: 1px solid black;
176+
height: 50px;
177+
width: 50px;
178+
}
179+
"""
180+
)
181+
.WithBody(
182+
"""
183+
<span style="position: relative;">
184+
<div id= "a" class="c" style="left:25;top:0;">El-A</div>
185+
<div id= "b" class="c" style="left:78;top:30;">El-B</div>
186+
<div id= "c" class="c" style="left:131;top:60;">El-C</div>
187+
<div id= "d" class="c" style="left:0;top:53;">El-D</div>
188+
<div id= "e" class="c" style="left:53;top:83;">El-E</div>
189+
<div id= "f" class="c" style="left:106;top:113;">El-F</div>
190+
</span>
191+
"""
192+
));
193+
194+
IWebElement @base = driver.FindElement(By.Id("e"));
195+
ReadOnlyCollection<IWebElement> cells = driver.FindElements(RelativeBy.WithLocator(By.TagName("div")).Above(@base));
196+
197+
IWebElement a = driver.FindElement(By.Id("a"));
198+
IWebElement b = driver.FindElement(By.Id("b"));
199+
200+
var values = cells.Select(element => element.GetDomAttribute("id"));
201+
Assert.That(values, Is.EqualTo(new List<string> { b.GetDomAttribute("id"), a.GetDomAttribute("id") }));
202+
}
203+
204+
[Test]
205+
public void NearLocatorShouldFindNearElements()
206+
{
207+
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));
208+
209+
var rect1 = driver.FindElement(By.Id("rect1"));
210+
211+
var rect2 = driver.FindElement(RelativeBy.WithLocator(By.Id("rect2")).Near(rect1));
212+
213+
Assert.That(rect2.GetDomAttribute("id"), Is.EqualTo("rect2"));
214+
}
215+
216+
[Test]
217+
public void NearLocatorShouldNotFindFarElements()
218+
{
219+
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));
220+
221+
var rect3 = driver.FindElement(By.Id("rect3"));
222+
223+
Assert.That(() =>
58224
{
59-
string id = element.GetAttribute("id");
60-
elementIds.Add(id);
61-
}
225+
var rect2 = driver.FindElement(RelativeBy.WithLocator(By.Id("rect4")).Near(rect3));
62226

63-
Assert.That(elementIds, Is.EquivalentTo(new List<string>() { "third" }));
227+
}, Throws.TypeOf<NoSuchElementException>().With.Message.EqualTo("Unable to find element; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#no-such-element-exception"));
228+
}
229+
230+
//------------------------------------------------------------------
231+
// Tests below here are not included in the Java test suite
232+
//------------------------------------------------------------------
233+
234+
[Test]
235+
public void ShouldReturnEmptyListWhenNoElementsFound()
236+
{
237+
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));
238+
239+
var elements = driver.FindElements(RelativeBy.WithLocator(By.TagName("does-not-exist")));
240+
241+
Assert.That(elements, Is.Empty);
64242
}
65243
}
66244
}

0 commit comments

Comments
 (0)