From 6b6801ce223f9a49bd0afdafb7a852a894d835b7 Mon Sep 17 00:00:00 2001 From: Simon Benzer Date: Thu, 9 Jan 2025 15:09:23 -0500 Subject: [PATCH 1/5] Upgraded WebElement Docstrings --- py/selenium/webdriver/remote/webelement.py | 316 ++++++++++++++++----- 1 file changed, 245 insertions(+), 71 deletions(-) diff --git a/py/selenium/webdriver/remote/webelement.py b/py/selenium/webdriver/remote/webelement.py index f2b672ec17736..016d2a0de6f9d 100644 --- a/py/selenium/webdriver/remote/webelement.py +++ b/py/selenium/webdriver/remote/webelement.py @@ -81,20 +81,51 @@ def __repr__(self): @property def tag_name(self) -> str: - """This element's ``tagName`` property.""" + """This element's ``tagName`` property. + + Returns: + -------- + str : The tag name of the element. + + Example: + -------- + >>> element = driver.find_element(By.ID, 'foo') + """ return self._execute(Command.GET_ELEMENT_TAG_NAME)["value"] @property def text(self) -> str: - """The text of the element.""" + """The text of the element. + + Returns: + -------- + str : The text of the element. + + Example: + -------- + >>> element = driver.find_element(By.ID, 'foo') + >>> print(element.text) + """ return self._execute(Command.GET_ELEMENT_TEXT)["value"] def click(self) -> None: - """Clicks the element.""" + """Clicks the element. + + Example: + -------- + >>> element = driver.find_element(By.ID, 'foo') + >>> element.click() + """ self._execute(Command.CLICK_ELEMENT) def submit(self) -> None: - """Submits a form.""" + """Submits a form. + + Example: + -------- + >>> form = driver.find_element(By.NAME, 'login') + >>> form.submit() + """ script = ( "/* submitForm */var form = arguments[0];\n" 'while (form.nodeName != "FORM" && form.parentNode) {\n' @@ -113,19 +144,30 @@ def submit(self) -> None: raise WebDriverException("To submit an element, it must be nested inside a form element") from exc def clear(self) -> None: - """Clears the text if it's a text entry element.""" + """Clears the text if it's a text entry element. + + Example: + -------- + >>> text_field = driver.find_element(By.NAME, 'username') + >>> text_field.clear() + """ self._execute(Command.CLEAR_ELEMENT) def get_property(self, name) -> str | bool | WebElement | dict: """Gets the given property of the element. - :Args: - - name - Name of the property to retrieve. + Parameters: + ---------- + name : str + - Name of the property to retrieve. - :Usage: - :: + Returns: + ------- + str | bool | WebElement | dict : The value of the property. - text_length = target_element.get_property("text_length") + Example: + ------- + >>> text_length = target_element.get_property("text_length") """ try: return self._execute(Command.GET_ELEMENT_PROPERTY, {"name": name})["value"] @@ -138,13 +180,18 @@ def get_dom_attribute(self, name) -> str: :func:`~selenium.webdriver.remote.BaseWebElement.get_attribute`, this method only returns attributes declared in the element's HTML markup. - :Args: - - name - Name of the attribute to retrieve. - - :Usage: - :: + Parameters: + ---------- + name : str + - Name of the attribute to retrieve. + + Returns: + ------- + str : The value of the attribute. - text_length = target_element.get_dom_attribute("class") + Example: + ------- + >>> text_length = target_element.get_dom_attribute("class") """ return self._execute(Command.GET_ELEMENT_ATTRIBUTE, {"name": name})["value"] @@ -165,13 +212,19 @@ def get_attribute(self, name) -> str | None: use :func:`~selenium.webdriver.remote.BaseWebElement.get_dom_attribute` or :func:`~selenium.webdriver.remote.BaseWebElement.get_property` methods respectively. - :Args: - - name - Name of the attribute/property to retrieve. + Parameters: + ---------- + name : str + - Name of the attribute/property to retrieve. - Example:: + Returns: + ------- + str | bool | None : The value of the attribute/property. - # Check if the "active" CSS class is applied to an element. - is_active = "active" in target_element.get_attribute("class") + Example: + -------- + >>> # Check if the "active" CSS class is applied to an element. + >>> is_active = "active" in target_element.get_attribute("class") """ if getAttribute_js is None: _load_js() @@ -183,35 +236,52 @@ def get_attribute(self, name) -> str | None: def is_selected(self) -> bool: """Returns whether the element is selected. - Can be used to check if a checkbox or radio button is selected. + Example: + -------- + >>> is_selected = element.is_selected() + + Notes: + ------ + - This method is generally used on checkboxes, options in a select + and radio buttons. """ return self._execute(Command.IS_ELEMENT_SELECTED)["value"] def is_enabled(self) -> bool: - """Returns whether the element is enabled.""" + """Returns whether the element is enabled. + + Example: + -------- + >>> is_enabled = element.is_enabled() + """ return self._execute(Command.IS_ELEMENT_ENABLED)["value"] def send_keys(self, *value: str) -> None: """Simulates typing into the element. - :Args: - - value - A string for typing, or setting form fields. For setting - file inputs, this could be a local file path. - - Use this to send simple key events or to fill out form fields:: - - form_textfield = driver.find_element(By.NAME, 'username') - form_textfield.send_keys("admin") - - This can also be used to set file inputs. + Parameters: + ---------- + value : str + - A string for typing, or setting form fields. For setting + file inputs, this could be a local file path. - :: + Notes: + ------ + - Use this to send simple key events or to fill out form fields + - This can also be used to set file inputs. - file_input = driver.find_element(By.NAME, 'profilePic') - file_input.send_keys("path/to/profilepic.gif") - # Generally it's better to wrap the file path in one of the methods - # in os.path to return the actual path to support cross OS testing. - # file_input.send_keys(os.path.abspath("path/to/profilepic.gif")) + Examples: + -------- + To send a simple key event:: + >>> form_textfield = driver.find_element(By.NAME, 'username') + >>> form_textfield.send_keys("admin") + + or to set a file input field:: + >>> file_input = driver.find_element(By.NAME, 'profilePic') + >>> file_input.send_keys("path/to/profilepic.gif") + >>> # Generally it's better to wrap the file path in one of the methods + >>> # in os.path to return the actual path to support cross OS testing. + >>> # file_input.send_keys(os.path.abspath("path/to/profilepic.gif")) """ # transfer file to another machine only if remote driver is used # the same behaviour as for java binding @@ -237,15 +307,31 @@ def shadow_root(self) -> ShadowRoot: """Returns a shadow root of the element if there is one or an error. Only works from Chromium 96, Firefox 96, and Safari 16.4 onwards. - :Returns: - - ShadowRoot object or - - NoSuchShadowRoot - if no shadow root was attached to element + Returns: + ------- + ShadowRoot : object + + Raises: + ------- + NoSuchShadowRoot - if no shadow root was attached to element + + Example: + -------- + >>> try: + ... shadow_root = element.shadow_root + >>> except NoSuchShadowRoot: + ... print("No shadow root attached to element") """ return self._execute(Command.GET_SHADOW_ROOT)["value"] # RenderedWebElement Items def is_displayed(self) -> bool: - """Whether the element is visible to a user.""" + """Whether the element is visible to a user. + + Example: + -------- + >>> is_displayed = element.is_displayed() + """ # Only go into this conditional for browsers that don't use the atom themselves if isDisplayed_js is None: _load_js() @@ -253,12 +339,19 @@ def is_displayed(self) -> bool: @property def location_once_scrolled_into_view(self) -> dict: - """THIS PROPERTY MAY CHANGE WITHOUT WARNING. Use this to discover where + """THIS PROPERTY MAY CHANGE WITHOUT WARNING. + Use this to discover where on the screen an element is so that we can click it. This method should cause the element to be scrolled into view. - Returns the top lefthand corner location on the screen, or zero - coordinates if the element is not visible. + Returns: + -------- + dict: the top lefthand corner location on the screen, or zero + coordinates if the element is not visible. + + Example: + -------- + >>> loc = element.location_once_scrolled_into_view """ old_loc = self._execute( Command.W3C_EXECUTE_SCRIPT, @@ -271,35 +364,93 @@ def location_once_scrolled_into_view(self) -> dict: @property def size(self) -> dict: - """The size of the element.""" + """The size of the element. + + Returns: + -------- + dict: The width and height of the element. + + Example: + -------- + >>> size = element.size + """ size = self._execute(Command.GET_ELEMENT_RECT)["value"] new_size = {"height": size["height"], "width": size["width"]} return new_size def value_of_css_property(self, property_name) -> str: - """The value of a CSS property.""" + """The value of a CSS property. + + Parameters: + ---------- + property_name : str + - The name of the CSS property to get the value of. + + Returns: + -------- + str : The value of the CSS property. + + Example: + -------- + >>> value = element.value_of_css_property('color') + """ return self._execute(Command.GET_ELEMENT_VALUE_OF_CSS_PROPERTY, {"propertyName": property_name})["value"] @property def location(self) -> dict: - """The location of the element in the renderable canvas.""" + """The location of the element in the renderable canvas. + + Returns: + -------- + dict: The x and y coordinates of the element. + + Example: + -------- + >>> loc = element.location + """ old_loc = self._execute(Command.GET_ELEMENT_RECT)["value"] new_loc = {"x": round(old_loc["x"]), "y": round(old_loc["y"])} return new_loc @property def rect(self) -> dict: - """A dictionary with the size and location of the element.""" + """A dictionary with the size and location of the element. + + Returns: + -------- + dict: The size and location of the element. + + Example: + -------- + >>> rect = element.rect + """ return self._execute(Command.GET_ELEMENT_RECT)["value"] @property def aria_role(self) -> str: - """Returns the ARIA role of the current web element.""" + """Returns the ARIA role of the current web element. + + Returns: + -------- + str : The ARIA role of the element. + + Example: + -------- + >>> role = element.aria_role""" return self._execute(Command.GET_ELEMENT_ARIA_ROLE)["value"] @property def accessible_name(self) -> str: - """Returns the ARIA Level of the current webelement.""" + """Returns the ARIA Level of the current webelement. + + Returns: + -------- + str : The ARIA Level of the element. + + Example: + -------- + >>> name = element.accessible_name + """ return self._execute(Command.GET_ELEMENT_ARIA_LABEL)["value"] @property @@ -307,10 +458,13 @@ def screenshot_as_base64(self) -> str: """Gets the screenshot of the current element as a base64 encoded string. - :Usage: - :: + Returns: + -------- + str : The screenshot of the element as a base64 encoded string. - img_b64 = element.screenshot_as_base64 + Example: + -------- + >>> img_b64 = element.screenshot_as_base64 """ return self._execute(Command.ELEMENT_SCREENSHOT)["value"] @@ -318,10 +472,13 @@ def screenshot_as_base64(self) -> str: def screenshot_as_png(self) -> bytes: """Gets the screenshot of the current element as a binary data. - :Usage: - :: + Returns: + -------- + bytes : The screenshot of the element as binary data. - element_png = element.screenshot_as_png + Example: + -------- + >>> element_png = element.screenshot_as_png """ return b64decode(self.screenshot_as_base64.encode("ascii")) @@ -330,14 +487,19 @@ def screenshot(self, filename) -> bool: Returns False if there is any IOError, else returns True. Use full paths in your filename. - :Args: - - filename: The full path you wish to save your screenshot to. This - should end with a `.png` extension. + Returns: + -------- + bool : True if the screenshot was saved successfully, False otherwise. - :Usage: - :: + Parameters: + ---------- + filename : str + The full path you wish to save your screenshot to. This + should end with a `.png` extension. - element.screenshot('/Screenshots/foo.png') + Element: + -------- + >>> element.screenshot('/Screenshots/foo.png') """ if not filename.lower().endswith(".png"): warnings.warn( @@ -357,7 +519,13 @@ def screenshot(self, filename) -> bool: @property def parent(self): """Internal reference to the WebDriver instance this element was found - from.""" + from. + + Example: + -------- + >>> element = driver.find_element(By.ID, 'foo') + >>> parent_element = element.parent + """ return self._parent @property @@ -367,8 +535,10 @@ def id(self) -> str: This is mainly for internal use. Simple use cases such as checking if 2 webelements refer to the same element, can be done using ``==``:: - if element1 == element2: - print("These 2 are equal") + Example: + -------- + >>> if element1 == element2: + ... print("These 2 are equal") """ return self._id @@ -382,11 +552,15 @@ def __ne__(self, element): def _execute(self, command, params=None): """Executes a command against the underlying HTML element. - Args: - command: The name of the command to _execute as a string. - params: A dictionary of named parameters to send with the command. + Parameters: + ---------- + command : any + The name of the command to _execute as a string. + params : dict + A dictionary of named parameters to send with the command. Returns: + ------- The command's JSON response loaded into a dictionary object. """ if not params: @@ -442,7 +616,7 @@ def find_elements(self, by=By.ID, value=None) -> List[WebElement]: Example: -------- - element = driver.find_element(By.ID, 'foo') + >>> element = driver.find_element(By.ID, 'foo') Returns: ------- From 7c9043b9da7831b70d313ee1780daa43b3f02a38 Mon Sep 17 00:00:00 2001 From: Simon Benzer Date: Sun, 12 Jan 2025 13:00:46 -0500 Subject: [PATCH 2/5] format.sh --- py/selenium/webdriver/remote/webelement.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/py/selenium/webdriver/remote/webelement.py b/py/selenium/webdriver/remote/webelement.py index 016d2a0de6f9d..f3073cf80bb0a 100644 --- a/py/selenium/webdriver/remote/webelement.py +++ b/py/selenium/webdriver/remote/webelement.py @@ -82,7 +82,7 @@ def __repr__(self): @property def tag_name(self) -> str: """This element's ``tagName`` property. - + Returns: -------- str : The tag name of the element. @@ -110,7 +110,7 @@ def text(self) -> str: def click(self) -> None: """Clicks the element. - + Example: -------- >>> element = driver.find_element(By.ID, 'foo') @@ -120,7 +120,7 @@ def click(self) -> None: def submit(self) -> None: """Submits a form. - + Example: -------- >>> form = driver.find_element(By.NAME, 'login') @@ -145,7 +145,7 @@ def submit(self) -> None: def clear(self) -> None: """Clears the text if it's a text entry element. - + Example: -------- >>> text_field = driver.find_element(By.NAME, 'username') @@ -249,7 +249,7 @@ def is_selected(self) -> bool: def is_enabled(self) -> bool: """Returns whether the element is enabled. - + Example: -------- >>> is_enabled = element.is_enabled() @@ -327,7 +327,7 @@ def shadow_root(self) -> ShadowRoot: # RenderedWebElement Items def is_displayed(self) -> bool: """Whether the element is visible to a user. - + Example: -------- >>> is_displayed = element.is_displayed() @@ -339,8 +339,7 @@ def is_displayed(self) -> bool: @property def location_once_scrolled_into_view(self) -> dict: - """THIS PROPERTY MAY CHANGE WITHOUT WARNING. - Use this to discover where + """THIS PROPERTY MAY CHANGE WITHOUT WARNING. Use this to discover where on the screen an element is so that we can click it. This method should cause the element to be scrolled into view. @@ -436,7 +435,8 @@ def aria_role(self) -> str: Example: -------- - >>> role = element.aria_role""" + >>> role = element.aria_role + """ return self._execute(Command.GET_ELEMENT_ARIA_ROLE)["value"] @property @@ -520,7 +520,7 @@ def screenshot(self, filename) -> bool: def parent(self): """Internal reference to the WebDriver instance this element was found from. - + Example: -------- >>> element = driver.find_element(By.ID, 'foo') From b0baa9943c97688082d9d63f3148b1da3090b5bf Mon Sep 17 00:00:00 2001 From: Simon Benzer Date: Sun, 12 Jan 2025 14:55:54 -0500 Subject: [PATCH 3/5] format.sh --- py/selenium/webdriver/remote/webelement.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/py/selenium/webdriver/remote/webelement.py b/py/selenium/webdriver/remote/webelement.py index f3073cf80bb0a..789c5954e89e4 100644 --- a/py/selenium/webdriver/remote/webelement.py +++ b/py/selenium/webdriver/remote/webelement.py @@ -86,7 +86,7 @@ def tag_name(self) -> str: Returns: -------- str : The tag name of the element. - + Example: -------- >>> element = driver.find_element(By.ID, 'foo') @@ -100,7 +100,7 @@ def text(self) -> str: Returns: -------- str : The text of the element. - + Example: -------- >>> element = driver.find_element(By.ID, 'foo') @@ -184,7 +184,7 @@ def get_dom_attribute(self, name) -> str: ---------- name : str - Name of the attribute to retrieve. - + Returns: ------- str : The value of the attribute. @@ -347,7 +347,7 @@ def location_once_scrolled_into_view(self) -> dict: -------- dict: the top lefthand corner location on the screen, or zero coordinates if the element is not visible. - + Example: -------- >>> loc = element.location_once_scrolled_into_view @@ -368,7 +368,7 @@ def size(self) -> dict: Returns: -------- dict: The width and height of the element. - + Example: -------- >>> size = element.size @@ -388,7 +388,7 @@ def value_of_css_property(self, property_name) -> str: Returns: -------- str : The value of the CSS property. - + Example: -------- >>> value = element.value_of_css_property('color') @@ -402,7 +402,7 @@ def location(self) -> dict: Returns: -------- dict: The x and y coordinates of the element. - + Example: -------- >>> loc = element.location @@ -418,7 +418,7 @@ def rect(self) -> dict: Returns: -------- dict: The size and location of the element. - + Example: -------- >>> rect = element.rect @@ -432,7 +432,7 @@ def aria_role(self) -> str: Returns: -------- str : The ARIA role of the element. - + Example: -------- >>> role = element.aria_role @@ -446,7 +446,7 @@ def accessible_name(self) -> str: Returns: -------- str : The ARIA Level of the element. - + Example: -------- >>> name = element.accessible_name @@ -494,7 +494,7 @@ def screenshot(self, filename) -> bool: Parameters: ---------- filename : str - The full path you wish to save your screenshot to. This + The full path you wish to save your screenshot to. This should end with a `.png` extension. Element: From bf53277489f48cc8073ef8e2442d517a537fe9c6 Mon Sep 17 00:00:00 2001 From: Simon Benzer <69980130+shbenzer@users.noreply.github.com> Date: Mon, 13 Jan 2025 08:54:30 -0500 Subject: [PATCH 4/5] Change Parameters to Attributes --- py/selenium/webdriver/remote/webelement.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/py/selenium/webdriver/remote/webelement.py b/py/selenium/webdriver/remote/webelement.py index 789c5954e89e4..c57959a12543c 100644 --- a/py/selenium/webdriver/remote/webelement.py +++ b/py/selenium/webdriver/remote/webelement.py @@ -156,7 +156,7 @@ def clear(self) -> None: def get_property(self, name) -> str | bool | WebElement | dict: """Gets the given property of the element. - Parameters: + Attributes: ---------- name : str - Name of the property to retrieve. @@ -180,7 +180,7 @@ def get_dom_attribute(self, name) -> str: :func:`~selenium.webdriver.remote.BaseWebElement.get_attribute`, this method only returns attributes declared in the element's HTML markup. - Parameters: + Attributes: ---------- name : str - Name of the attribute to retrieve. @@ -212,7 +212,7 @@ def get_attribute(self, name) -> str | None: use :func:`~selenium.webdriver.remote.BaseWebElement.get_dom_attribute` or :func:`~selenium.webdriver.remote.BaseWebElement.get_property` methods respectively. - Parameters: + Attributes: ---------- name : str - Name of the attribute/property to retrieve. @@ -259,7 +259,7 @@ def is_enabled(self) -> bool: def send_keys(self, *value: str) -> None: """Simulates typing into the element. - Parameters: + Attributes: ---------- value : str - A string for typing, or setting form fields. For setting @@ -380,7 +380,7 @@ def size(self) -> dict: def value_of_css_property(self, property_name) -> str: """The value of a CSS property. - Parameters: + Attributes: ---------- property_name : str - The name of the CSS property to get the value of. @@ -491,7 +491,7 @@ def screenshot(self, filename) -> bool: -------- bool : True if the screenshot was saved successfully, False otherwise. - Parameters: + Attributes: ---------- filename : str The full path you wish to save your screenshot to. This @@ -552,12 +552,12 @@ def __ne__(self, element): def _execute(self, command, params=None): """Executes a command against the underlying HTML element. - Parameters: + Attributes: ---------- command : any The name of the command to _execute as a string. params : dict - A dictionary of named parameters to send with the command. + A dictionary of named Attributes to send with the command. Returns: ------- @@ -571,7 +571,7 @@ def _execute(self, command, params=None): def find_element(self, by=By.ID, value=None) -> WebElement: """Find an element given a By strategy and locator. - Parameters: + Attributes: ---------- by : selenium.webdriver.common.by.By The locating strategy to use. Default is `By.ID`. Supported values include: @@ -600,7 +600,7 @@ def find_element(self, by=By.ID, value=None) -> WebElement: def find_elements(self, by=By.ID, value=None) -> List[WebElement]: """Find elements given a By strategy and locator. - Parameters: + Attributes: ---------- by : selenium.webdriver.common.by.By The locating strategy to use. Default is `By.ID`. Supported values include: From f5d65c2a7e7285ed45c49a591dfdc0a5300f9fc5 Mon Sep 17 00:00:00 2001 From: Simon Benzer <69980130+shbenzer@users.noreply.github.com> Date: Mon, 13 Jan 2025 09:02:32 -0500 Subject: [PATCH 5/5] clarification --- py/selenium/webdriver/remote/webelement.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/py/selenium/webdriver/remote/webelement.py b/py/selenium/webdriver/remote/webelement.py index c57959a12543c..a6b17fc9ebe88 100644 --- a/py/selenium/webdriver/remote/webelement.py +++ b/py/selenium/webdriver/remote/webelement.py @@ -156,7 +156,7 @@ def clear(self) -> None: def get_property(self, name) -> str | bool | WebElement | dict: """Gets the given property of the element. - Attributes: + Parameters: ---------- name : str - Name of the property to retrieve. @@ -180,7 +180,7 @@ def get_dom_attribute(self, name) -> str: :func:`~selenium.webdriver.remote.BaseWebElement.get_attribute`, this method only returns attributes declared in the element's HTML markup. - Attributes: + Parameters: ---------- name : str - Name of the attribute to retrieve. @@ -212,7 +212,7 @@ def get_attribute(self, name) -> str | None: use :func:`~selenium.webdriver.remote.BaseWebElement.get_dom_attribute` or :func:`~selenium.webdriver.remote.BaseWebElement.get_property` methods respectively. - Attributes: + Parameters: ---------- name : str - Name of the attribute/property to retrieve. @@ -259,7 +259,7 @@ def is_enabled(self) -> bool: def send_keys(self, *value: str) -> None: """Simulates typing into the element. - Attributes: + Parameters: ---------- value : str - A string for typing, or setting form fields. For setting @@ -380,7 +380,7 @@ def size(self) -> dict: def value_of_css_property(self, property_name) -> str: """The value of a CSS property. - Attributes: + Parameters: ---------- property_name : str - The name of the CSS property to get the value of. @@ -491,7 +491,7 @@ def screenshot(self, filename) -> bool: -------- bool : True if the screenshot was saved successfully, False otherwise. - Attributes: + Parameters: ---------- filename : str The full path you wish to save your screenshot to. This @@ -552,12 +552,12 @@ def __ne__(self, element): def _execute(self, command, params=None): """Executes a command against the underlying HTML element. - Attributes: + Parameters: ---------- command : any The name of the command to _execute as a string. params : dict - A dictionary of named Attributes to send with the command. + A dictionary of named Parameters to send with the command. Returns: ------- @@ -571,7 +571,7 @@ def _execute(self, command, params=None): def find_element(self, by=By.ID, value=None) -> WebElement: """Find an element given a By strategy and locator. - Attributes: + Parameters: ---------- by : selenium.webdriver.common.by.By The locating strategy to use. Default is `By.ID`. Supported values include: @@ -600,7 +600,7 @@ def find_element(self, by=By.ID, value=None) -> WebElement: def find_elements(self, by=By.ID, value=None) -> List[WebElement]: """Find elements given a By strategy and locator. - Attributes: + Parameters: ---------- by : selenium.webdriver.common.by.By The locating strategy to use. Default is `By.ID`. Supported values include: