From a849bb75a3d4382f2e3c9341ee00a4acba1114bf Mon Sep 17 00:00:00 2001 From: AutomatedTester Date: Fri, 28 Feb 2020 11:49:24 +0000 Subject: [PATCH] [py] Add the ability to get and set timeouts against a driver. Fixes #7738 This adds in Get Timeouts command as described in https://w3c.github.io/webdriver/#get-timeouts and cleans up the Set Timeouts command. A new Timeouts object has been added to help guide people through this. --- py/selenium/webdriver/common/timeouts.py | 94 +++++++++++++++++++ py/selenium/webdriver/remote/command.py | 1 + .../webdriver/remote/remote_connection.py | 2 + py/selenium/webdriver/remote/webdriver.py | 41 +++++++- .../webdriver/common/timeout_tests.py | 70 ++++++++++++++ 5 files changed, 204 insertions(+), 4 deletions(-) create mode 100644 py/selenium/webdriver/common/timeouts.py create mode 100644 py/test/selenium/webdriver/common/timeout_tests.py diff --git a/py/selenium/webdriver/common/timeouts.py b/py/selenium/webdriver/common/timeouts.py new file mode 100644 index 0000000000000..9d40b0f035add --- /dev/null +++ b/py/selenium/webdriver/common/timeouts.py @@ -0,0 +1,94 @@ +# 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. + +class Timeouts(object): + + def __init__(self, implicit_wait=None, page_load=None, script=None): + """ + Create a new Timeout object. + + :Args: + - implicit_wait - Either an int or a float. The number passed in needs to how many + seconds the driver will wait. + - page_load - Either an int or a float. The number passed in needs to how many + seconds the driver will wait. + - script - Either an int or a float. The number passed in needs to how many + seconds the driver will wait. + """ + self._implicit_wait = self._convert(implicit_wait) + self._page_load = self._convert(page_load) + self._script = self._convert(script) + + @property + def implicit_wait(self): + """ + Return the value for the implicit wait. This does not return the value on the remote end + """ + return self._implicit_wait / 1000 + + @implicit_wait.setter + def implicit_wait(self, _implicit_wait): + """ + Sets the value for the implicit wait. This does not set the value on the remote end + """ + self._implicit_wait = self._convert(_implicit_wait) + + @property + def page_load(self): + """ + Return the value for the page load wait. This does not return the value on the remote end + """ + return self._page_load / 1000 + + @page_load.setter + def page_load(self, _page_load): + """ + Sets the value for the page load wait. This does not set the value on the remote end + """ + self._page_load = self._convert(_page_load) + + @property + def script(self): + """ + Return the value for the script wait. This does not return the value on the remote end + """ + return self._script / 1000 + + @script.setter + def script(self, _script): + """ + Sets the value for the script wait. This does not set the value on the remote end + """ + self._script = self._convert(_script) + + def _convert(self, timeout): + if timeout is not None: + if isinstance(timeout, (int, float)): + return int(float(timeout) * 1000) + else: + raise TypeError("Timeouts can only be an int or a float") + + def _to_json(self): + timeouts = {} + if self._implicit_wait is not None: + timeouts["implicit"] = self._implicit_wait + if self._page_load is not None: + timeouts["pageLoad"] = self._page_load + if self._script is not None: + timeouts["script"] = self._script + + return timeouts diff --git a/py/selenium/webdriver/remote/command.py b/py/selenium/webdriver/remote/command.py index 053e47b764a8c..1a53e8a28396a 100644 --- a/py/selenium/webdriver/remote/command.py +++ b/py/selenium/webdriver/remote/command.py @@ -101,6 +101,7 @@ class Command(object): EXECUTE_ASYNC_SCRIPT = "executeAsyncScript" SET_SCRIPT_TIMEOUT = "setScriptTimeout" SET_TIMEOUTS = "setTimeouts" + GET_TIMEOUTS = "getTimeouts" MAXIMIZE_WINDOW = "windowMaximize" W3C_MAXIMIZE_WINDOW = "w3cMaximizeWindow" GET_LOG = "getLog" diff --git a/py/selenium/webdriver/remote/remote_connection.py b/py/selenium/webdriver/remote/remote_connection.py index e8abcceef51b5..90951c682181b 100644 --- a/py/selenium/webdriver/remote/remote_connection.py +++ b/py/selenium/webdriver/remote/remote_connection.py @@ -202,6 +202,8 @@ def __init__(self, remote_server_addr, keep_alive=False, resolve_ip=None): ('POST', '/session/$sessionId/timeouts/async_script'), Command.SET_TIMEOUTS: ('POST', '/session/$sessionId/timeouts'), + Command.GET_TIMEOUTS: + ('GET', '/session/$sessionId/timeouts'), Command.DISMISS_ALERT: ('POST', '/session/$sessionId/dismiss_alert'), Command.W3C_DISMISS_ALERT: diff --git a/py/selenium/webdriver/remote/webdriver.py b/py/selenium/webdriver/remote/webdriver.py index 2a5bd809cb0cf..5017b7285173b 100644 --- a/py/selenium/webdriver/remote/webdriver.py +++ b/py/selenium/webdriver/remote/webdriver.py @@ -24,12 +24,13 @@ import warnings from .command import Command -from .webelement import WebElement -from .remote_connection import RemoteConnection from .errorhandler import ErrorHandler -from .switch_to import SwitchTo -from .mobile import Mobile from .file_detector import FileDetector, LocalFileDetector +from .mobile import Mobile +from .remote_connection import RemoteConnection +from .switch_to import SwitchTo +from .webelement import WebElement + from selenium.common.exceptions import (InvalidArgumentException, WebDriverException, NoSuchCookieException, @@ -37,6 +38,8 @@ from selenium.webdriver.common.by import By from selenium.webdriver.common.html5.application_cache import ApplicationCache +from selenium.webdriver.common.timeouts import Timeouts + from selenium.webdriver.support.relative_locator import RelativeBy try: @@ -1002,6 +1005,36 @@ def set_page_load_timeout(self, time_to_wait): 'ms': float(time_to_wait) * 1000, 'type': 'page load'}) + @property + def timeouts(self): + """ + Get all the timeouts that have been set on the current session + + :Usage: + :: + driver.timeouts + :rtype: Timeout + """ + timeouts = self.execute(Command.GET_TIMEOUTS)['value'] + timeouts["implicit_wait"] = timeouts.pop("implicit") / 1000 + timeouts["page_load"] = timeouts.pop("pageLoad") / 1000 + timeouts["script"] = timeouts.pop("script") / 1000 + return Timeouts(**timeouts) + + @timeouts.setter + def timeouts(self, timeouts): + """ + Set all timeouts for the session. This will override any previously + set timeouts. + + :Usage: + :: + my_timeouts = Timeouts() + my_timeouts.implicit_wait = 10 + driver.timeouts = my_timeouts + """ + self.execute(Command.SET_TIMEOUTS, timeouts._to_json())['value'] + def find_element(self, by=By.ID, value=None): """ Find an element given a By strategy and locator. Prefer the find_element_by_* methods when diff --git a/py/test/selenium/webdriver/common/timeout_tests.py b/py/test/selenium/webdriver/common/timeout_tests.py new file mode 100644 index 0000000000000..a7cf01bb8fedb --- /dev/null +++ b/py/test/selenium/webdriver/common/timeout_tests.py @@ -0,0 +1,70 @@ +# 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 pytest + +from selenium.webdriver.common.timeouts import Timeouts + +def test_should_create_timeouts_object(): + implicit_wait = 10 + page_load = 10 + script = 10 + timeouts = Timeouts(implicit_wait=implicit_wait,page_load=page_load,script=script) + + assert implicit_wait == timeouts.implicit_wait + assert page_load == timeouts.page_load + assert script == timeouts.script + +def test_should_error_if_implicit_wait_isnt_a_number(): + with pytest.raises(TypeError): + Timeouts(implicit_wait="abc") + + timeout = Timeouts(implicit_wait=0) + with pytest.raises(TypeError): + timeout.implicit_wait="abc" + + +def test_should_error_if_page_load_isnt_a_number(): + with pytest.raises(TypeError): + Timeouts(page_load="abc") + + timeout = Timeouts(page_load=0) + with pytest.raises(TypeError): + timeout.page_load = "abc" + + +def test_should_error_if_script_isnt_a_number(): + with pytest.raises(TypeError): + Timeouts(script="abc") + + timeout = Timeouts(script=0) + with pytest.raises(TypeError): + timeout.script = "abc" + + +def test_should_get_timeouts_without_setting_them(driver): + results = driver.timeouts + assert results.implicit_wait == 0 + assert results.page_load == 300 + assert results.script == 30 + +def test_should_set_and_get_timeouts_on_remote_end(driver): + timeout = Timeouts(implicit_wait=10) + driver.timeouts = timeout + result = driver.timeouts + assert result.implicit_wait == timeout.implicit_wait \ No newline at end of file