Source code for selenium.webdriver.support.relative_locator

# Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The SFC licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
import warnings
from typing import NoReturn, overload

from selenium.common.exceptions import WebDriverException
from selenium.webdriver.common.by import By, ByType
from selenium.webdriver.remote.webelement import WebElement


[docs] def with_tag_name(tag_name: str) -> "RelativeBy": """Start searching for relative objects using a tag name. Args: tag_name: The DOM tag of element to start searching. Returns: RelativeBy: Use this object to create filters within a `find_elements` call. Raises: WebDriverException: If `tag_name` is None. Note: This method is deprecated and may be removed in future versions. Please use `locate_with` instead. """ warnings.warn("This method is deprecated and may be removed in future versions. Please use `locate_with` instead.") if not tag_name: raise WebDriverException("tag_name can not be null") return RelativeBy({By.CSS_SELECTOR: tag_name})
[docs] def locate_with(by: ByType, using: str) -> "RelativeBy": """Start searching for relative objects your search criteria with By. Args: by: The method to find the element. using: The value from `By` passed in. Returns: RelativeBy: Use this object to create filters within a `find_elements` call. Example: >>> lowest = driver.find_element(By.ID, "below") >>> elements = driver.find_elements(locate_with(By.CSS_SELECTOR, "p").above(lowest)) """ assert by is not None, "Please pass in a by argument" assert using is not None, "Please pass in a using argument" return RelativeBy({by: using})
[docs] class RelativeBy: """Find elements based on their relative location from a root element. It is recommended that you use the helper function to create instances. Example: -------- >>> lowest = driver.find_element(By.ID, "below") >>> elements = driver.find_elements(locate_with(By.CSS_SELECTOR, "p").above(lowest)) >>> ids = [el.get_attribute("id") for el in elements] >>> assert "above" in ids >>> assert "mid" in ids """ LocatorType = dict[ByType, str] def __init__(self, root: dict[ByType, str] | None = None, filters: list | None = None): """Create a RelativeBy object (prefer using `locate_with` instead). Args: root: A dict with `By` enum as the key and the search query as the value filters: A list of the filters that will be searched. If none are passed in please use the fluent API on the object to create the filters """ self.root = root self.filters = filters or [] @overload def above(self, element_or_locator: WebElement | LocatorType) -> "RelativeBy": ... @overload def above(self, element_or_locator: None = None) -> "NoReturn": ...
[docs] def above(self, element_or_locator: WebElement | LocatorType | None = None) -> "RelativeBy": """Add a filter to look for elements above. Args: element_or_locator: Element to look above Returns: RelativeBy Raises: WebDriverException: If `element_or_locator` is None. Example: -------- >>> lowest = driver.find_element(By.ID, "below") >>> elements = driver.find_elements(locate_with(By.CSS_SELECTOR, "p").above(lowest)) """ if not element_or_locator: raise WebDriverException("Element or locator must be given when calling above method") self.filters.append({"kind": "above", "args": [element_or_locator]}) return self
@overload def below(self, element_or_locator: WebElement | LocatorType) -> "RelativeBy": ... @overload def below(self, element_or_locator: None = None) -> "NoReturn": ...
[docs] def below(self, element_or_locator: WebElement | dict | None = None) -> "RelativeBy": """Add a filter to look for elements below. Args: element_or_locator: Element to look below Returns: RelativeBy Raises: WebDriverException: If `element_or_locator` is None. Example: >>> highest = driver.find_element(By.ID, "high") >>> elements = driver.find_elements(locate_with(By.CSS_SELECTOR, "p").below(highest)) """ if not element_or_locator: raise WebDriverException("Element or locator must be given when calling below method") self.filters.append({"kind": "below", "args": [element_or_locator]}) return self
@overload def to_left_of(self, element_or_locator: WebElement | LocatorType) -> "RelativeBy": ... @overload def to_left_of(self, element_or_locator: None = None) -> "NoReturn": ...
[docs] def to_left_of(self, element_or_locator: WebElement | dict | None = None) -> "RelativeBy": """Add a filter to look for elements to the left of. Args: element_or_locator: Element to look to the left of Returns: RelativeBy Raises: WebDriverException: If `element_or_locator` is None. Example: >>> right = driver.find_element(By.ID, "right") >>> elements = driver.find_elements(locate_with(By.CSS_SELECTOR, "p").to_left_of(right)) """ if not element_or_locator: raise WebDriverException("Element or locator must be given when calling to_left_of method") self.filters.append({"kind": "left", "args": [element_or_locator]}) return self
@overload def to_right_of(self, element_or_locator: WebElement | LocatorType) -> "RelativeBy": ... @overload def to_right_of(self, element_or_locator: None = None) -> "NoReturn": ...
[docs] def to_right_of(self, element_or_locator: WebElement | dict | None = None) -> "RelativeBy": """Add a filter to look for elements right of. Args: element_or_locator: Element to look right of Returns: RelativeBy Raises: WebDriverException: If `element_or_locator` is None. Example: >>> left = driver.find_element(By.ID, "left") >>> elements = driver.find_elements(locate_with(By.CSS_SELECTOR, "p").to_right_of(left)) """ if not element_or_locator: raise WebDriverException("Element or locator must be given when calling to_right_of method") self.filters.append({"kind": "right", "args": [element_or_locator]}) return self
@overload def straight_above(self, element_or_locator: WebElement | LocatorType) -> "RelativeBy": ... @overload def straight_above(self, element_or_locator: None = None) -> "NoReturn": ...
[docs] def straight_above(self, element_or_locator: WebElement | LocatorType | None = None) -> "RelativeBy": """Add a filter to look for elements above. Args: element_or_locator: Element to look above """ if not element_or_locator: raise WebDriverException("Element or locator must be given when calling above method") self.filters.append({"kind": "straightAbove", "args": [element_or_locator]}) return self
@overload def straight_below(self, element_or_locator: WebElement | LocatorType) -> "RelativeBy": ... @overload def straight_below(self, element_or_locator: None = None) -> "NoReturn": ...
[docs] def straight_below(self, element_or_locator: WebElement | dict | None = None) -> "RelativeBy": """Add a filter to look for elements below. Args: element_or_locator: Element to look below """ if not element_or_locator: raise WebDriverException("Element or locator must be given when calling below method") self.filters.append({"kind": "straightBelow", "args": [element_or_locator]}) return self
@overload def straight_left_of(self, element_or_locator: WebElement | LocatorType) -> "RelativeBy": ... @overload def straight_left_of(self, element_or_locator: None = None) -> "NoReturn": ...
[docs] def straight_left_of(self, element_or_locator: WebElement | dict | None = None) -> "RelativeBy": """Add a filter to look for elements to the left of. Args: element_or_locator: Element to look to the left of """ if not element_or_locator: raise WebDriverException("Element or locator must be given when calling to_left_of method") self.filters.append({"kind": "straightLeft", "args": [element_or_locator]}) return self
@overload def straight_right_of(self, element_or_locator: WebElement | LocatorType) -> "RelativeBy": ... @overload def straight_right_of(self, element_or_locator: None = None) -> "NoReturn": ...
[docs] def straight_right_of(self, element_or_locator: WebElement | dict | None = None) -> "RelativeBy": """Add a filter to look for elements right of. Args: element_or_locator: Element to look right of """ if not element_or_locator: raise WebDriverException("Element or locator must be given when calling to_right_of method") self.filters.append({"kind": "straightRight", "args": [element_or_locator]}) return self
@overload def near(self, element_or_locator: WebElement | LocatorType, distance: int = 50) -> "RelativeBy": ... @overload def near(self, element_or_locator: None = None, distance: int = 50) -> "NoReturn": ...
[docs] def near(self, element_or_locator: WebElement | LocatorType | None = None, distance: int = 50) -> "RelativeBy": """Add a filter to look for elements near. Args: element_or_locator: Element to look near by the element or within a distance distance: Distance in pixel Returns: RelativeBy Raises: WebDriverException: If `element_or_locator` is None WebDriverException: If `distance` is less than or equal to 0. Example: >>> near = driver.find_element(By.ID, "near") >>> elements = driver.find_elements(locate_with(By.CSS_SELECTOR, "p").near(near, 50)) """ if not element_or_locator: raise WebDriverException("Element or locator must be given when calling near method") if distance <= 0: raise WebDriverException("Distance must be positive") self.filters.append({"kind": "near", "args": [element_or_locator, distance]}) return self
[docs] def to_dict(self) -> dict: """Create a dict to be passed to the driver for element searching.""" return { "relative": { "root": self.root, "filters": self.filters, } }