Skip to content

Commit 9aa1a7f

Browse files
pinteriordiemol
andauthored
py: fix RelativeBy#near to take 2 parameters (#13082)
* add failing test to check RelativeLocator#near accept single int * fix s.w.support.RelativeBy#near to take 2 parameters * more strict typing on s.w.support.relative_locator * add some tests for s.w.support.relative_locator * remove test case calling RelativeLocator#near wrong way * fix linting issues --------- Co-authored-by: Diego Molina <[email protected]>
1 parent 9d6131f commit 9aa1a7f

File tree

3 files changed

+143
-12
lines changed

3 files changed

+143
-12
lines changed

py/selenium/webdriver/common/by.py

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
# under the License.
1717
"""The By implementation."""
1818

19+
from typing import Literal
20+
1921

2022
class By:
2123
"""Set of supported locator strategies."""
@@ -28,3 +30,6 @@ class By:
2830
TAG_NAME = "tag name"
2931
CLASS_NAME = "class name"
3032
CSS_SELECTOR = "css selector"
33+
34+
35+
ByType = Literal["id", "xpath", "link text", "partial link text", "name", "tag name", "class name", "css selector"]

py/selenium/webdriver/support/relative_locator.py

+50-12
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@
1616
# under the License.
1717
from typing import Dict
1818
from typing import List
19+
from typing import NoReturn
1920
from typing import Optional
2021
from typing import Union
22+
from typing import overload
2123

2224
from selenium.common.exceptions import WebDriverException
2325
from selenium.webdriver.common.by import By
26+
from selenium.webdriver.common.by import ByType
2427
from selenium.webdriver.remote.webelement import WebElement
2528

2629

@@ -37,10 +40,10 @@ def with_tag_name(tag_name: str) -> "RelativeBy":
3740
"""
3841
if not tag_name:
3942
raise WebDriverException("tag_name can not be null")
40-
return RelativeBy({"css selector": tag_name})
43+
return RelativeBy({By.CSS_SELECTOR: tag_name})
4144

4245

43-
def locate_with(by: By, using: str) -> "RelativeBy":
46+
def locate_with(by: ByType, using: str) -> "RelativeBy":
4447
"""Start searching for relative objects your search criteria with By.
4548
4649
:Args:
@@ -70,7 +73,9 @@ class RelativeBy:
7073
assert "mid" in ids
7174
"""
7275

73-
def __init__(self, root: Optional[Dict[Union[By, str], str]] = None, filters: Optional[List] = None):
76+
LocatorType = Dict[ByType, str]
77+
78+
def __init__(self, root: Optional[Dict[ByType, str]] = None, filters: Optional[List] = None):
7479
"""Creates a new RelativeBy object. It is preferred if you use the
7580
`locate_with` method as this signature could change.
7681
@@ -82,7 +87,13 @@ def __init__(self, root: Optional[Dict[Union[By, str], str]] = None, filters: Op
8287
self.root = root
8388
self.filters = filters or []
8489

85-
def above(self, element_or_locator: Union[WebElement, Dict] = None) -> "RelativeBy":
90+
@overload
91+
def above(self, element_or_locator: Union[WebElement, LocatorType]) -> "RelativeBy": ...
92+
93+
@overload
94+
def above(self, element_or_locator: None = None) -> "NoReturn": ...
95+
96+
def above(self, element_or_locator: Union[WebElement, LocatorType, None] = None) -> "RelativeBy":
8697
"""Add a filter to look for elements above.
8798
8899
:Args:
@@ -94,7 +105,13 @@ def above(self, element_or_locator: Union[WebElement, Dict] = None) -> "Relative
94105
self.filters.append({"kind": "above", "args": [element_or_locator]})
95106
return self
96107

97-
def below(self, element_or_locator: Union[WebElement, Dict] = None) -> "RelativeBy":
108+
@overload
109+
def below(self, element_or_locator: Union[WebElement, LocatorType]) -> "RelativeBy": ...
110+
111+
@overload
112+
def below(self, element_or_locator: None = None) -> "NoReturn": ...
113+
114+
def below(self, element_or_locator: Union[WebElement, Dict, None] = None) -> "RelativeBy":
98115
"""Add a filter to look for elements below.
99116
100117
:Args:
@@ -106,7 +123,13 @@ def below(self, element_or_locator: Union[WebElement, Dict] = None) -> "Relative
106123
self.filters.append({"kind": "below", "args": [element_or_locator]})
107124
return self
108125

109-
def to_left_of(self, element_or_locator: Union[WebElement, Dict] = None) -> "RelativeBy":
126+
@overload
127+
def to_left_of(self, element_or_locator: Union[WebElement, LocatorType]) -> "RelativeBy": ...
128+
129+
@overload
130+
def to_left_of(self, element_or_locator: None = None) -> "NoReturn": ...
131+
132+
def to_left_of(self, element_or_locator: Union[WebElement, Dict, None] = None) -> "RelativeBy":
110133
"""Add a filter to look for elements to the left of.
111134
112135
:Args:
@@ -118,7 +141,13 @@ def to_left_of(self, element_or_locator: Union[WebElement, Dict] = None) -> "Rel
118141
self.filters.append({"kind": "left", "args": [element_or_locator]})
119142
return self
120143

121-
def to_right_of(self, element_or_locator: Union[WebElement, Dict] = None) -> "RelativeBy":
144+
@overload
145+
def to_right_of(self, element_or_locator: Union[WebElement, LocatorType]) -> "RelativeBy": ...
146+
147+
@overload
148+
def to_right_of(self, element_or_locator: None = None) -> "NoReturn": ...
149+
150+
def to_right_of(self, element_or_locator: Union[WebElement, Dict, None] = None) -> "RelativeBy":
122151
"""Add a filter to look for elements right of.
123152
124153
:Args:
@@ -130,16 +159,25 @@ def to_right_of(self, element_or_locator: Union[WebElement, Dict] = None) -> "Re
130159
self.filters.append({"kind": "right", "args": [element_or_locator]})
131160
return self
132161

133-
def near(self, element_or_locator_distance: Union[WebElement, Dict, int] = None) -> "RelativeBy":
162+
@overload
163+
def near(self, element_or_locator: Union[WebElement, LocatorType], distance: int = 50) -> "RelativeBy": ...
164+
165+
@overload
166+
def near(self, element_or_locator: None = None, distance: int = 50) -> "NoReturn": ...
167+
168+
def near(self, element_or_locator: Union[WebElement, LocatorType, None] = None, distance: int = 50) -> "RelativeBy":
134169
"""Add a filter to look for elements near.
135170
136171
:Args:
137-
- element_or_locator_distance: Element to look near by the element or within a distance
172+
- element_or_locator: Element to look near by the element or within a distance
173+
- distance: distance in pixel
138174
"""
139-
if not element_or_locator_distance:
140-
raise WebDriverException("Element or locator or distance must be given when calling near method")
175+
if not element_or_locator:
176+
raise WebDriverException("Element or locator must be given when calling near method")
177+
if distance <= 0:
178+
raise WebDriverException("Distance must be positive")
141179

142-
self.filters.append({"kind": "near", "args": [element_or_locator_distance]})
180+
self.filters.append({"kind": "near", "args": [element_or_locator, distance]})
143181
return self
144182

145183
def to_dict(self) -> Dict:

py/test/selenium/webdriver/support/relative_by_tests.py

+88
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ def test_should_be_able_to_find_first_one(driver, pages):
3131
assert el.get_attribute("id") == "mid"
3232

3333

34+
def test_should_be_able_to_find_first_one_by_locator(driver, pages):
35+
pages.load("relative_locators.html")
36+
37+
el = driver.find_element(with_tag_name("p").above({By.ID: "below"}))
38+
39+
assert el.get_attribute("id") == "mid"
40+
41+
3442
def test_should_be_able_to_find_elements_above_another(driver, pages):
3543
pages.load("relative_locators.html")
3644
lowest = driver.find_element(By.ID, "below")
@@ -42,6 +50,16 @@ def test_should_be_able_to_find_elements_above_another(driver, pages):
4250
assert "mid" in ids
4351

4452

53+
def test_should_be_able_to_find_elements_above_another_by_locator(driver, pages):
54+
pages.load("relative_locators.html")
55+
56+
elements = driver.find_elements(with_tag_name("p").above({By.ID: "below"}))
57+
58+
ids = [el.get_attribute("id") for el in elements]
59+
assert "above" in ids
60+
assert "mid" in ids
61+
62+
4563
def test_should_be_able_to_combine_filters(driver, pages):
4664
pages.load("relative_locators.html")
4765

@@ -55,6 +73,15 @@ def test_should_be_able_to_combine_filters(driver, pages):
5573
assert "third" in ids
5674

5775

76+
def test_should_be_able_to_combine_filters_by_locator(driver, pages):
77+
pages.load("relative_locators.html")
78+
79+
elements = driver.find_elements(with_tag_name("td").above({By.ID: "center"}).to_right_of({By.ID: "second"}))
80+
81+
ids = [el.get_attribute("id") for el in elements]
82+
assert "third" in ids
83+
84+
5885
def test_should_be_able_to_use_css_selectors(driver, pages):
5986
pages.load("relative_locators.html")
6087

@@ -68,6 +95,17 @@ def test_should_be_able_to_use_css_selectors(driver, pages):
6895
assert "third" in ids
6996

7097

98+
def test_should_be_able_to_use_css_selectors_by_locator(driver, pages):
99+
pages.load("relative_locators.html")
100+
101+
elements = driver.find_elements(
102+
locate_with(By.CSS_SELECTOR, "td").above({By.ID: "center"}).to_right_of({By.ID: "second"})
103+
)
104+
105+
ids = [el.get_attribute("id") for el in elements]
106+
assert "third" in ids
107+
108+
71109
def test_should_be_able_to_use_xpath(driver, pages):
72110
pages.load("relative_locators.html")
73111

@@ -81,6 +119,15 @@ def test_should_be_able_to_use_xpath(driver, pages):
81119
assert "fourth" in ids
82120

83121

122+
def test_should_be_able_to_use_xpath_by_locator(driver, pages):
123+
pages.load("relative_locators.html")
124+
125+
elements = driver.find_elements(locate_with(By.XPATH, "//td[1]").below({By.ID: "second"}).above({By.ID: "seventh"}))
126+
127+
ids = [el.get_attribute("id") for el in elements]
128+
assert "fourth" in ids
129+
130+
84131
def test_no_such_element_is_raised_rather_than_index_error(driver, pages):
85132
pages.load("relative_locators.html")
86133
with pytest.raises(NoSuchElementException) as exc:
@@ -89,6 +136,13 @@ def test_no_such_element_is_raised_rather_than_index_error(driver, pages):
89136
assert "Cannot locate relative element with: {'id': 'nonexistentid'}" in exc.value.msg
90137

91138

139+
def test_no_such_element_is_raised_rather_than_index_error_by_locator(driver, pages):
140+
pages.load("relative_locators.html")
141+
with pytest.raises(NoSuchElementException) as exc:
142+
driver.find_element(locate_with(By.ID, "nonexistentid").above({By.ID: "second"}))
143+
assert "Cannot locate relative element with: {'id': 'nonexistentid'}" in exc.value.msg
144+
145+
92146
def test_near_locator_should_find_near_elements(driver, pages):
93147
pages.load("relative_locators.html")
94148
rect1 = driver.find_element(By.ID, "rect1")
@@ -98,6 +152,14 @@ def test_near_locator_should_find_near_elements(driver, pages):
98152
assert el.get_attribute("id") == "rect2"
99153

100154

155+
def test_near_locator_should_find_near_elements_by_locator(driver, pages):
156+
pages.load("relative_locators.html")
157+
158+
el = driver.find_element(locate_with(By.ID, "rect2").near({By.ID: "rect1"}))
159+
160+
assert el.get_attribute("id") == "rect2"
161+
162+
101163
def test_near_locator_should_not_find_far_elements(driver, pages):
102164
pages.load("relative_locators.html")
103165
rect3 = driver.find_element(By.ID, "rect3")
@@ -106,3 +168,29 @@ def test_near_locator_should_not_find_far_elements(driver, pages):
106168
driver.find_element(locate_with(By.ID, "rect4").near(rect3))
107169

108170
assert "Cannot locate relative element with: {'id': 'rect4'}" in exc.value.msg
171+
172+
173+
def test_near_locator_should_not_find_far_elements_by_locator(driver, pages):
174+
pages.load("relative_locators.html")
175+
176+
with pytest.raises(NoSuchElementException) as exc:
177+
driver.find_element(locate_with(By.ID, "rect4").near({By.ID: "rect3"}))
178+
179+
assert "Cannot locate relative element with: {'id': 'rect4'}" in exc.value.msg
180+
181+
182+
def test_near_locator_should_find_far_elements(driver, pages):
183+
pages.load("relative_locators.html")
184+
rect3 = driver.find_element(By.ID, "rect3")
185+
186+
el = driver.find_element(locate_with(By.ID, "rect4").near(rect3, 100))
187+
188+
assert el.get_attribute("id") == "rect4"
189+
190+
191+
def test_near_locator_should_find_far_elements_by_locator(driver, pages):
192+
pages.load("relative_locators.html")
193+
194+
el = driver.find_element(locate_with(By.ID, "rect4").near({By.ID: "rect3"}, 100))
195+
196+
assert el.get_attribute("id") == "rect4"

0 commit comments

Comments
 (0)