From e7cf68fe03d033fedf02327c59482ede6557c402 Mon Sep 17 00:00:00 2001 From: sandeepsuryaprasad Date: Thu, 29 Jun 2023 20:07:01 +0530 Subject: [PATCH 1/6] [py] added type hinting support to ActionChains and Alert class --- py/selenium/webdriver/common/action_chains.py | 55 +++++++++++-------- py/selenium/webdriver/common/alert.py | 10 ++-- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/py/selenium/webdriver/common/action_chains.py b/py/selenium/webdriver/common/action_chains.py index b9fbaa11a5a30..bcdd7f78dc14f 100644 --- a/py/selenium/webdriver/common/action_chains.py +++ b/py/selenium/webdriver/common/action_chains.py @@ -17,6 +17,9 @@ """The ActionChains implementation,""" import warnings +from typing import List +from typing import Optional +from typing import Union from selenium.webdriver.remote.webelement import WebElement @@ -27,6 +30,8 @@ from .actions.wheel_input import WheelInput from .utils import keys_to_typing +any_device = Union[PointerInput, KeyInput, WheelInput] + class ActionChains: """ActionChains are a way to automate low level interactions such as mouse @@ -61,7 +66,7 @@ class ActionChains: another. """ - def __init__(self, driver, duration=250, devices=None): + def __init__(self, driver, duration: int = 250, devices: Optional[List[any_device]] = None) -> None: """Creates a new ActionChains. :Args: @@ -82,18 +87,18 @@ def __init__(self, driver, duration=250, devices=None): wheel = device self.w3c_actions = ActionBuilder(driver, mouse=mouse, keyboard=keyboard, wheel=wheel, duration=duration) - def perform(self): + def perform(self) -> None: """Performs all stored actions.""" self.w3c_actions.perform() - def reset_actions(self): + def reset_actions(self) -> None: """Clears actions that are already stored locally and on the remote end.""" self.w3c_actions.clear_actions() for device in self.w3c_actions.devices: device.clear_actions() - def click(self, on_element=None): + def click(self, on_element=Optional[WebElement]) -> "ActionChains": """Clicks an element. :Args: @@ -109,7 +114,7 @@ def click(self, on_element=None): return self - def click_and_hold(self, on_element=None): + def click_and_hold(self, on_element=Optional[WebElement]) -> "ActionChains": """Holds down the left mouse button on an element. :Args: @@ -124,7 +129,7 @@ def click_and_hold(self, on_element=None): return self - def context_click(self, on_element=None): + def context_click(self, on_element=Optional[WebElement]) -> "ActionChains": """Performs a context-click (right click) on an element. :Args: @@ -140,7 +145,7 @@ def context_click(self, on_element=None): return self - def double_click(self, on_element=None): + def double_click(self, on_element=Optional[WebElement]) -> "ActionChains": """Double-clicks an element. :Args: @@ -156,7 +161,7 @@ def double_click(self, on_element=None): return self - def drag_and_drop(self, source, target): + def drag_and_drop(self, source: WebElement, target: WebElement) -> "ActionChains": """Holds down the left mouse button on the source element, then moves to the target element and releases the mouse button. @@ -168,7 +173,7 @@ def drag_and_drop(self, source, target): self.release(target) return self - def drag_and_drop_by_offset(self, source, xoffset, yoffset): + def drag_and_drop_by_offset(self, source: WebElement, xoffset: int, yoffset: int) -> "ActionChains": """Holds down the left mouse button on the source element, then moves to the target offset and releases the mouse button. @@ -182,7 +187,7 @@ def drag_and_drop_by_offset(self, source, xoffset, yoffset): self.release() return self - def key_down(self, value, element=None): + def key_down(self, value, element=Optional[WebElement]) -> "ActionChains": """Sends a key press only, without releasing it. Should only be used with modifier keys (Control, Alt and Shift). @@ -203,7 +208,7 @@ def key_down(self, value, element=None): return self - def key_up(self, value, element=None): + def key_up(self, value, element=Optional[WebElement]) -> "ActionChains": """Releases a modifier key. :Args: @@ -223,7 +228,7 @@ def key_up(self, value, element=None): return self - def move_by_offset(self, xoffset, yoffset): + def move_by_offset(self, xoffset: int, yoffset: int) -> "ActionChains": """Moving the mouse to an offset from current mouse position. :Args: @@ -236,7 +241,7 @@ def move_by_offset(self, xoffset, yoffset): return self - def move_to_element(self, to_element): + def move_to_element(self, to_element: WebElement) -> "ActionChains": """Moving the mouse to the middle of an element. :Args: @@ -248,7 +253,7 @@ def move_to_element(self, to_element): return self - def move_to_element_with_offset(self, to_element, xoffset, yoffset): + def move_to_element_with_offset(self, to_element: WebElement, xoffset: int, yoffset: int) -> "ActionChains": """Move the mouse by an offset of the specified element. Offsets are relative to the in-view center point of the element. @@ -263,7 +268,7 @@ def move_to_element_with_offset(self, to_element, xoffset, yoffset): return self - def pause(self, seconds): + def pause(self, seconds: Union[float, int]) -> "ActionChains": """Pause all inputs for the specified duration in seconds.""" self.w3c_actions.pointer_action.pause(seconds) @@ -271,7 +276,7 @@ def pause(self, seconds): return self - def release(self, on_element=None): + def release(self, on_element=Optional[WebElement]) -> "ActionChains": """Releasing a held mouse button on an element. :Args: @@ -286,7 +291,7 @@ def release(self, on_element=None): return self - def send_keys(self, *keys_to_send): + def send_keys(self, *keys_to_send: str) -> "ActionChains": """Sends keys to current focused element. :Args: @@ -301,7 +306,7 @@ def send_keys(self, *keys_to_send): return self - def send_keys_to_element(self, element, *keys_to_send): + def send_keys_to_element(self, element: WebElement, *keys_to_send: str) -> "ActionChains": """Sends keys to an element. :Args: @@ -313,7 +318,7 @@ def send_keys_to_element(self, element, *keys_to_send): self.send_keys(*keys_to_send) return self - def scroll_to_element(self, element: WebElement): + def scroll_to_element(self, element: WebElement) -> "ActionChains": """If the element is outside the viewport, scrolls the bottom of the element to the bottom of the viewport. @@ -324,7 +329,7 @@ def scroll_to_element(self, element: WebElement): self.w3c_actions.wheel_action.scroll(origin=element) return self - def scroll_by_amount(self, delta_x: int, delta_y: int): + def scroll_by_amount(self, delta_x: int, delta_y: int) -> "ActionChains": """Scrolls by provided amounts with the origin in the top left corner of the viewport. @@ -336,7 +341,7 @@ def scroll_by_amount(self, delta_x: int, delta_y: int): self.w3c_actions.wheel_action.scroll(delta_x=delta_x, delta_y=delta_y) return self - def scroll_from_origin(self, scroll_origin: ScrollOrigin, delta_x: int, delta_y: int): + def scroll_from_origin(self, scroll_origin: ScrollOrigin, delta_x: int, delta_y: int) -> "ActionChains": """Scrolls by provided amount based on a provided origin. The scroll origin is either the center of an element or the upper left of the viewport plus any offsets. If the origin is an element, and the element @@ -364,7 +369,9 @@ def scroll_from_origin(self, scroll_origin: ScrollOrigin, delta_x: int, delta_y: ) return self - def scroll(self, x: int, y: int, delta_x: int, delta_y: int, duration: int = 0, origin: str = "viewport"): + def scroll( + self, x: int, y: int, delta_x: int, delta_y: int, duration: int = 0, origin: str = "viewport" + ) -> "ActionChains": """Sends wheel scroll information to the browser to be processed. :Args: @@ -386,8 +393,8 @@ def scroll(self, x: int, y: int, delta_x: int, delta_y: int, duration: int = 0, # Context manager so ActionChains can be used in a 'with .. as' statements. - def __enter__(self): + def __enter__(self) -> "ActionChains": return self # Return created instance of self. - def __exit__(self, _type, _value, _traceback): + def __exit__(self, _type, _value, _traceback) -> None: pass # Do nothing, does not require additional cleanup. diff --git a/py/selenium/webdriver/common/alert.py b/py/selenium/webdriver/common/alert.py index 131ced8cb991d..ae2675e505540 100644 --- a/py/selenium/webdriver/common/alert.py +++ b/py/selenium/webdriver/common/alert.py @@ -45,7 +45,7 @@ class Alert: self.assertEqual("Do you wish to quit?", alert_text) """ - def __init__(self, driver): + def __init__(self, driver) -> None: """Creates a new Alert. :Args: @@ -54,15 +54,15 @@ def __init__(self, driver): self.driver = driver @property - def text(self): + def text(self) -> str: """Gets the text of the Alert.""" return self.driver.execute(Command.W3C_GET_ALERT_TEXT)["value"] - def dismiss(self): + def dismiss(self) -> None: """Dismisses the alert available.""" self.driver.execute(Command.W3C_DISMISS_ALERT) - def accept(self): + def accept(self) -> None: """Accepts the alert available. :Usage: @@ -72,7 +72,7 @@ def accept(self): """ self.driver.execute(Command.W3C_ACCEPT_ALERT) - def send_keys(self, keysToSend): + def send_keys(self, keysToSend: str) -> None: """Send Keys to the Alert. :Args: From 4d831876141387b87e7dcc2b25af23e47f3c1561 Mon Sep 17 00:00:00 2001 From: sandeepsuryaprasad Date: Fri, 30 Jun 2023 12:42:20 +0530 Subject: [PATCH 2/6] [py] incorporated review comments --- py/selenium/webdriver/common/action_chains.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/py/selenium/webdriver/common/action_chains.py b/py/selenium/webdriver/common/action_chains.py index bcdd7f78dc14f..8ed8a8f5cb3d4 100644 --- a/py/selenium/webdriver/common/action_chains.py +++ b/py/selenium/webdriver/common/action_chains.py @@ -30,7 +30,7 @@ from .actions.wheel_input import WheelInput from .utils import keys_to_typing -any_device = Union[PointerInput, KeyInput, WheelInput] +AnyDevice = Union[PointerInput, KeyInput, WheelInput] class ActionChains: @@ -66,7 +66,7 @@ class ActionChains: another. """ - def __init__(self, driver, duration: int = 250, devices: Optional[List[any_device]] = None) -> None: + def __init__(self, driver, duration: int = 250, devices: Optional[List[AnyDevice]] = None) -> None: """Creates a new ActionChains. :Args: @@ -187,7 +187,7 @@ def drag_and_drop_by_offset(self, source: WebElement, xoffset: int, yoffset: int self.release() return self - def key_down(self, value, element=Optional[WebElement]) -> "ActionChains": + def key_down(self, value: str, element=Optional[WebElement]) -> "ActionChains": """Sends a key press only, without releasing it. Should only be used with modifier keys (Control, Alt and Shift). @@ -208,7 +208,7 @@ def key_down(self, value, element=Optional[WebElement]) -> "ActionChains": return self - def key_up(self, value, element=Optional[WebElement]) -> "ActionChains": + def key_up(self, value: str, element=Optional[WebElement]) -> "ActionChains": """Releases a modifier key. :Args: From 5aa2cb7f71f08db2adb52a65a0683db171e2f096 Mon Sep 17 00:00:00 2001 From: sandeepsuryaprasad Date: Sat, 1 Jul 2023 07:42:44 +0530 Subject: [PATCH 3/6] [py] fixed all review comments --- py/selenium/webdriver/common/action_chains.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/py/selenium/webdriver/common/action_chains.py b/py/selenium/webdriver/common/action_chains.py index 8ed8a8f5cb3d4..9646cf51e95d2 100644 --- a/py/selenium/webdriver/common/action_chains.py +++ b/py/selenium/webdriver/common/action_chains.py @@ -16,7 +16,10 @@ # under the License. """The ActionChains implementation,""" +from __future__ import annotations + import warnings +from typing import TYPE_CHECKING from typing import List from typing import Optional from typing import Union @@ -30,6 +33,15 @@ from .actions.wheel_input import WheelInput from .utils import keys_to_typing +if TYPE_CHECKING: + from selenium.webdriver import Chrome + from selenium.webdriver import Edge + from selenium.webdriver import Firefox + from selenium.webdriver import Ie + from selenium.webdriver import Safari + + AnyDriver = Union[Chrome, Firefox, Safari, Ie, Edge] + AnyDevice = Union[PointerInput, KeyInput, WheelInput] @@ -66,7 +78,7 @@ class ActionChains: another. """ - def __init__(self, driver, duration: int = 250, devices: Optional[List[AnyDevice]] = None) -> None: + def __init__(self, driver: AnyDriver, duration: int = 250, devices: Optional[List[AnyDevice]] = None) -> None: """Creates a new ActionChains. :Args: From 5aa4b01ff80df9c7b85bb6a9d64f164e6951a86d Mon Sep 17 00:00:00 2001 From: sandeepsuryaprasad Date: Sat, 1 Jul 2023 20:21:01 +0530 Subject: [PATCH 4/6] [py] fixed lynting error --- py/selenium/webdriver/ie/options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/selenium/webdriver/ie/options.py b/py/selenium/webdriver/ie/options.py index e64f41127b2cb..94b56f40a2f89 100644 --- a/py/selenium/webdriver/ie/options.py +++ b/py/selenium/webdriver/ie/options.py @@ -387,4 +387,4 @@ def to_capabilities(self) -> dict: @property def default_capabilities(self) -> dict: - return DesiredCapabilities.INTERNETEXPLORER.copy() \ No newline at end of file + return DesiredCapabilities.INTERNETEXPLORER.copy() From 5500026d60ba37ebf2a63c18eeb91248c83559e4 Mon Sep 17 00:00:00 2001 From: sandeepsuryaprasad Date: Tue, 4 Jul 2023 20:47:13 +0530 Subject: [PATCH 5/6] initlised functions which takes optional webelement with None --- py/selenium/webdriver/common/action_chains.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/py/selenium/webdriver/common/action_chains.py b/py/selenium/webdriver/common/action_chains.py index 9646cf51e95d2..e1e50b0c5aca9 100644 --- a/py/selenium/webdriver/common/action_chains.py +++ b/py/selenium/webdriver/common/action_chains.py @@ -110,7 +110,7 @@ def reset_actions(self) -> None: for device in self.w3c_actions.devices: device.clear_actions() - def click(self, on_element=Optional[WebElement]) -> "ActionChains": + def click(self, on_element: Optional[WebElement] = None) -> "ActionChains": """Clicks an element. :Args: @@ -126,7 +126,7 @@ def click(self, on_element=Optional[WebElement]) -> "ActionChains": return self - def click_and_hold(self, on_element=Optional[WebElement]) -> "ActionChains": + def click_and_hold(self, on_element: Optional[WebElement] = None) -> "ActionChains": """Holds down the left mouse button on an element. :Args: @@ -141,7 +141,7 @@ def click_and_hold(self, on_element=Optional[WebElement]) -> "ActionChains": return self - def context_click(self, on_element=Optional[WebElement]) -> "ActionChains": + def context_click(self, on_element: Optional[WebElement] = None) -> "ActionChains": """Performs a context-click (right click) on an element. :Args: @@ -157,7 +157,7 @@ def context_click(self, on_element=Optional[WebElement]) -> "ActionChains": return self - def double_click(self, on_element=Optional[WebElement]) -> "ActionChains": + def double_click(self, on_element: Optional[WebElement] = None) -> "ActionChains": """Double-clicks an element. :Args: @@ -199,7 +199,7 @@ def drag_and_drop_by_offset(self, source: WebElement, xoffset: int, yoffset: int self.release() return self - def key_down(self, value: str, element=Optional[WebElement]) -> "ActionChains": + def key_down(self, value: str, element: Optional[WebElement] = None) -> "ActionChains": """Sends a key press only, without releasing it. Should only be used with modifier keys (Control, Alt and Shift). @@ -220,7 +220,7 @@ def key_down(self, value: str, element=Optional[WebElement]) -> "ActionChains": return self - def key_up(self, value: str, element=Optional[WebElement]) -> "ActionChains": + def key_up(self, value: str, element: Optional[WebElement] = None) -> "ActionChains": """Releases a modifier key. :Args: @@ -288,7 +288,7 @@ def pause(self, seconds: Union[float, int]) -> "ActionChains": return self - def release(self, on_element=Optional[WebElement]) -> "ActionChains": + def release(self, on_element: Optional[WebElement] = None) -> "ActionChains": """Releasing a held mouse button on an element. :Args: From 714e6457c73587a0649742009abb0c45df86b220 Mon Sep 17 00:00:00 2001 From: sandeepsuryaprasad Date: Wed, 5 Jul 2023 10:13:50 +0530 Subject: [PATCH 6/6] [py] initialised functions with optional WebElement with None --- py/selenium/webdriver/common/action_chains.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/selenium/webdriver/common/action_chains.py b/py/selenium/webdriver/common/action_chains.py index e1e50b0c5aca9..90721e1341ae8 100644 --- a/py/selenium/webdriver/common/action_chains.py +++ b/py/selenium/webdriver/common/action_chains.py @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -"""The ActionChains implementation,""" +"""The ActionChains implementation.""" from __future__ import annotations import warnings