From 9212a473d97f6ac8bd56baaa6e6ed92e1cc5b72d Mon Sep 17 00:00:00 2001 From: Antoine Brunet Date: Fri, 2 Aug 2024 08:39:13 -0400 Subject: [PATCH 1/3] Fixed bug #13642 related to relative locators The bug which can be found at https://github.com/SeleniumHQ/selenium/issues/13642 happened in the table of the first example at https://www.w3schools.com/html/html_tables.aspFor. The table has 7 rows and 3 columns. I added the values of the first 3 rows below where each line represents a row and the values of each row are separated by a comma and a space. All cells are td elements apart from the cells of the first row which are th elements. The table has "collapse" as the value of the "border-collapse" CSS property. Company, Contact, Country Alfreds Futterkiste, Maria Anders, Germany Centro comercial Moctezuma, Francisco Chang, Mexico The person who raised the bug was trying, in Chrome and using relative locators, to get the cell that was to the right of the cell containing "Alfreds Futterkiste" and below the cell containing "Contact". They asserted that the text of the cell they were getting was "Maria Anders" but got "Germany" instead. I created a browser test in Chrome called shouldBeAbleToFindElementWithToRightOfAndBelowWithBorderCollapseInTable reproducing the steps of the bug in the java/test/org/openqa/selenium/support/locators/RelativeLocatorTest.java Java class and got "Mexico". I made all my modifications to the logic of the project inside the javascript/atoms/locators/relative.js JavaScript file. I modified the bot.locators.relative.below_ and the bot.locators.relative.rightOf_ functions and added a util function called twoElementsAreCellsOfATableThatUsesBorderCollapse. Before my modifications, inside the bot.locators.relative.below_ function, for the nested function we returned, we only considered that B was below A if the top of B was below the bottom of A. In a table that has collapsing borders, neighboring cells touch each other. For the the bot.locators.relative.below_ function, I don't call the bot.locators.relative.proximity_ function since I need access to the elements, not just rect1 and rect2. I return a function in which I consider that B is below A also if the top of B is at the same vertical position as the bottom of A and if the two elements are cells of the same table. To verify the first part (before the "and") of the condition I added, I call a utility function I created called twoElementsAreCellsOfATableThatUsesBorderCollapse which takes in the two elements and returns true only if the condition described by its name is true. I modified the documentation of the function bot.locators.relative.below_. The old documentation had the two elements switched. My new documentation does not contain that switch. Before my modifications, inside the bot.locators.relative.rightOf_ function, for the nested function we returned, we only considered that B was to the right of A if the left of B was to the right of the right of A. For the the bot.locators.relative.rightOf_ function, I don't call the bot.locators.relative.proximity_ function since I need access to the elements, not just rect1 and rect2. I return a function in which I consider that B is to the right of A also if the left of B is at the same horizontal position as the right of A and if the two elements are cells of the same table. To verify the first part (before the "and") of the condition I added, I call again the twoElementsAreCellsOfATableThatUsesBorderCollapse function. As I was informed on the Slack application, the tests will be run after I make my pull request. I only ran the test I created, which passed. Fixes #13642 --- .../support/locators/RelativeLocatorTest.java | 14 +++++ javascript/atoms/locators/relative.js | 57 +++++++++++++------ 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/java/test/org/openqa/selenium/support/locators/RelativeLocatorTest.java b/java/test/org/openqa/selenium/support/locators/RelativeLocatorTest.java index 2f9d3b448b8ca..b42c77274c777 100644 --- a/java/test/org/openqa/selenium/support/locators/RelativeLocatorTest.java +++ b/java/test/org/openqa/selenium/support/locators/RelativeLocatorTest.java @@ -35,6 +35,20 @@ class RelativeLocatorTest extends JupiterTestBase { + @Test + void shouldBeAbleToFindElementWithToRightOfAndBelowWithBorderCollapseInTable() { + driver.get("https://www.w3schools.com/html/html_tables.asp"); + + By contactNameForTargetCompany = + RelativeLocator.with(By.tagName("td")) + .toRightOf(By.xpath("//td[text()='Alfreds Futterkiste']")) + .below(By.xpath("//th[text()='Contact']")); + + String actualName = driver.findElement(contactNameForTargetCompany).getText(); + + assertThat(actualName).isEqualTo("Maria Anders"); + } + @Test void shouldBeAbleToFindElementsAboveAnotherWithTagName() { driver.get(appServer.whereIs("relative_locators.html")); diff --git a/javascript/atoms/locators/relative.js b/javascript/atoms/locators/relative.js index 4a0175f46b0e6..e93cc91e71887 100644 --- a/javascript/atoms/locators/relative.js +++ b/javascript/atoms/locators/relative.js @@ -23,6 +23,7 @@ goog.require('bot.locators'); goog.require('goog.array'); goog.require('goog.dom'); goog.require('goog.math.Rect'); +goog.require('goog.style'); /** @@ -79,23 +80,44 @@ bot.locators.relative.above_ = function (selector) { /** - * Relative locator to find elements that are below the expected one. "Below" - * is defined as where the top of the element found by `selector` is below the - * bottom of an element we're comparing to. - * + * Relative locator to find elements that are below the expected one. An element is "below" either when: + * 1. The bottom of the element found by `selector` is above the top of an element we're comparing to + * 2. Or the bottom of the element found by `selector` is at the same vertical position as the element we're comparing + * to and the two elements are cells of the same table. * @param {!Element|function():!Element|!Object} selector Mechanism to be used to find the element. * @return {!Filter} A function that determines whether the selector is below the given element. * @private */ bot.locators.relative.below_ = function (selector) { - return bot.locators.relative.proximity_( - selector, - function (rect1, rect2) { - var bottom = rect1.top + rect1.height; - return bottom < rect2.top; - }); + var toReturn = function (compareTo) { + var element = bot.locators.relative.resolve_(selector); + var rect1 = bot.dom.getClientRect(element); + var rect2 = bot.dom.getClientRect(compareTo); + var bottom = rect1.top + rect1.height; + return (bottom < rect2.top) || ((bottom == rect2.top) && twoElementsAreCellsOfSameTable(element, compareTo)); + }; + return toReturn; }; +/** + * Verifies if element and compareTo are table cells (i.e. td or th elements) and if they are part of the same table. + * @param {*} element Base element of the comparison (for example, for the bot.locators.relative.rightOf_ method, it + * would be the element we want to find an element to the right of) + * @param {*} compareTo Current element + * @returns True only if element and compareTo are table cells (i.e. td or th elements) and if they are part of the same + * table. + */ +function twoElementsAreCellsOfSameTable(element, compareTo) { + var tableCellsTagNames = ["TD", "TH"]; + var elementAndCompareToAreTableCellElements = + tableCellsTagNames.includes(element.tagName) && + tableCellsTagNames.includes(compareTo.tagName); + var cssSelector = "table"; + var elementTable = element.closest(cssSelector); + var compareToTable = compareTo.closest(cssSelector); + var elementAndCompareToAreFromSameTable = elementTable.isEqualNode(compareToTable); + return elementAndCompareToAreTableCellElements && elementAndCompareToAreFromSameTable; +} /** * Relative locator to find elements that are to the left of the expected one. @@ -122,15 +144,16 @@ bot.locators.relative.leftOf_ = function (selector) { * @private */ bot.locators.relative.rightOf_ = function (selector) { - return bot.locators.relative.proximity_( - selector, - function (rect1, rect2) { - var right = rect1.left + rect1.width; - return right < rect2.left; - }); + var toReturn = function (compareTo) { + var element = bot.locators.relative.resolve_(selector); + var rect1 = bot.dom.getClientRect(element); + var rect2 = bot.dom.getClientRect(compareTo); + var right = rect1.left + rect1.width; + return (right < rect2.left) || ((right == rect2.left) && twoElementsAreCellsOfSameTable(element, compareTo)); + }; + return toReturn; }; - /** * Find elements within (by default) 50 pixels of the selected element. An * element is not near itself. From a16004416bed41d1261f66bfd34c97bb96e913f9 Mon Sep 17 00:00:00 2001 From: Diego Molina Date: Wed, 7 Aug 2024 18:01:18 +0000 Subject: [PATCH 2/3] Running format script --- .../selenium/support/locators/RelativeLocatorTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/test/org/openqa/selenium/support/locators/RelativeLocatorTest.java b/java/test/org/openqa/selenium/support/locators/RelativeLocatorTest.java index b42c77274c777..3253e1b0f1a27 100644 --- a/java/test/org/openqa/selenium/support/locators/RelativeLocatorTest.java +++ b/java/test/org/openqa/selenium/support/locators/RelativeLocatorTest.java @@ -40,9 +40,9 @@ void shouldBeAbleToFindElementWithToRightOfAndBelowWithBorderCollapseInTable() { driver.get("https://www.w3schools.com/html/html_tables.asp"); By contactNameForTargetCompany = - RelativeLocator.with(By.tagName("td")) - .toRightOf(By.xpath("//td[text()='Alfreds Futterkiste']")) - .below(By.xpath("//th[text()='Contact']")); + RelativeLocator.with(By.tagName("td")) + .toRightOf(By.xpath("//td[text()='Alfreds Futterkiste']")) + .below(By.xpath("//th[text()='Contact']")); String actualName = driver.findElement(contactNameForTargetCompany).getText(); From 46a1281d31b7356d886618abbd61c0b9989eb893 Mon Sep 17 00:00:00 2001 From: Antoine Brunet Date: Tue, 13 Aug 2024 20:17:16 -0400 Subject: [PATCH 3/3] Added a table with collapsing borders for my browser test to the test page called relative_locators.html which was already existing and used that page for my browser test --- common/src/web/relative_locators.html | 49 ++++++++++++++++++- .../support/locators/RelativeLocatorTest.java | 2 +- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/common/src/web/relative_locators.html b/common/src/web/relative_locators.html index 7857a367bb0a8..6f09befab539b 100644 --- a/common/src/web/relative_locators.html +++ b/common/src/web/relative_locators.html @@ -19,7 +19,15 @@ .big-rectangle { border: 1px solid black; width: 150px; - height: 400px; + height: 400px; + } + #bug-13642-table { + border-collapse: collapse; + margin-top: 25px; + margin-bottom: 25px; + } + th { + border: solid; } @@ -29,6 +37,45 @@

Relative Locator Tests

This is a paragraph of text in the middle.

This text is below. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CompanyContactCountry
Alfreds FutterkisteMaria AndersGermany
Centro comercial MoctezumaFrancisco ChangMexico
Ernst HandelRoland MendelAustria
Island TradingHelen BennettUK
Laughing Bacchus WinecellarsYoshi TannamuriCanada
Magazzini Alimentari RiunitiGiovanni RovelliItaly
diff --git a/java/test/org/openqa/selenium/support/locators/RelativeLocatorTest.java b/java/test/org/openqa/selenium/support/locators/RelativeLocatorTest.java index 3253e1b0f1a27..6933bbdaf8f41 100644 --- a/java/test/org/openqa/selenium/support/locators/RelativeLocatorTest.java +++ b/java/test/org/openqa/selenium/support/locators/RelativeLocatorTest.java @@ -37,7 +37,7 @@ class RelativeLocatorTest extends JupiterTestBase { @Test void shouldBeAbleToFindElementWithToRightOfAndBelowWithBorderCollapseInTable() { - driver.get("https://www.w3schools.com/html/html_tables.asp"); + driver.get(appServer.whereIs("relative_locators.html")); By contactNameForTargetCompany = RelativeLocator.with(By.tagName("td"))