From 23df12adfcb33a0786c74f5920c90ec507ae6dca Mon Sep 17 00:00:00 2001 From: titusfortner Date: Fri, 24 Sep 2021 18:02:53 -0500 Subject: [PATCH 1/3] [py] move page load strategy methods into Base Options --- py/selenium/webdriver/chromium/options.py | 11 ----------- py/selenium/webdriver/common/options.py | 22 ++++++++++++++++++++++ py/selenium/webdriver/firefox/options.py | 11 ----------- py/selenium/webdriver/opera/options.py | 11 ----------- py/selenium/webdriver/safari/options.py | 11 ----------- py/selenium/webdriver/webkitgtk/options.py | 11 ----------- 6 files changed, 22 insertions(+), 55 deletions(-) diff --git a/py/selenium/webdriver/chromium/options.py b/py/selenium/webdriver/chromium/options.py index 3edf2655462aa..14d974c514f96 100644 --- a/py/selenium/webdriver/chromium/options.py +++ b/py/selenium/webdriver/chromium/options.py @@ -151,17 +151,6 @@ def headless(self, value: bool): else: self._arguments = list(set(self._arguments) - args) - @property - def page_load_strategy(self) -> str: - return self._caps["pageLoadStrategy"] - - @page_load_strategy.setter - def page_load_strategy(self, strategy: str): - if strategy in ["normal", "eager", "none"]: - self.set_capability("pageLoadStrategy", strategy) - else: - raise ValueError("Strategy can only be one of the following: normal, eager, none") - def to_capabilities(self) -> dict: """ Creates a capabilities with all the options that have been set diff --git a/py/selenium/webdriver/common/options.py b/py/selenium/webdriver/common/options.py index 0fe46b5cb34b2..75ad3cadf6d63 100644 --- a/py/selenium/webdriver/common/options.py +++ b/py/selenium/webdriver/common/options.py @@ -16,6 +16,7 @@ # under the License. from abc import ABCMeta, abstractmethod +from typing import NoReturn class BaseOptions(metaclass=ABCMeta): @@ -37,6 +38,27 @@ def set_capability(self, name, value): """ Sets a capability """ self._caps[name] = value + @property + def page_load_strategy(self) -> str: + """ + :returns: page load strategy if set, otherwise "normal" + """ + return self._caps["pageLoadStrategy"] + + @page_load_strategy.setter + def page_load_strategy(self, strategy: str) -> NoReturn: + """ + Determines the point at which a navigation command is returned: + https://w3c.github.io/webdriver/#dfn-table-of-page-load-strategies + + :param strategy: the strategy corresponding to a document readiness state + """ + if strategy in ["normal", "eager", "none"]: + self.set_capability("pageLoadStrategy", strategy) + else: + raise ValueError("Strategy can only be one of the following: normal, eager, none") + + def enable_mobile(self, android_package: str = None, android_activity: str = None, device_serial: str = None): """ Enables mobile browser use for browsers that support it diff --git a/py/selenium/webdriver/firefox/options.py b/py/selenium/webdriver/firefox/options.py index a48504ea8607b..2de08f471e2be 100644 --- a/py/selenium/webdriver/firefox/options.py +++ b/py/selenium/webdriver/firefox/options.py @@ -150,17 +150,6 @@ def headless(self, value: bool): elif '-headless' in self._arguments: self._arguments.remove('-headless') - @property - def page_load_strategy(self) -> str: - return self._caps["pageLoadStrategy"] - - @page_load_strategy.setter - def page_load_strategy(self, strategy: str): - if strategy in ["normal", "eager", "none"]: - self.set_capability("pageLoadStrategy", strategy) - else: - raise ValueError("Strategy can only be one of the following: normal, eager, none") - def enable_mobile(self, android_package: str = "org.mozilla.firefox", android_activity=None, device_serial=None): super().enable_mobile(android_package, android_activity, device_serial) diff --git a/py/selenium/webdriver/opera/options.py b/py/selenium/webdriver/opera/options.py index 5bc10b44c448f..4fc29c172d665 100644 --- a/py/selenium/webdriver/opera/options.py +++ b/py/selenium/webdriver/opera/options.py @@ -79,17 +79,6 @@ def android_command_line_file(self, value): """ self._android_command_line_file = value - @property - def page_load_strategy(self): - return self._caps["pageLoadStrategy"] - - @page_load_strategy.setter - def page_load_strategy(self, strategy): - if strategy in ["normal", "eager", "none"]: - self.set_capability("pageLoadStrategy", strategy) - else: - raise ValueError("Strategy can only be one of the following: normal, eager, none") - def to_capabilities(self): """ Creates a capabilities with all the options that have been set and diff --git a/py/selenium/webdriver/safari/options.py b/py/selenium/webdriver/safari/options.py index 4f78a57677cec..4727e323af73d 100644 --- a/py/selenium/webdriver/safari/options.py +++ b/py/selenium/webdriver/safari/options.py @@ -62,17 +62,6 @@ def accept_insecure_certs(self) -> bool: def accept_insecure_certs(self, value: bool): self._caps['acceptInsecureCerts'] = value - @property - def page_load_strategy(self) -> str: - return self._caps["pageLoadStrategy"] - - @page_load_strategy.setter - def page_load_strategy(self, strategy: str): - if strategy in ["normal", "eager", "none"]: - self.set_capability("pageLoadStrategy", strategy) - else: - raise ValueError("Strategy can only be one of the following: normal, eager, none") - def to_capabilities(self) -> dict: """Marshals the options to an desired capabilities object. """ diff --git a/py/selenium/webdriver/webkitgtk/options.py b/py/selenium/webdriver/webkitgtk/options.py index cbf5a620c9352..ad84c468336ae 100644 --- a/py/selenium/webdriver/webkitgtk/options.py +++ b/py/selenium/webdriver/webkitgtk/options.py @@ -61,17 +61,6 @@ def overlay_scrollbars_enabled(self, value): """ self._overlay_scrollbars_enabled = value - @property - def page_load_strategy(self): - return self._caps["pageLoadStrategy"] - - @page_load_strategy.setter - def page_load_strategy(self, strategy): - if strategy in ["normal", "eager", "none"]: - self.set_capability("pageLoadStrategy", strategy) - else: - raise ValueError("Strategy can only be one of the following: normal, eager, none") - def to_capabilities(self): """ Creates a capabilities with all the options that have been set and From 464ce082e0cccd3d18718936d85601b04bace9fa Mon Sep 17 00:00:00 2001 From: titusfortner Date: Sat, 25 Sep 2021 15:37:48 -0500 Subject: [PATCH 2/3] [py] move proxy and insecure certs capabilities to Base Options --- py/selenium/webdriver/common/options.py | 32 ++++++++++++++++++++++++ py/selenium/webdriver/firefox/options.py | 23 ----------------- py/selenium/webdriver/safari/options.py | 8 ------ 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/py/selenium/webdriver/common/options.py b/py/selenium/webdriver/common/options.py index 75ad3cadf6d63..48df8f17ccd04 100644 --- a/py/selenium/webdriver/common/options.py +++ b/py/selenium/webdriver/common/options.py @@ -17,6 +17,8 @@ from abc import ABCMeta, abstractmethod from typing import NoReturn +from selenium.webdriver.common.proxy import Proxy +from selenium.common.exceptions import InvalidArgumentException class BaseOptions(metaclass=ABCMeta): @@ -76,6 +78,36 @@ def enable_mobile(self, android_package: str = None, android_activity: str = Non if device_serial: self.mobile_options["androidDeviceSerial"] = device_serial + @property + def accept_insecure_certs(self) -> bool: + """ + :returns: whether the session accepts insecure certificates + """ + return self._caps.get('acceptInsecureCerts') + + @accept_insecure_certs.setter + def accept_insecure_certs(self, value: bool) -> NoReturn: + """ + Whether untrusted and self-signed TLS certificates are implicitly trusted: + https://w3c.github.io/webdriver/#dfn-insecure-tls-certificates + + :param value: whether to accept insecure certificates + """ + self._caps['acceptInsecureCerts'] = value + + @property + def proxy(self) -> Proxy: + """ + :Returns: Proxy if set, otherwise None. + """ + return self._proxy + + @proxy.setter + def proxy(self, value: Proxy): + if not isinstance(value, Proxy): + raise InvalidArgumentException("Only Proxy objects can be passed in.") + self._proxy = value + @abstractmethod def to_capabilities(self): """Convert options into capabilities dictionary.""" diff --git a/py/selenium/webdriver/firefox/options.py b/py/selenium/webdriver/firefox/options.py index 2de08f471e2be..441dfe0965292 100644 --- a/py/selenium/webdriver/firefox/options.py +++ b/py/selenium/webdriver/firefox/options.py @@ -16,9 +16,7 @@ # under the License. from typing import Union import warnings -from selenium.common.exceptions import InvalidArgumentException from selenium.webdriver.common.desired_capabilities import DesiredCapabilities -from selenium.webdriver.common.proxy import Proxy from selenium.webdriver.firefox.firefox_binary import FirefoxBinary from selenium.webdriver.firefox.firefox_profile import FirefoxProfile from selenium.webdriver.common.options import ArgOptions @@ -72,14 +70,6 @@ def binary_location(self, value: str): """ Sets the location of the browser binary by string """ self.binary = value - @property - def accept_insecure_certs(self) -> bool: - return self._caps.get('acceptInsecureCerts') - - @accept_insecure_certs.setter - def accept_insecure_certs(self, value: bool): - self._caps['acceptInsecureCerts'] = value - @property def preferences(self) -> dict: """:Returns: A dict of preferences.""" @@ -89,19 +79,6 @@ def set_preference(self, name: str, value: Union[str, int, bool]): """Sets a preference.""" self._preferences[name] = value - @property - def proxy(self) -> Proxy: - """ - :Returns: Proxy if set, otherwise None. - """ - return self._proxy - - @proxy.setter - def proxy(self, value: Proxy): - if not isinstance(value, Proxy): - raise InvalidArgumentException("Only Proxy objects can be passed in.") - self._proxy = value - @property def profile(self) -> FirefoxProfile: """ diff --git a/py/selenium/webdriver/safari/options.py b/py/selenium/webdriver/safari/options.py index 4727e323af73d..1687bf3c604d1 100644 --- a/py/selenium/webdriver/safari/options.py +++ b/py/selenium/webdriver/safari/options.py @@ -54,14 +54,6 @@ def binary_location(self, value: str): """ self._binary_location = value - @property - def accept_insecure_certs(self) -> bool: - return self._caps.get('acceptInsecureCerts') - - @accept_insecure_certs.setter - def accept_insecure_certs(self, value: bool): - self._caps['acceptInsecureCerts'] = value - def to_capabilities(self) -> dict: """Marshals the options to an desired capabilities object. """ From 9efcd57391abc4a55f3ece89e5cc46ef42dc0a1b Mon Sep 17 00:00:00 2001 From: titusfortner Date: Sat, 25 Sep 2021 17:53:47 -0500 Subject: [PATCH 3/3] [py] implement remaining w3c compliant capabilities to Base Options class --- py/selenium/webdriver/common/options.py | 108 +++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/py/selenium/webdriver/common/options.py b/py/selenium/webdriver/common/options.py index 48df8f17ccd04..e940a4645a3b1 100644 --- a/py/selenium/webdriver/common/options.py +++ b/py/selenium/webdriver/common/options.py @@ -40,10 +40,43 @@ def set_capability(self, name, value): """ Sets a capability """ self._caps[name] = value + @property + def browser_version(self) -> str: + """ + :returns: the version of the browser if set, otherwise None. + """ + return self._caps["browserVersion"] + + @browser_version.setter + def browser_version(self, version: str) -> NoReturn: + """ + Requires the major version of the browser to match provided value: + https://w3c.github.io/webdriver/#dfn-browser-version + + :param version: The required version of the browser + """ + self.set_capability("browserVersion", version) + + @property + def platform_name(self) -> str: + """ + :returns: The name of the platform + """ + return self._caps["platformName"] + + @platform_name.setter + def platform_name(self, platform: str) -> NoReturn: + """ + Requires the platform to match the provided value: https://w3c.github.io/webdriver/#dfn-platform-name + + :param platform: the required name of the platform + """ + self.set_capability("platformName", platform) + @property def page_load_strategy(self) -> str: """ - :returns: page load strategy if set, otherwise "normal" + :returns: page load strategy if set, the default is "normal" """ return self._caps["pageLoadStrategy"] @@ -60,6 +93,46 @@ def page_load_strategy(self, strategy: str) -> NoReturn: else: raise ValueError("Strategy can only be one of the following: normal, eager, none") + @property + def unhandled_prompt_behavior(self) -> str: + """ + :returns: unhandled prompt behavior if set, the default is "dismiss and notify" + """ + return self._caps["unhandledPromptBehavior"] + + @unhandled_prompt_behavior.setter + def unhandled_prompt_behavior(self, behavior: str) -> NoReturn: + """ + How the driver should respond when an alert is present and the command sent is not handling the alert: + https://w3c.github.io/webdriver/#dfn-table-of-page-load-strategies + + :param behavior: behavior to use when an alert is encountered + """ + if behavior in ["dismiss", "accept", "dismiss and notify", "accept and notify", "ignore"]: + self.set_capability("unhandledPromptBehavior", behavior) + else: + raise ValueError("Behavior can only be one of the following: dismiss, accept, dismiss and notify, " + "accept and notify, ignore") + + @property + def timeouts(self) -> dict: + """ + :returns: Values for implicit timeout, pageLoad timeout and script timeout if set (in milliseconds) + """ + return self._caps["timeouts"] + + @timeouts.setter + def timeouts(self, timeouts: dict) -> NoReturn: + """ + How long the driver should wait for actions to complete before returning an error + https://w3c.github.io/webdriver/#timeouts + + :param timeouts: values in milliseconds for implicit wait, page load and script timeout + """ + if all(x in timeouts.keys() for x in ["implicit", "pageLoad", "script"]): + self.set_capability("timeouts", timeouts) + else: + raise ValueError("Timeout keys can only be one of the following: implicit, pageLoad, script") def enable_mobile(self, android_package: str = None, android_activity: str = None, device_serial: str = None): """ @@ -95,6 +168,39 @@ def accept_insecure_certs(self, value: bool) -> NoReturn: """ self._caps['acceptInsecureCerts'] = value + @property + def strict_file_interactability(self) -> bool: + """ + :returns: whether session is strict about file interactability + """ + return self._caps.get('strictFileInteractability') + + @strict_file_interactability.setter + def strict_file_interactability(self, value: bool): + """ + Whether interactability checks will be applied to file type input elements. The default is false. + + :param value: whether file interactability is strict + """ + self._caps['strictFileInteractability'] = value + + @property + def set_window_rect(self) -> bool: + """ + :returns: whether the remote end supports setting window size and position + """ + return self._caps.get('setWindowRect') + + @set_window_rect.setter + def set_window_rect(self, value: bool): + """ + Whether the remote end supports all of the resizing and positioning commands. The default is false. + https://w3c.github.io/webdriver/#dfn-strict-file-interactability + + :param value: whether remote end must support setting window resizing and repositioning + """ + self._caps['setWindowRect'] = value + @property def proxy(self) -> Proxy: """