Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dotnet] Fix RelativeBy.Near and empty list return, port Java tests #14737

Merged
merged 11 commits into from
Nov 14, 2024
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)
nvborisenko marked this conversation as resolved.
Show resolved Hide resolved
{
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 };
nvborisenko marked this conversation as resolved.
Show resolved Hide resolved
this.filters.Add(filter);

return new RelativeBy(this.root, this.filters);
Expand Down
202 changes: 188 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,166 @@ 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 {\n"
RenderMichael marked this conversation as resolved.
Show resolved Hide resolved
+ " \tposition: absolute;\n"
+ " \tborder: 1px solid black;\n"
+ " \theight: 50px;\n"
+ " \twidth: 50px;\n"
+ " }"
)
.WithBody(
"<span style=\"position: relative;\">\n"
+ " <div id= \"a\" class=\"c\" style=\"left:25;top:0;\">El-A</div>\n"
+ " <div id= \"b\" class=\"c\" style=\"left:78;top:30;\">El-B</div>\n"
+ " <div id= \"c\" class=\"c\" style=\"left:131;top:60;\">El-C</div>\n"
+ " <div id= \"d\" class=\"c\" style=\"left:0;top:53;\">El-D</div>\n"
+ " <div id= \"e\" class=\"c\" style=\"left:53;top:83;\">El-E</div>\n"
+ " <div id= \"f\" class=\"c\" style=\"left:106;top:113;\">El-F</div>\n"
+ " </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
nvborisenko marked this conversation as resolved.
Show resolved Hide resolved
//------------------------------------------------------------------

[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);
}
}
}