Skip to content

Commit

Permalink
[dotnet] Fix RelativeBy.Near and empty list return (SeleniumHQ#14737)
Browse files Browse the repository at this point in the history
  • Loading branch information
RenderMichael authored and jkim2492 committed Nov 17, 2024
1 parent 304f744 commit 6574a64
Show file tree
Hide file tree
Showing 2 changed files with 215 additions and 17 deletions.
26 changes: 23 additions & 3 deletions dotnet/src/webdriver/RelativeBy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,28 @@ public override ReadOnlyCollection<IWebElement> FindElements(ISearchContext cont
filterParameters["filters"] = this.filters;
parameters["relative"] = filterParameters;
object rawElements = js.ExecuteScript(wrappedAtom, parameters);
ReadOnlyCollection<IWebElement> elements = rawElements as ReadOnlyCollection<IWebElement>;
return elements;

if (rawElements is ReadOnlyCollection<IWebElement> elements)
{
return elements;
}

// De-serializer quirk - if the response is empty then the de-serializer will not know we're getting back elements
// We will have a ReadOnlyCollection<object>

if (rawElements is ReadOnlyCollection<object> elementsObj)
{
if (elementsObj.Count == 0)
{
#if NET8_0_OR_GREATER
return ReadOnlyCollection<IWebElement>.Empty;
#else
return new List<IWebElement>().AsReadOnly();
#endif
}
}

throw new WebDriverException($"Could not de-serialize element list response{Environment.NewLine}{rawElements}");
}

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

Dictionary<string, object> filter = new Dictionary<string, object>();
filter["kind"] = "near";
filter["args"] = new List<object>() { GetSerializableObject(locator), "distance", atMostDistanceInPixels };
filter["args"] = new List<object>() { GetSerializableObject(locator), atMostDistanceInPixels };
this.filters.Add(filter);

return new RelativeBy(this.root, this.filters);
Expand Down
206 changes: 192 additions & 14 deletions dotnet/test/common/RelativeLocatorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
using OpenQA.Selenium.Environment;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

namespace OpenQA.Selenium
{
Expand All @@ -29,21 +30,42 @@ namespace OpenQA.Selenium
public class RelativeLocatorTest : DriverTestFixture
{
[Test]
public void ShouldBeAbleToFindElementsAboveAnother()
public void ShouldBeAbleToFindElementsAboveAnotherWithTagName()
{
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));

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

ReadOnlyCollection<IWebElement> elements = driver.FindElements(RelativeBy.WithLocator(By.TagName("p")).Above(lowest));
List<string> elementIds = new List<string>();
foreach (IWebElement element in elements)
{
string id = element.GetAttribute("id");
elementIds.Add(id);
}

Assert.That(elementIds, Is.EquivalentTo(new List<string>() { "above", "mid" }));
var values = elements.Select(element => element.GetDomAttribute("id"));
Assert.That(values, Is.EquivalentTo(new List<string>() { "above", "mid" }));
}

[Test]
public void ShouldBeAbleToFindElementsAboveAnotherWithXpath()
{
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));

IWebElement lowest = driver.FindElement(By.Id("seventh"));

var elements = driver.FindElements(RelativeBy.WithLocator(By.XPath("//td[1]")).Above(lowest));

var values = elements.Select(element => element.GetDomAttribute("id"));
Assert.That(values, Is.EquivalentTo(new List<string> { "fourth", "first" }));
}

[Test]
public void ShouldBeAbleToFindElementsAboveAnotherWithCssSelector()
{
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));

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

var elements = driver.FindElements(RelativeBy.WithLocator(By.CssSelector("p")).Above(lowest));

var values = elements.Select(element => element.GetDomAttribute("id"));
Assert.That(values, Is.EquivalentTo(new List<string> { "mid", "above" }));
}

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

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

List<string> elementIds = new List<string>();
foreach (IWebElement element in seen)
var elementIds = seen.Select(element => element.GetDomAttribute("id"));
Assert.That(elementIds, Is.EquivalentTo(new List<string>() { "third" }));
}


[Test]
public void ShouldBeAbleToCombineFiltersWithXpath()
{
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));

ReadOnlyCollection<IWebElement> seen = driver.FindElements(RelativeBy.WithLocator(By.XPath("//td[1]")).Below(By.Id("second")).Above(By.Id("seventh")));

var values = seen.Select(element => element.GetDomAttribute("id"));
Assert.That(values, Is.EquivalentTo(new List<string> { "fourth" }));
}

[Test]
public void ShouldBeAbleToCombineFiltersWithCssSelector()
{
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));

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

var values = seen.Select(element => element.GetDomAttribute("id"));
Assert.That(values, Is.EquivalentTo(new List<string> { "third" }));
}

[Test]
public void ExerciseNearLocatorWithTagName()
{
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));

ReadOnlyCollection<IWebElement> seen = driver.FindElements(RelativeBy.WithLocator(By.TagName("td")).Near(By.Id("center")));

// Elements are sorted by proximity and then DOM insertion order.
// Proximity is determined using distance from center points, so
// we expect the order to be:
// 1. Directly above (short vertical distance, first in DOM)
// 2. Directly below (short vertical distance, later in DOM)
// 3. Directly left (slight longer distance horizontally, first in DOM)
// 4. Directly right (slight longer distance horizontally, later in DOM)
// 5-8. Diagonally close (pythagoras sorting, with top row first
// because of DOM insertion order)
var values = seen.Select(element => element.GetDomAttribute("id"));
Assert.That(values, Is.EquivalentTo(new List<string> { "second", "eighth", "fourth", "sixth", "first", "third", "seventh", "ninth" }));
}

[Test]
public void ExerciseNearLocatorWithXpath()
{
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));

ReadOnlyCollection<IWebElement> seen = driver.FindElements(RelativeBy.WithLocator(By.XPath("//td")).Near(By.Id("center")));

// Elements are sorted by proximity and then DOM insertion order.
// Proximity is determined using distance from center points, so
// we expect the order to be:
// 1. Directly above (short vertical distance, first in DOM)
// 2. Directly below (short vertical distance, later in DOM)
// 3. Directly left (slight longer distance horizontally, first in DOM)
// 4. Directly right (slight longer distance horizontally, later in DOM)
// 5-8. Diagonally close (pythagoras sorting, with top row first
// because of DOM insertion order)
var values = seen.Select(element => element.GetDomAttribute("id"));
Assert.That(values, Is.EquivalentTo(new List<string> { "second", "eighth", "fourth", "sixth", "first", "third", "seventh", "ninth" }));
}

[Test]
public void ExerciseNearLocatorWithCssSelector()
{
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));

ReadOnlyCollection<IWebElement> seen = driver.FindElements(RelativeBy.WithLocator(By.CssSelector("td")).Near(By.Id("center")));

// Elements are sorted by proximity and then DOM insertion order.
// Proximity is determined using distance from center points, so
// we expect the order to be:
// 1. Directly above (short vertical distance, first in DOM)
// 2. Directly below (short vertical distance, later in DOM)
// 3. Directly left (slight longer distance horizontally, first in DOM)
// 4. Directly right (slight longer distance horizontally, later in DOM)
// 5-8. Diagonally close (pythagoras sorting, with top row first
// because of DOM insertion order)
var values = seen.Select(element => element.GetDomAttribute("id"));
Assert.That(values, Is.EquivalentTo(new List<string> { "second", "eighth", "fourth", "sixth", "first", "third", "seventh", "ninth" }));
}

[Test]
public void EnsureNoRepeatedElements()
{
driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage()
.WithTitle("Repeated Elements")
.WithStyles(
"""
.c {
position: absolute;
border: 1px solid black;
height: 50px;
width: 50px;
}
"""
)
.WithBody(
"""
<span style="position: relative;">
<div id= "a" class="c" style="left:25;top:0;">El-A</div>
<div id= "b" class="c" style="left:78;top:30;">El-B</div>
<div id= "c" class="c" style="left:131;top:60;">El-C</div>
<div id= "d" class="c" style="left:0;top:53;">El-D</div>
<div id= "e" class="c" style="left:53;top:83;">El-E</div>
<div id= "f" class="c" style="left:106;top:113;">El-F</div>
</span>
"""
));

IWebElement @base = driver.FindElement(By.Id("e"));
ReadOnlyCollection<IWebElement> cells = driver.FindElements(RelativeBy.WithLocator(By.TagName("div")).Above(@base));

IWebElement a = driver.FindElement(By.Id("a"));
IWebElement b = driver.FindElement(By.Id("b"));

var values = cells.Select(element => element.GetDomAttribute("id"));
Assert.That(values, Is.EqualTo(new List<string> { b.GetDomAttribute("id"), a.GetDomAttribute("id") }));
}

[Test]
public void NearLocatorShouldFindNearElements()
{
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));

var rect1 = driver.FindElement(By.Id("rect1"));

var rect2 = driver.FindElement(RelativeBy.WithLocator(By.Id("rect2")).Near(rect1));

Assert.That(rect2.GetDomAttribute("id"), Is.EqualTo("rect2"));
}

[Test]
public void NearLocatorShouldNotFindFarElements()
{
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));

var rect3 = driver.FindElement(By.Id("rect3"));

Assert.That(() =>
{
string id = element.GetAttribute("id");
elementIds.Add(id);
}
var rect2 = driver.FindElement(RelativeBy.WithLocator(By.Id("rect4")).Near(rect3));

Assert.That(elementIds, Is.EquivalentTo(new List<string>() { "third" }));
}, 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"));
}

//------------------------------------------------------------------
// Tests below here are not included in the Java test suite
//------------------------------------------------------------------

[Test]
public void ShouldReturnEmptyListWhenNoElementsFound()
{
driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("relative_locators.html"));

var elements = driver.FindElements(RelativeBy.WithLocator(By.TagName("does-not-exist")));

Assert.That(elements, Is.Empty);
}
}
}

0 comments on commit 6574a64

Please sign in to comment.