Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/py/selenium/webdriver/support/relative_locator.py
1864 views
1
# Licensed to the Software Freedom Conservancy (SFC) under one
2
# or more contributor license agreements. See the NOTICE file
3
# distributed with this work for additional information
4
# regarding copyright ownership. The SFC licenses this file
5
# to you under the Apache License, Version 2.0 (the
6
# "License"); you may not use this file except in compliance
7
# with the License. You may obtain a copy of the License at
8
#
9
# http://www.apache.org/licenses/LICENSE-2.0
10
#
11
# Unless required by applicable law or agreed to in writing,
12
# software distributed under the License is distributed on an
13
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
# KIND, either express or implied. See the License for the
15
# specific language governing permissions and limitations
16
# under the License.
17
import warnings
18
from typing import NoReturn, Optional, Union, overload
19
20
from selenium.common.exceptions import WebDriverException
21
from selenium.webdriver.common.by import By, ByType
22
from selenium.webdriver.remote.webelement import WebElement
23
24
25
def with_tag_name(tag_name: str) -> "RelativeBy":
26
"""Start searching for relative objects using a tag name.
27
28
Parameters:
29
-----------
30
tag_name : str
31
The DOM tag of element to start searching.
32
33
Returns:
34
--------
35
RelativeBy
36
Use this object to create filters within a `find_elements` call.
37
38
Raises:
39
-------
40
WebDriverException
41
If `tag_name` is None.
42
43
Notes:
44
------
45
- This method is deprecated and may be removed in future versions.
46
- Please use `locate_with` instead.
47
"""
48
warnings.warn("This method is deprecated and may be removed in future versions. Please use `locate_with` instead.")
49
if not tag_name:
50
raise WebDriverException("tag_name can not be null")
51
return RelativeBy({By.CSS_SELECTOR: tag_name})
52
53
54
def locate_with(by: ByType, using: str) -> "RelativeBy":
55
"""Start searching for relative objects your search criteria with By.
56
57
Parameters:
58
-----------
59
by : ByType
60
The method to find the element.
61
62
using : str
63
The value from `By` passed in.
64
65
Returns:
66
--------
67
RelativeBy
68
Use this object to create filters within a `find_elements` call.
69
70
Example:
71
--------
72
>>> lowest = driver.find_element(By.ID, "below")
73
>>> elements = driver.find_elements(locate_with(By.CSS_SELECTOR, "p").above(lowest))
74
"""
75
assert by is not None, "Please pass in a by argument"
76
assert using is not None, "Please pass in a using argument"
77
return RelativeBy({by: using})
78
79
80
class RelativeBy:
81
"""Gives the opportunity to find elements based on their relative location
82
on the page from a root element. It is recommended that you use the helper
83
function to create it.
84
85
Example:
86
--------
87
>>> lowest = driver.find_element(By.ID, "below")
88
>>> elements = driver.find_elements(locate_with(By.CSS_SELECTOR, "p").above(lowest))
89
>>> ids = [el.get_attribute("id") for el in elements]
90
>>> assert "above" in ids
91
>>> assert "mid" in ids
92
"""
93
94
LocatorType = dict[ByType, str]
95
96
def __init__(self, root: Optional[dict[ByType, str]] = None, filters: Optional[list] = None):
97
"""Creates a new RelativeBy object. It is preferred if you use the
98
`locate_with` method as this signature could change.
99
100
Attributes:
101
-----------
102
root : Dict[By, str]
103
- A dict with `By` enum as the key and the search query as the value
104
105
filters : List
106
- A list of the filters that will be searched. If none are passed
107
in please use the fluent API on the object to create the filters
108
"""
109
self.root = root
110
self.filters = filters or []
111
112
@overload
113
def above(self, element_or_locator: Union[WebElement, LocatorType]) -> "RelativeBy": ...
114
115
@overload
116
def above(self, element_or_locator: None = None) -> "NoReturn": ...
117
118
def above(self, element_or_locator: Union[WebElement, LocatorType, None] = None) -> "RelativeBy":
119
"""Add a filter to look for elements above.
120
121
Parameters:
122
-----------
123
element_or_locator : Union[WebElement, Dict, None]
124
Element to look above
125
126
Returns:
127
--------
128
RelativeBy
129
130
Raises:
131
-------
132
WebDriverException
133
If `element_or_locator` is None.
134
135
Example:
136
--------
137
>>> lowest = driver.find_element(By.ID, "below")
138
>>> elements = driver.find_elements(locate_with(By.CSS_SELECTOR, "p").above(lowest))
139
"""
140
if not element_or_locator:
141
raise WebDriverException("Element or locator must be given when calling above method")
142
143
self.filters.append({"kind": "above", "args": [element_or_locator]})
144
return self
145
146
@overload
147
def below(self, element_or_locator: Union[WebElement, LocatorType]) -> "RelativeBy": ...
148
149
@overload
150
def below(self, element_or_locator: None = None) -> "NoReturn": ...
151
152
def below(self, element_or_locator: Union[WebElement, dict, None] = None) -> "RelativeBy":
153
"""Add a filter to look for elements below.
154
155
Parameters:
156
-----------
157
element_or_locator : Union[WebElement, Dict, None]
158
Element to look below
159
160
Returns:
161
--------
162
RelativeBy
163
164
Raises:
165
-------
166
WebDriverException
167
If `element_or_locator` is None.
168
169
Example:
170
--------
171
>>> highest = driver.find_element(By.ID, "high")
172
>>> elements = driver.find_elements(locate_with(By.CSS_SELECTOR, "p").below(highest))
173
"""
174
if not element_or_locator:
175
raise WebDriverException("Element or locator must be given when calling below method")
176
177
self.filters.append({"kind": "below", "args": [element_or_locator]})
178
return self
179
180
@overload
181
def to_left_of(self, element_or_locator: Union[WebElement, LocatorType]) -> "RelativeBy": ...
182
183
@overload
184
def to_left_of(self, element_or_locator: None = None) -> "NoReturn": ...
185
186
def to_left_of(self, element_or_locator: Union[WebElement, dict, None] = None) -> "RelativeBy":
187
"""Add a filter to look for elements to the left of.
188
189
Parameters:
190
-----------
191
element_or_locator : Union[WebElement, Dict, None]
192
Element to look to the left of
193
194
Returns:
195
--------
196
RelativeBy
197
198
Raises:
199
-------
200
WebDriverException
201
If `element_or_locator` is None.
202
203
Example:
204
--------
205
>>> right = driver.find_element(By.ID, "right")
206
>>> elements = driver.find_elements(locate_with(By.CSS_SELECTOR, "p").to_left_of(right))
207
"""
208
if not element_or_locator:
209
raise WebDriverException("Element or locator must be given when calling to_left_of method")
210
211
self.filters.append({"kind": "left", "args": [element_or_locator]})
212
return self
213
214
@overload
215
def to_right_of(self, element_or_locator: Union[WebElement, LocatorType]) -> "RelativeBy": ...
216
217
@overload
218
def to_right_of(self, element_or_locator: None = None) -> "NoReturn": ...
219
220
def to_right_of(self, element_or_locator: Union[WebElement, dict, None] = None) -> "RelativeBy":
221
"""Add a filter to look for elements right of.
222
223
Parameters:
224
-----------
225
element_or_locator : Union[WebElement, Dict, None]
226
Element to look right of
227
228
Returns:
229
--------
230
RelativeBy
231
232
Raises:
233
-------
234
WebDriverException
235
If `element_or_locator` is None.
236
237
Example:
238
--------
239
>>> left = driver.find_element(By.ID, "left")
240
>>> elements = driver.find_elements(locate_with(By.CSS_SELECTOR, "p").to_right_of(left))
241
"""
242
if not element_or_locator:
243
raise WebDriverException("Element or locator must be given when calling to_right_of method")
244
245
self.filters.append({"kind": "right", "args": [element_or_locator]})
246
return self
247
248
@overload
249
def straight_above(self, element_or_locator: Union[WebElement, LocatorType]) -> "RelativeBy": ...
250
251
@overload
252
def straight_above(self, element_or_locator: None = None) -> "NoReturn": ...
253
254
def straight_above(self, element_or_locator: Union[WebElement, LocatorType, None] = None) -> "RelativeBy":
255
"""Add a filter to look for elements above.
256
257
:Args:
258
- element_or_locator: Element to look above
259
"""
260
if not element_or_locator:
261
raise WebDriverException("Element or locator must be given when calling above method")
262
263
self.filters.append({"kind": "straightAbove", "args": [element_or_locator]})
264
return self
265
266
@overload
267
def straight_below(self, element_or_locator: Union[WebElement, LocatorType]) -> "RelativeBy": ...
268
269
@overload
270
def straight_below(self, element_or_locator: None = None) -> "NoReturn": ...
271
272
def straight_below(self, element_or_locator: Union[WebElement, dict, None] = None) -> "RelativeBy":
273
"""Add a filter to look for elements below.
274
275
:Args:
276
- element_or_locator: Element to look below
277
"""
278
if not element_or_locator:
279
raise WebDriverException("Element or locator must be given when calling below method")
280
281
self.filters.append({"kind": "straightBelow", "args": [element_or_locator]})
282
return self
283
284
@overload
285
def straight_left_of(self, element_or_locator: Union[WebElement, LocatorType]) -> "RelativeBy": ...
286
287
@overload
288
def straight_left_of(self, element_or_locator: None = None) -> "NoReturn": ...
289
290
def straight_left_of(self, element_or_locator: Union[WebElement, dict, None] = None) -> "RelativeBy":
291
"""Add a filter to look for elements to the left of.
292
293
:Args:
294
- element_or_locator: Element to look to the left of
295
"""
296
if not element_or_locator:
297
raise WebDriverException("Element or locator must be given when calling to_left_of method")
298
299
self.filters.append({"kind": "straightLeft", "args": [element_or_locator]})
300
return self
301
302
@overload
303
def straight_right_of(self, element_or_locator: Union[WebElement, LocatorType]) -> "RelativeBy": ...
304
305
@overload
306
def straight_right_of(self, element_or_locator: None = None) -> "NoReturn": ...
307
308
def straight_right_of(self, element_or_locator: Union[WebElement, dict, None] = None) -> "RelativeBy":
309
"""Add a filter to look for elements right of.
310
311
:Args:
312
- element_or_locator: Element to look right of
313
"""
314
if not element_or_locator:
315
raise WebDriverException("Element or locator must be given when calling to_right_of method")
316
317
self.filters.append({"kind": "straightRight", "args": [element_or_locator]})
318
return self
319
320
@overload
321
def near(self, element_or_locator: Union[WebElement, LocatorType], distance: int = 50) -> "RelativeBy": ...
322
323
@overload
324
def near(self, element_or_locator: None = None, distance: int = 50) -> "NoReturn": ...
325
326
def near(self, element_or_locator: Union[WebElement, LocatorType, None] = None, distance: int = 50) -> "RelativeBy":
327
"""Add a filter to look for elements near.
328
329
Parameters:
330
-----------
331
element_or_locator : Union[WebElement, Dict, None]
332
Element to look near by the element or within a distance
333
334
distance : int
335
Distance in pixel
336
337
Returns:
338
--------
339
RelativeBy
340
341
Raises:
342
-------
343
WebDriverException
344
- If `element_or_locator` is None
345
- If `distance` is less than or equal to 0.
346
347
Example:
348
--------
349
>>> near = driver.find_element(By.ID, "near")
350
>>> elements = driver.find_elements(locate_with(By.CSS_SELECTOR, "p").near(near, 50))
351
"""
352
if not element_or_locator:
353
raise WebDriverException("Element or locator must be given when calling near method")
354
if distance <= 0:
355
raise WebDriverException("Distance must be positive")
356
357
self.filters.append({"kind": "near", "args": [element_or_locator, distance]})
358
return self
359
360
def to_dict(self) -> dict:
361
"""Create a dict that will be passed to the driver to start searching
362
for the element."""
363
return {
364
"relative": {
365
"root": self.root,
366
"filters": self.filters,
367
}
368
}
369
370