From 2aa203e6b25d1ce40caa7ef654225089425c9e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pekka=20Kl=C3=A4rck?= Date: Fri, 29 Sep 2017 00:31:21 +0300 Subject: [PATCH 01/12] `Set Selenium Speed` return speed as timestr, not int. This is same behavior as in 1.8. Also added a FIXME about totally stupid tests inside suite setup (which did actually reveal this small regression...). --- src/SeleniumLibrary/keywords/browsermanagement.py | 2 +- test/acceptance/keywords/set_selenium_speed.robot | 10 +++++----- test/acceptance/resource.robot | 4 +++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/SeleniumLibrary/keywords/browsermanagement.py b/src/SeleniumLibrary/keywords/browsermanagement.py index 95f7fb799..f76eb6ce7 100644 --- a/src/SeleniumLibrary/keywords/browsermanagement.py +++ b/src/SeleniumLibrary/keywords/browsermanagement.py @@ -522,7 +522,7 @@ def set_selenium_speed(self, seconds): Example: | Set Selenium Speed | .5 seconds | """ - old_speed = self.ctx.speed + old_speed = self.get_selenium_speed() self.ctx.speed = timestr_to_secs(seconds) for browser in self.browsers.browsers: self._monkey_patch_speed(browser) diff --git a/test/acceptance/keywords/set_selenium_speed.robot b/test/acceptance/keywords/set_selenium_speed.robot index a9cbf02fc..8d3da88fa 100644 --- a/test/acceptance/keywords/set_selenium_speed.robot +++ b/test/acceptance/keywords/set_selenium_speed.robot @@ -1,15 +1,15 @@ *** Settings *** Suite Setup Go To Page "forms/prefilled_email_form.html" -Suite Teardown Set Selenium Speed 0 +Test Teardown Set Selenium Speed 0 Resource ../resource.robot *** Test Cases *** Settimg selenium speed is possible multiple times Set Selenium Speed 10 - ${speed} = Set Selenium Speed 5 - Should Be Equal ${speed} ${10} - ${speed} = Set Selenium Speed 1 - Should Be Equal ${speed} ${5} + ${old} = Set Selenium Speed 1 + Should Be Equal ${old} 10 seconds + ${old} = Set Selenium Speed 100 + Should Be Equal ${old} 1 second Selenium speed should affect execution [Documentation] Click Element executes two selenium commands and diff --git a/test/acceptance/resource.robot b/test/acceptance/resource.robot index ce76d1ac2..28e019d4a 100644 --- a/test/acceptance/resource.robot +++ b/test/acceptance/resource.robot @@ -17,7 +17,9 @@ Open Browser To Start Page [Documentation] This keyword also tests 'Set Selenium Speed' and 'Set Selenium Timeout' ... against all reason. ${default speed} ${default timeout}= Open Browser To Start Page Without Testing Default Options - Should Be Equal ${default speed} ${0.0} + # FIXME: We shouldn't test anything here. If this stuff isn't tested elsewhere, new *tests* needs to be added. + # FIXME: The second test below verifies a hard coded return value!!?! + Should Be Equal ${default speed} 0 seconds Should Be Equal ${default timeout} 5 seconds Open Browser To Start Page Without Testing Default Options From d17e3f5f68c1132f681ac7a354dec96f47fa8cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pekka=20Kl=C3=A4rck?= Date: Fri, 29 Sep 2017 00:48:02 +0300 Subject: [PATCH 02/12] Fix Dismiss Alert and Confirm Action regressions. Fixes #934. --- src/SeleniumLibrary/keywords/alert.py | 9 +- test/acceptance/keywords/alerts.robot | 89 +++++++++++++++++++ test/acceptance/keywords/javascript.robot | 64 +------------ .../html/javascript/dynamic_content.html | 2 +- .../keywords/test_keyword_arguments_alert.py | 4 +- 5 files changed, 98 insertions(+), 70 deletions(-) create mode 100644 test/acceptance/keywords/alerts.robot diff --git a/src/SeleniumLibrary/keywords/alert.py b/src/SeleniumLibrary/keywords/alert.py index fe45a6247..f5427e31e 100644 --- a/src/SeleniumLibrary/keywords/alert.py +++ b/src/SeleniumLibrary/keywords/alert.py @@ -102,7 +102,7 @@ def confirm_action(self): | Confirm Action | | # Chooses Cancel | """ text = self._handle_alert(self._next_alert_dismiss_type) - self._next_alert_dismiss_type = self.DISMISS_ALERT + self._next_alert_dismiss_type = self.ACCEPT_ALERT return text @keyword @@ -128,9 +128,10 @@ def dismiss_alert(self, accept=True): dismissed by this keyword or another like `Get Alert Message`. """ if is_truthy(accept): - return self._handle_alert(self.ACCEPT_ALERT) - else: - return self._handle_alert() + self._handle_alert(self.ACCEPT_ALERT) + return True + self._handle_alert(self.DISMISS_ALERT) + return False def _handle_alert(self, dismiss_type=None): """Alert re-try for Chrome diff --git a/test/acceptance/keywords/alerts.robot b/test/acceptance/keywords/alerts.robot new file mode 100644 index 000000000..dca5084d2 --- /dev/null +++ b/test/acceptance/keywords/alerts.robot @@ -0,0 +1,89 @@ +*** Settings *** +Force Tags Known Issue Safari +Test Setup Go To Page "javascript/alert.html" +Resource ../resource.robot + +*** Test Cases *** +Alert Should Be Present + Click Link Click Me! + Alert Should Be Present + +Alert Should Be Present with message validation + Click Link Click Me! + Alert Should Be Present ALERT! + Click Link Click Me Too! + Alert Should Be Present MULTILINE ALERT! + Click Link Click Me! + Run Keyword And Expect Error + ... Alert text should have been 'foo bar' but was 'ALERT!' + ... Alert Should Be Present foo bar + +Alert Should Be Present when there is none + Run Keyword And Expect Error + ... There were no alerts + ... Alert Should Be Present + +Get Alert Message + Click Link Click Me! + ${msg} = Get Alert Message + Should Be Equal ${msg} ALERT! + Click Link Click Me Too! + ${msg} = Get Alert Message + Should Be Equal ${msg} MULTILINE ALERT! + Run Keyword And Expect Error + ... There were no alerts + ... Get Alert Message + +Get Alert Message can leave alert open + Click Link Click Me! + ${msg} = Get Alert Message ${FALSE} + Should Be Equal ${msg} ALERT! + Alert Should Be Present + +Input Text Into Prompt + [Documentation] Always leaves the alert open + [Setup] Go To Page "javascript/alert_prompt.html" + Click Button css=button + Input Text Into Prompt Robot + Alert Should Be Present + Page Should Contain Hello Robot! How are you today? + +Confirm Action + [Setup] Go To Page "javascript/dynamic_content.html" + Click Button Change the title + ${msg}= Confirm Action + Should Be Equal ${msg} Really change the title? + Title Should Be Original Changed! + +Confirm Action multiple times + [Setup] Go To Page "javascript/alert_prompt.html" + Click Button css=button + Input Text Into Prompt Robot # Leaves the alert open + Confirm Action + Page Should Contain Hello Robot! How are you today? + Click Button css=button + Input Text Into Prompt Mr. Robot + Confirm Action + Page Should Contain Hello Mr. Robot! How are you today? + +Cancel Action + [Setup] Go To Page "javascript/alert_prompt.html" + Choose Cancel On Next Confirmation + Click Button css=button + Input Text Into Prompt Robot # Leaves the alert open + Confirm Action + Page Should Not Contain Robot + +Dismiss Alert + [Setup] Go To Page "javascript/dynamic_content.html" + Click Button Change the title + ${accepted} = Dismiss Alert # This actually accepts the alert + Should Be Equal ${accepted} ${TRUE} + Title Should Be Original Changed! + Click Button Change the title + ${accepted} = Dismiss Alert ${FALSE} # and this dismisses.... + Title Should Be Original Changed! + Should Be Equal ${accepted} ${FALSE} + Click Button Change the title + Dismiss Alert true + Title Should Be Original Changed! Changed! diff --git a/test/acceptance/keywords/javascript.robot b/test/acceptance/keywords/javascript.robot index 59b49585b..d16de95c4 100644 --- a/test/acceptance/keywords/javascript.robot +++ b/test/acceptance/keywords/javascript.robot @@ -1,56 +1,14 @@ *** Settings *** -Documentation Tests javascript Test Setup Go To Page "javascript/dynamic_content.html" Resource ../resource.robot *** Test Cases *** Clicking Elements Should Activate Javascript - [Documentation] Clicking Elements Should Activate Javascript Title Should Be Original Click Element link=change title Title Should Be Changed -Alert Should Be Present - [Documentation] Alert Should Be Present - [Tags] Known Issue Safari - [Setup] Go To Page "javascript/alert.html" - Click Link Click Me! - Alert Should Be Present - Click Link Click Me Too! - Alert Should Be Present MULTILINE ALERT! - Click Link Click Me! - Run Keyword And Expect Error Alert text should have been 'foo bar' but was 'ALERT!' Alert Should Be Present foo bar - -Get Alert Message - [Documentation] Get Alert Message - [Tags] Known Issue Safari - [Setup] Go To Page "javascript/alert.html" - Click Link Click Me! - ${msg} = Get Alert Message - Should Be Equal ${msg} ALERT! - Run Keyword And Expect Error There were no alerts Get Alert Message - -Read Alert Message - [Documentation] Read Alert Message - [Tags] Known Issue Safari - [Setup] Go To Page "javascript/alert.html" - Click Link Click Me! - ${msg} = Get Alert Message ${FALSE} - Should Be Equal ${msg} ALERT! - Dismiss Alert - Run Keyword And Expect Error There were no alerts Get Alert Message - -Input Text Into Prompt - [Documentation] Input Text Into Prompt - [Tags] Known Issue Safari - [Setup] Go To Page "javascript/alert_prompt.html" - Click Element css=button - Input Text Into Prompt myname - Dismiss Alert - Page Should Contain myname - Mouse Down On Link - [Documentation] Mouse Down On Link [Tags] Known Issue Safari [Setup] Go To Page "javascript/mouse_events.html" Mouse Down On Image image_mousedown @@ -61,23 +19,6 @@ Mouse Down On Link Text Field Should Contain textfield onmousedown Mouse Up link_mousedown -Confirm Action - [Documentation] Confirm Action - [Tags] Known Issue Safari - Click Button Change the title - ${msg}= Confirm Action - Title Should Be Changed after confirmation - Should Be Equal ${msg} Really change the title? - -Cancel Action - [Documentation] Cancel Action - [Tags] Known Issue Safari - Choose Cancel On Next Confirmation - Click Button Change the title - ${msg}= Confirm Action - Title Should Be Original - Should Be Equal ${msg} Really change the title? - Execute Javascript [Documentation] LOG 2 Executing JavaScript: ... window.add_content('button_target', 'Inserted directly') @@ -92,13 +33,11 @@ Execute Javascript from File Page Should Contain Inserted via file Open Context Menu - [Documentation] Open Context Menu [Tags] Known Issue Safari Go To Page "javascript/context_menu.html" Open Context Menu myDiv Drag and Drop - [Documentation] Drag and Drop [Tags] Known Issue Internet Explorer Known Issue Safari [Setup] Go To Page "javascript/drag_and_drop.html" Element Text Should Be id=droppable Drop here @@ -106,7 +45,6 @@ Drag and Drop Element Text Should Be id=droppable Dropped! Drag and Drop by Offset - [Documentation] Drag and Drop by Offset [Tags] Known Issue Firefox Known Issue Internet Explorer Known Issue Safari [Setup] Go To Page "javascript/drag_and_drop.html" Element Text Should Be id=droppable Drop here @@ -117,7 +55,7 @@ Drag and Drop by Offset Verify Console Log Can be Caught [Tags] Known Issue Firefox - ${message} Set Variable Sample Console Error + ${message}= Set Variable Sample Console Error Execute Javascript console.error('${message}') ${logs}= Get Log browser ${err}= Convert To string ${logs} diff --git a/test/resources/html/javascript/dynamic_content.html b/test/resources/html/javascript/dynamic_content.html index 07b235d29..97d317bd3 100644 --- a/test/resources/html/javascript/dynamic_content.html +++ b/test/resources/html/javascript/dynamic_content.html @@ -28,7 +28,7 @@
+ document.title += ' Changed!';" >
diff --git a/test/unit/keywords/test_keyword_arguments_alert.py b/test/unit/keywords/test_keyword_arguments_alert.py index 5ec5c3b1a..a96e1c053 100644 --- a/test/unit/keywords/test_keyword_arguments_alert.py +++ b/test/unit/keywords/test_keyword_arguments_alert.py @@ -47,9 +47,9 @@ def test_dismiss_alert_true(self): verify(AlertKeywords, times=count)._handle_alert('accept') def test_dismiss_alert_false(self): - when(AlertKeywords)._handle_alert().thenReturn('text') + when(AlertKeywords)._handle_alert('dismiss').thenReturn('text') count = 1 for false in FALSES: self.alert.dismiss_alert(false) - verify(AlertKeywords, times=count)._handle_alert() + verify(AlertKeywords, times=count)._handle_alert('dismiss') count += 1 From d9c9bc37844ae3d772a389e1e79183f7c7962a15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pekka=20Kl=C3=A4rck?= Date: Mon, 2 Oct 2017 23:13:20 +0300 Subject: [PATCH 03/12] Major alert keyword rewrite. 1. Introduce new keywords: Alert Should Not Be Present Handle Alert Input Text Into Alert 2. Add new `action` argument to Alert Should Be Present. 3. Silently depracate all other existing alert related keywords. 4. Change alert polling logic to use WebDriverWait only. Timeout will be made configurable later. 5. Documentation cleanup. --- src/SeleniumLibrary/keywords/alert.py | 224 ++++++++++-------- test/acceptance/keywords/alerts.robot | 129 +++++++++- .../keywords/test_keyword_arguments_alert.py | 23 +- 3 files changed, 257 insertions(+), 119 deletions(-) diff --git a/src/SeleniumLibrary/keywords/alert.py b/src/SeleniumLibrary/keywords/alert.py index f5427e31e..e0ee38e0f 100644 --- a/src/SeleniumLibrary/keywords/alert.py +++ b/src/SeleniumLibrary/keywords/alert.py @@ -14,153 +14,183 @@ # See the License for the specific language governing permissions and # limitations under the License. -import time - from selenium.common.exceptions import WebDriverException from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait -from SeleniumLibrary.base import keyword -from SeleniumLibrary.base import LibraryComponent +from SeleniumLibrary.base import keyword, LibraryComponent from SeleniumLibrary.utils import is_truthy class AlertKeywords(LibraryComponent): + ACCEPT = 'ACCEPT' + DISMISS = 'DISMISS' + LEAVE = 'LEAVE' + _next_alert_action = ACCEPT - ACCEPT_ALERT = 'accept' - DISMISS_ALERT = 'dismiss' + @keyword + def input_text_into_prompt(self, text): + """Deprecated. Use `Input Text Into Alert` instead. - def __init__(self, ctx): - LibraryComponent.__init__(self, ctx) - self._next_alert_dismiss_type = self.ACCEPT_ALERT + Types the given ``text`` into an input field in an alert. + Leaves the alert open. + """ + self.input_text_into_alert(text, self.LEAVE) @keyword - def input_text_into_prompt(self, text): - """Types the given `text` into alert box. """ - try: - alert = self._wait_alert() - alert.send_keys(text) - except WebDriverException: - raise RuntimeError('There were no alerts') + def input_text_into_alert(self, text, action=ACCEPT): + """Types the given ``text`` into an input field in an alert. + + The alert is accepted by default, but that behavior can be controlled + by using the ``action`` argument same way as with `Handle Alert`. + + New in SeleniumLibrary 3.0. + """ + alert = self._wait_alert() + alert.send_keys(text) + self._handle_alert(alert, action) + + @keyword + def alert_should_be_present(self, text='', action=ACCEPT): + """Verifies that an alert is present and, by default, accepts it. + + Fails if no alert is present. If ``text`` is a non-empty string, + then it is used to verify alert's message. The alert is accepted + by default, but that behavior can be controlled by using the + ``action`` argument same way as with `Handle Alert`. + + The ``action`` argument is new in SeleniumLibrary 3.0. In earlier + versions the alert was always accepted. + """ + message = self.handle_alert(action) + if text and text != message: + raise AssertionError("Alert message should have been '%s' but it " + "was '%s'." % (text, message)) @keyword - def alert_should_be_present(self, text=''): - """Verifies an alert is present and dismisses it. + def alert_should_not_be_present(self, action=ACCEPT): + """Verifies that no alert is present. - If `text` is a non-empty string, then it is also verified that the - message of the alert equals to `text`. + If the alert actually exists, ``action`` argument determines + how it should be handled same way as with `Handle Alert`. - Will fail if no alert is present. Note that following keywords - will fail unless the alert is dismissed by this - keyword or another like `Get Alert Message`. + New in SeleniumLibrary 3.0. """ - alert_text = self._handle_alert(self.ACCEPT_ALERT) - if text and alert_text != text: - raise AssertionError("Alert text should have been " - "'%s' but was '%s'" - % (text, alert_text)) + try: + alert = self._wait_alert(timeout=0) + except AssertionError: + return + text = self._handle_alert(alert, action) + raise AssertionError("Alert with message '%s' present." % text) @keyword def choose_cancel_on_next_confirmation(self): - """Cancel will be selected the next time `Confirm Action` is used.""" - self._next_alert_dismiss_type = self.DISMISS_ALERT + """Deprecated. Use `Handle Alert` directly instead. + + In versions prior to SeleniumLibrary 3.0, the alert handling + approach needed to be set separately before using the `Confirm + Action` keyword. New `Handle Alert` keyword accepts the action how + to handle the alert as a normal argument and should be used instead. + """ + self._next_alert_action = self.DISMISS @keyword def choose_ok_on_next_confirmation(self): - """Undo the effect of using keywords `Choose Cancel On Next Confirmation`. Note - that Selenium's overridden window.confirm() function will normally - automatically return true, as if the user had manually clicked OK, so - you shouldn't need to use this command unless for some reason you need - to change your mind prior to the next confirmation. After any - confirmation, Selenium will resume using the default behavior for - future confirmations, automatically returning true (OK) unless/until - you explicitly use `Choose Cancel On Next Confirmation` for each - confirmation. - - Note that every time a confirmation comes up, you must - consume it by using a keywords such as `Get Alert Message`, or else - the following selenium operations will fail. + """Deprecated. Use `Handle Alert` directly instead. + + In versions prior to SeleniumLibrary 3.0, the alert handling + approach needed to be set separately before using the `Confirm + Action` keyword. New `Handle Alert` keyword accepts the action how + to handle the alert as a normal argument and should be used instead. """ - self._next_alert_dismiss_type = self.ACCEPT_ALERT + self._next_alert_action = self.ACCEPT @keyword def confirm_action(self): - """Dismisses currently shown confirmation dialog and returns it's message. - - By default, this keyword chooses 'OK' option from the dialog. If - 'Cancel' needs to be chosen, keyword `Choose Cancel On Next - Confirmation` must be called before the action that causes the - confirmation dialog to be shown. + """Deprecated. Use `Handle Alert` instead. - Examples: - | Click Button | Send | # Shows a confirmation dialog | - | ${message}= | Confirm Action | # Chooses Ok | - | Should Be Equal | ${message} | Are your sure? | - | | | | - | Choose Cancel On Next Confirmation | | | - | Click Button | Send | # Shows a confirmation dialog | - | Confirm Action | | # Chooses Cancel | + By default accepts an alert, but this behavior can be altered + with `Choose Cancel On Next Confirmation` and `Choose Ok On Next + Confirmation` keywords. New `Handle Alert` keyword accepts the action + how to handle the alert as a normal argument and should be used + instead. """ - text = self._handle_alert(self._next_alert_dismiss_type) - self._next_alert_dismiss_type = self.ACCEPT_ALERT + text = self.handle_alert(self._next_alert_action) + self._next_alert_action = self.ACCEPT return text @keyword def get_alert_message(self, dismiss=True): - """Returns the text of current JavaScript alert. + """Deprecated. Use `Handle Alert` instead. + + Returns the message the alert has. Dismisses the alert by default + (i.e. presses ``Cancel``) and setting ``dismiss`` to false leaves + the alert open. There is no support to accept the alert (i.e. to + press ``Ok``). - By default the current JavaScript alert will be dismissed. - This keyword will fail if no alert is present. Note that - following keywords will fail unless the alert is - dismissed by this keyword or another like `Dismiss Alert`. + `Handle Alert` has better support for controlling should the alert + be accepted, dismissed, or left open. """ - if is_truthy(dismiss): - return self._handle_alert(self.DISMISS_ALERT) - else: - return self._handle_alert() + action = self.DISMISS if is_truthy(dismiss) else self.LEAVE + return self.handle_alert(action) @keyword def dismiss_alert(self, accept=True): - """ Returns true if alert was confirmed, false if it was dismissed + """Deprecated. Use `Handle Alert` instead. - This keyword will fail if no alert is present. Note that - following keywords will fail unless the alert is - dismissed by this keyword or another like `Get Alert Message`. + Contrary to its name, this keyword accepts the alert by default + (i.e. presses ``Ok``). ``accept`` can be set to a false value + to dismiss the alert (i.e. to press ``Cancel``). + + `Handle Alert` has better support for controlling should the alert + be accepted, dismissed, or left open. """ if is_truthy(accept): - self._handle_alert(self.ACCEPT_ALERT) + self.handle_alert(self.ACCEPT) return True - self._handle_alert(self.DISMISS_ALERT) + self.handle_alert(self.DISMISS) return False - def _handle_alert(self, dismiss_type=None): - """Alert re-try for Chrome + @keyword + def handle_alert(self, action=ACCEPT): + """Handles the current alert and returns its message. + + By default the alert is accepted, but this can be controlled + with the ``action`` argument that supports the following + case-insensitive values: + + - ``ACCEPT``: Accept the alert i.e. press ``Ok``. Default. + - ``DISMISS``: Dismiss the alert i.e. press ``Cancel``. + - ``LEAVE``: Leave the alert open. - Because Chrome has difficulties to handle alerts, like:: + Notice that alerts must be closed using this or some other keyword + before further actions can be done on the page. - alert.text - alert.dismiss + Examples: + | Handle Alert | | | # Accept alert. | + | Handle Alert | action=DISMISS | | # Dismiss alert. | + | ${message} = | Handle Alert | | # Accept alert and get its message. | + | ${message} = | Handle Alert | LEAVE | # Leave alert open and get its message. | - This function creates a re-try functionality to better support - alerts in Chrome. + New in SeleniumLibrary 3.0. """ - retry = 0 - while retry < 4: - try: - return self._alert_worker(dismiss_type) - except WebDriverException: - time.sleep(0.2) - retry += 1 - raise RuntimeError('There were no alerts') - - def _alert_worker(self, dismiss_type=None): alert = self._wait_alert() + return self._handle_alert(alert, action) + + def _handle_alert(self, alert, action): + action = action.upper() text = ' '.join(alert.text.splitlines()) - if dismiss_type == self.DISMISS_ALERT: - alert.dismiss() - elif dismiss_type == self.ACCEPT_ALERT: + if action == self.ACCEPT: alert.accept() + elif action == self.DISMISS: + alert.dismiss() + elif action != self.LEAVE: + raise ValueError("Invalid alert action '%s'." % action) return text - def _wait_alert(self): - return WebDriverWait(self.browser, 1).until(EC.alert_is_present()) + def _wait_alert(self, timeout=5): + wait = WebDriverWait(self.browser, timeout) + try: + return wait.until(EC.alert_is_present()) + except WebDriverException: + raise AssertionError('Expected alert not present.') diff --git a/test/acceptance/keywords/alerts.robot b/test/acceptance/keywords/alerts.robot index dca5084d2..faf92de92 100644 --- a/test/acceptance/keywords/alerts.robot +++ b/test/acceptance/keywords/alerts.robot @@ -4,7 +4,63 @@ Test Setup Go To Page "javascript/alert.html" Resource ../resource.robot *** Test Cases *** +Handle Alert accepts alert by default + [Setup] Go To Page "javascript/dynamic_content.html" + Click Button Change the title + Handle Alert + Alert Should Not Be Present + Title Should Be Original Changed! + +Handle Alert can dismiss + [Setup] Go To Page "javascript/dynamic_content.html" + Click Button Change the title + Handle Alert action=DISMISS + Alert Should Not Be Present + Title Should Be Original + +Handle Alert can leave open + Click Link Click Me! + Handle Alert Leave + Alert Should Be Present + +Handler Alert with invalid action + Click Link Click Me! + Run Keyword And Expect Error + ... ValueError: Invalid alert action 'INVALID'. + ... Handle Alert INVALID + Alert Should Be Present + +Handle Alert returns message + Click Link Click Me! + ${message} = Handle Alert + Should Be Equal ${message} ALERT! + Click Link Click Me Too! + ${message} = Handle Alert action=LEAVE + Should Be Equal ${message} MULTILINE ALERT! + Alert Should Be Present + +Alert Should Not Be Present + Alert Should Not Be Present + Click Link Click Me! + Run Keyword And Expect Error + ... Alert with message 'ALERT!' present. + ... Alert Should Not Be Present + +Alert Should Not Be Present with custom actions + [Setup] Go To Page "javascript/dynamic_content.html" + Click Button Change the title + Run Keyword And Expect Error + ... Alert with message 'Really change the title?' present. + ... Alert Should Not Be Present action=LEAVE + Run Keyword And Expect Error + ... Alert with message 'Really change the title?' present. + ... Alert Should Not Be Present action=DISmiss + Title Should Be Original + Alert Should Be Present + Run Keyword And Expect Error + ... Expected alert not present. + ... Alert Should Be Present Click Link Click Me! Alert Should Be Present @@ -15,15 +71,31 @@ Alert Should Be Present with message validation Alert Should Be Present MULTILINE ALERT! Click Link Click Me! Run Keyword And Expect Error - ... Alert text should have been 'foo bar' but was 'ALERT!' + ... Alert message should have been 'foo bar' but it was 'ALERT!'. ... Alert Should Be Present foo bar -Alert Should Be Present when there is none - Run Keyword And Expect Error - ... There were no alerts - ... Alert Should Be Present +Alert Should Be Present accepts by default + [Setup] Go To Page "javascript/dynamic_content.html" + Click Button Change the title + Alert Should Be Present Really change the title? + Title Should Be Original Changed! + Alert Should Not Be Present + +Alert Should Be Present can dismiss + [Setup] Go To Page "javascript/dynamic_content.html" + Click Button Change the title + Alert Should Be Present Really change the title? action=DISMISS + Title Should Be Original + Alert Should Not Be Present + +Alert Should Be Present can leave alert open + [Setup] Go To Page "javascript/dynamic_content.html" + Click Button Change the title + Alert Should Be Present action=LEAVE + Alert Should Be Present Get Alert Message + [Documentation] DEPRECATED! Click Link Click Me! ${msg} = Get Alert Message Should Be Equal ${msg} ALERT! @@ -31,17 +103,46 @@ Get Alert Message ${msg} = Get Alert Message Should Be Equal ${msg} MULTILINE ALERT! Run Keyword And Expect Error - ... There were no alerts + ... Expected alert not present. ... Get Alert Message +Get Alet Message dismisses by default + [Setup] Go To Page "javascript/dynamic_content.html" + Click Button Change the title + ${msg} = Get Alert Message + Should Be Equal ${msg} Really change the title? + Title Should Be Original + Get Alert Message can leave alert open + [Documentation] DEPRECATED! Click Link Click Me! ${msg} = Get Alert Message ${FALSE} Should Be Equal ${msg} ALERT! Alert Should Be Present +Input Text Into Alert + [Setup] Go To Page "javascript/alert_prompt.html" + Click Button css=button + Input Text Into Alert Robot + Alert Should Not Be Present + Page Should Contain Hello Robot! How are you today? + +Input Text Into Alert can leave alert open + [Setup] Go To Page "javascript/alert_prompt.html" + Click Button css=button + Input Text Into Alert Robot action=LEAVE + Alert Should Be Present + Page Should Contain Hello Robot! How are you today? + +Input Text Into Alert can dismiss + [Setup] Go To Page "javascript/alert_prompt.html" + Click Button css=button + Input Text Into Alert Robot action=DISMISS + Alert Should Not Be Present + Page Should Not Contain Robot + Input Text Into Prompt - [Documentation] Always leaves the alert open + [Documentation] DEPRECATED! Always leaves the alert open. [Setup] Go To Page "javascript/alert_prompt.html" Click Button css=button Input Text Into Prompt Robot @@ -49,6 +150,7 @@ Input Text Into Prompt Page Should Contain Hello Robot! How are you today? Confirm Action + [Documentation] DEPRECATED! [Setup] Go To Page "javascript/dynamic_content.html" Click Button Change the title ${msg}= Confirm Action @@ -56,32 +158,35 @@ Confirm Action Title Should Be Original Changed! Confirm Action multiple times + [Documentation] DEPRECATED! [Setup] Go To Page "javascript/alert_prompt.html" Click Button css=button - Input Text Into Prompt Robot # Leaves the alert open + Input Text Into Alert Robot action=LEAVE Confirm Action Page Should Contain Hello Robot! How are you today? Click Button css=button - Input Text Into Prompt Mr. Robot + Input Text Into Alert Mr. Roboto action=LEAVE Confirm Action - Page Should Contain Hello Mr. Robot! How are you today? + Page Should Contain Hello Mr. Roboto! How are you today? Cancel Action + [Documentation] DEPRECATED! [Setup] Go To Page "javascript/alert_prompt.html" Choose Cancel On Next Confirmation Click Button css=button - Input Text Into Prompt Robot # Leaves the alert open + Input Text Into Alert Robot action=LEAVE Confirm Action Page Should Not Contain Robot Dismiss Alert + [Documentation] DEPRECATED! [Setup] Go To Page "javascript/dynamic_content.html" Click Button Change the title ${accepted} = Dismiss Alert # This actually accepts the alert Should Be Equal ${accepted} ${TRUE} Title Should Be Original Changed! Click Button Change the title - ${accepted} = Dismiss Alert ${FALSE} # and this dismisses.... + ${accepted} = Dismiss Alert accept=${FALSE} Title Should Be Original Changed! Should Be Equal ${accepted} ${FALSE} Click Button Change the title diff --git a/test/unit/keywords/test_keyword_arguments_alert.py b/test/unit/keywords/test_keyword_arguments_alert.py index a96e1c053..a9b044858 100644 --- a/test/unit/keywords/test_keyword_arguments_alert.py +++ b/test/unit/keywords/test_keyword_arguments_alert.py @@ -7,6 +7,9 @@ TRUES = ['True', True, '1', 1, 'text'] FALSES = ['False', False, '', None, 'NONE'] +ACCEPT = AlertKeywords.ACCEPT +DISMISS = AlertKeywords.DISMISS +LEAVE = AlertKeywords.LEAVE class KeywordArgumentsAlertTest(unittest.TestCase): @@ -19,37 +22,37 @@ def tearDown(self): unstub() def test_get_alert_message_dismiss_true(self): - when(AlertKeywords)._handle_alert('dismiss').thenReturn('text') + when(AlertKeywords).handle_alert(DISMISS).thenReturn('text') count = 1 for true in TRUES: self.alert.get_alert_message(true) - verify(AlertKeywords, times=count)._handle_alert('dismiss') + verify(AlertKeywords, times=count).handle_alert(DISMISS) count += 1 self.alert.get_alert_message() - verify(AlertKeywords, times=count)._handle_alert('dismiss') + verify(AlertKeywords, times=count).handle_alert(DISMISS) def test_get_alert_message_dismiss_false(self): - when(AlertKeywords)._handle_alert().thenReturn('text') + when(AlertKeywords).handle_alert(LEAVE).thenReturn('text') count = 1 for false in FALSES: self.alert.get_alert_message(false) - verify(AlertKeywords, times=count)._handle_alert() + verify(AlertKeywords, times=count).handle_alert(LEAVE) count += 1 def test_dismiss_alert_true(self): - when(AlertKeywords)._handle_alert('accept').thenReturn('text') + when(AlertKeywords).handle_alert(ACCEPT).thenReturn('text') count = 1 for true in TRUES: self.alert.dismiss_alert(true) - verify(AlertKeywords, times=count)._handle_alert('accept') + verify(AlertKeywords, times=count).handle_alert(ACCEPT) count += 1 self.alert.dismiss_alert() - verify(AlertKeywords, times=count)._handle_alert('accept') + verify(AlertKeywords, times=count).handle_alert(ACCEPT) def test_dismiss_alert_false(self): - when(AlertKeywords)._handle_alert('dismiss').thenReturn('text') + when(AlertKeywords).handle_alert(DISMISS).thenReturn('text') count = 1 for false in FALSES: self.alert.dismiss_alert(false) - verify(AlertKeywords, times=count)._handle_alert('dismiss') + verify(AlertKeywords, times=count).handle_alert(DISMISS) count += 1 From 5b141f33dce59bdf9e124107b14f16a842d94431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pekka=20Kl=C3=A4rck?= Date: Tue, 3 Oct 2017 22:38:21 +0300 Subject: [PATCH 04/12] Add utils.is_noney() Also cleanup is_truthy/falsy tests. --- src/SeleniumLibrary/utils/__init__.py | 2 +- src/SeleniumLibrary/utils/types.py | 4 +++ test/unit/utils/test_types.py | 39 +++++++++++++-------------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/SeleniumLibrary/utils/__init__.py b/src/SeleniumLibrary/utils/__init__.py index f827f0b1d..377f9d42d 100644 --- a/src/SeleniumLibrary/utils/__init__.py +++ b/src/SeleniumLibrary/utils/__init__.py @@ -20,7 +20,7 @@ from .deprecated import Deprecated from .librarylistener import LibraryListener from .seleniumversion import SELENIUM_VERSION -from .types import is_string, is_truthy, is_falsy +from .types import is_falsy, is_noney, is_string, is_truthy def escape_xpath_value(value): diff --git a/src/SeleniumLibrary/utils/types.py b/src/SeleniumLibrary/utils/types.py index 86e1890fd..266c55a49 100644 --- a/src/SeleniumLibrary/utils/types.py +++ b/src/SeleniumLibrary/utils/types.py @@ -37,3 +37,7 @@ def is_truthy(item): def is_falsy(item): return not is_truthy(item) + + +def is_noney(item): + return item is None or is_string(item) and item.upper() == 'NONE' diff --git a/test/unit/utils/test_types.py b/test/unit/utils/test_types.py index 0cca4fbfb..b7b976214 100644 --- a/test/unit/utils/test_types.py +++ b/test/unit/utils/test_types.py @@ -1,6 +1,6 @@ import unittest -from SeleniumLibrary.utils import is_string, is_truthy, is_falsy +from SeleniumLibrary.utils import is_string, is_truthy, is_falsy, is_noney class IsstringTests(unittest.TestCase): @@ -16,27 +16,24 @@ def test_is_not_string(self): self.assertFalse(is_string(item)) -class IsTruthyTests(unittest.TestCase): +class IsTruthyFalsyNoneyTests(unittest.TestCase): + truthy = ['0', 'foo', ' ', 1, 2.3, True, [1], 'True', {'k': 'v'}] + falsy = [0, False, None, [], {}, (), u'', '', 'False', 'None'] def test_is_truthy(self): - truthys = ['1', 'foo', ' ', 1, 23.45, True, [1, 2], 'True', {'k': 'v'}] - for item in truthys: - self.assertTrue(is_truthy(item)) - - def test_is_not_truthy(self): - not_truthys = [0, False, None, [], {}, u'', '', 'False', 'None'] - for item in not_truthys: - self.assertFalse(is_truthy(item)) - - -class IsFalsyTest(unittest.TestCase): + for item in self.truthy: + self.assertTrue(is_truthy(item) is True) + for item in self.falsy: + self.assertTrue(is_truthy(item) is False) def test_is_falsy(self): - truthys = ['1', 'foo', ' ', 1, 23.45, True, [1, 2], 'True', {'k': 'v'}] - for item in truthys: - self.assertFalse(is_falsy(item)) - - def test_is_not_falsy(self): - not_truthys = [0, False, None, [], {}, u'', '', 'False', 'None'] - for item in not_truthys: - self.assertTrue(is_falsy(item)) + for item in self.truthy: + self.assertTrue(is_falsy(item) is False) + for item in self.falsy: + self.assertTrue(is_falsy(item) is True) + + def test_is_noney(self): + for item in [None, 'None', 'NONE', 'none']: + self.assertTrue(is_noney(item) is True) + for item in self.truthy + [False, 0, 'False', '', [], {}, ()]: + self.assertTrue(is_noney(item) is False) From 49e8351ac1e3e03c782d189bc7f30ec416f6e874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pekka=20Kl=C3=A4rck?= Date: Tue, 3 Oct 2017 23:11:07 +0300 Subject: [PATCH 05/12] Fix waiting timeout given as integer zero --- src/SeleniumLibrary/base/librarycomponent.py | 7 +++++++ src/SeleniumLibrary/keywords/waiting.py | 6 ++---- test/acceptance/keywords/waiting.robot | 19 +++++++++---------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/SeleniumLibrary/base/librarycomponent.py b/src/SeleniumLibrary/base/librarycomponent.py index b51ef3831..1273fe366 100644 --- a/src/SeleniumLibrary/base/librarycomponent.py +++ b/src/SeleniumLibrary/base/librarycomponent.py @@ -19,6 +19,8 @@ from robot.api import logger from robot.libraries.BuiltIn import BuiltIn, RobotNotRunningError +from SeleniumLibrary.utils import is_noney, timestr_to_secs + from .context import ContextAware from .robotlibcore import PY2 @@ -54,6 +56,11 @@ def assert_page_not_contains(self, locator, tag=None, message=None, self.element_finder.assert_page_not_contains(locator, tag, message, loglevel) + def get_timeout(self, timeout=None): + if is_noney(timeout): + return self.ctx.timeout + return timestr_to_secs(timeout) + @property def log_dir(self): try: diff --git a/src/SeleniumLibrary/keywords/waiting.py b/src/SeleniumLibrary/keywords/waiting.py index 38d6a5a09..5afdfb31f 100644 --- a/src/SeleniumLibrary/keywords/waiting.py +++ b/src/SeleniumLibrary/keywords/waiting.py @@ -263,8 +263,7 @@ def wait_func(): self._wait_until_no_error(timeout, wait_func) def _wait_until_no_error(self, timeout, wait_func, *args): - timeout = timestr_to_secs(timeout) if is_truthy(timeout) else self.ctx.timeout - maxtime = time.time() + timeout + maxtime = time.time() + self.get_timeout(timeout) while True: timeout_error = wait_func(*args) if not timeout_error: @@ -274,5 +273,4 @@ def _wait_until_no_error(self, timeout, wait_func, *args): time.sleep(0.2) def _format_timeout(self, timeout): - timeout = timestr_to_secs(timeout) if is_truthy(timeout) else self.ctx.timeout - return secs_to_timestr(timeout) + return secs_to_timestr(self.get_timeout(timeout)) diff --git a/test/acceptance/keywords/waiting.robot b/test/acceptance/keywords/waiting.robot index 69c4fa7fc..33c80f32f 100644 --- a/test/acceptance/keywords/waiting.robot +++ b/test/acceptance/keywords/waiting.robot @@ -1,12 +1,10 @@ *** Settings *** -Documentation Tests waiting Test Setup Go To Page "javascript/delayed_events.html" Resource ../resource.robot Force Tags Known Issue Internet Explorer *** Test Cases *** Wait For Condition - [Documentation] Wait For Condition Title Should Be Original Wait For Condition return window.document.title == "Changed" Run Keyword And Expect Error @@ -14,13 +12,11 @@ Wait For Condition ... Wait For Condition return window.document.title == "Invalid" ${0.1} Wait Until Page Contains - [Documentation] Wait Until Page Contains Wait Until Page Contains New Content 2 s Run Keyword And Expect Error Text 'invalid' did not appear in 100 milliseconds ... Wait Until Page Contains invalid 0.1 Wait Until Page Does Not Contain - [Documentation] Wait Until Page Does Not Contain Wait Until Page Does Not Contain This is content 2 s Run Keyword And Expect Error Text 'Initially hidden' did not disappear in 100 milliseconds ... Wait Until Page Does Not Contain Initially hidden 0.1 @@ -32,14 +28,12 @@ Wait Until Page Contains Element ... Wait Until Page Contains Element %cnon-existent 0.1 seconds Wait Until Page Does Not Contain Element - [Documentation] Tests also that format characters (e.g. %c) are handled correctly in error - ... messages + [Documentation] Tests also that format characters (e.g. %c) are handled correctly in error messages Wait Until Page Does Not Contain Element not_present 2 seconds Run Keyword And Expect Error Element 'content' did not disappear in 100 milliseconds ... Wait Until Page Does Not Contain Element content 0.1 seconds Wait Until Element Is Visible - [Documentation] Wait Until Element Is Visible Run Keyword And Expect Error Element 'hidden' was not visible in 100 milliseconds ... Wait Until Element Is Visible hidden 0.1 Wait Until Element Is Visible hidden 2 s @@ -57,7 +51,6 @@ Wait Until Element Is Visible with locator only Wait Until Element Is Visible hidden Wait Until Element Is Enabled - [Documentation] Wait Until Element Is Enabled Run Keyword And Expect Error Element 'id=disabled' was not enabled in 100 milliseconds ... Wait Until Element Is Enabled id=disabled 0.1 Wait Until Element Is Enabled id=disabled 2 s @@ -68,7 +61,6 @@ Wait Until Element Is Enabled ... id=invalid 0.1 User error message Wait Until Element Contains - [Documentation] Wait Until Element Contains Run Keyword And Expect Error ... Text 'New' did not appear in 100 milliseconds to element 'id=content'. Its text was 'This is content'. ... Wait Until Element Contains id=content New 0.1 @@ -82,7 +74,6 @@ Wait Until Element Contains ... Wait Until Element Contains id=invalid content 0.1 Wait Until Element Does Not Contain - [Documentation] Wait Until Element Does Not Contain Run Keyword And Expect Error ... Text 'This is' did not disappear in 100 milliseconds from element 'id=content'. ... Wait Until Element Does Not Contain id=content This is 0.1 @@ -90,3 +81,11 @@ Wait Until Element Does Not Contain Wait Until Element Does Not Contain id=content content 2 s Run Keyword And Expect Error User error message Wait Until Element Does Not Contain ... content New Content 0.1 User error message + +Timeout can be zero + Run Keyword And Expect Error + ... Text 'New Content' did not appear in 0 seconds to element 'content'. Its text was 'This is content'. + ... Wait Until Element Contains content New Content 0 + Run Keyword And Expect Error + ... Text 'New Content' did not appear in 0 seconds to element 'content'. Its text was 'This is content'. + ... Wait Until Element Contains content New Content ${0} From 393c41eff9bee4076400b13fdd21f7460c2bee4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pekka=20Kl=C3=A4rck?= Date: Wed, 4 Oct 2017 00:32:38 +0300 Subject: [PATCH 06/12] Add timeout support for alerts. --- src/SeleniumLibrary/__init__.py | 18 +++++---- src/SeleniumLibrary/keywords/alert.py | 47 +++++++++++++++-------- test/acceptance/keywords/alerts.robot | 41 ++++++++++++++++++-- test/acceptance/resource.robot | 8 ++++ test/resources/html/javascript/alert.html | 3 +- 5 files changed, 90 insertions(+), 27 deletions(-) diff --git a/src/SeleniumLibrary/__init__.py b/src/SeleniumLibrary/__init__.py index 25ed77023..1a7f13cb8 100644 --- a/src/SeleniumLibrary/__init__.py +++ b/src/SeleniumLibrary/__init__.py @@ -210,17 +210,17 @@ class SeleniumLibrary(DynamicCore): It also explains the `time format` that can be used when setting various timeouts, waits and delays. - == Timeouts == + == Timeout == - SeleniumLibrary contains various ``Wait ...`` keywords that can be used - to wait, for example, for a dynamically created elements to appear on - a page. All these keywords accept an optional ``timeout`` argument that - tells the maximum time to wait. + SeleniumLibrary contains various keywords that have an optional + ``timeout`` argument that specifies how long these keywords should + wait for certain events or actions. These keywords include, for example, + ``Wait ...`` keywords and keywords related to alerts. The default timeout these keywords use can be set globally either by using the `Set Selenium Timeout` keyword or with the ``timeout`` argument - when `importing` the library. The same timeout also applies to the - `Execute Async Javascript` keyword. + when `importing` the library. See `time format` below for supported + timeout syntax. == Implicit wait == @@ -230,6 +230,8 @@ class SeleniumLibrary(DynamicCore): the library. See [http://seleniumhq.org/docs/04_webdriver_advanced.html| Selenium documentation] for more information about this functionality. + See `time format` below for supported syntax. + == Selenium speed == Selenium execution speed can be slowed down globally by using `Set @@ -238,6 +240,8 @@ class SeleniumLibrary(DynamicCore): appear on a page is not a good idea, and the above explained timeouts and waits should be used instead. + See `time format` below for supported syntax. + == Time format == All timeouts and waits can be given as numbers considered seconds diff --git a/src/SeleniumLibrary/keywords/alert.py b/src/SeleniumLibrary/keywords/alert.py index e0ee38e0f..c12da81a1 100644 --- a/src/SeleniumLibrary/keywords/alert.py +++ b/src/SeleniumLibrary/keywords/alert.py @@ -19,7 +19,7 @@ from selenium.webdriver.support.ui import WebDriverWait from SeleniumLibrary.base import keyword, LibraryComponent -from SeleniumLibrary.utils import is_truthy +from SeleniumLibrary.utils import is_truthy, secs_to_timestr, timestr_to_secs class AlertKeywords(LibraryComponent): @@ -38,20 +38,23 @@ def input_text_into_prompt(self, text): self.input_text_into_alert(text, self.LEAVE) @keyword - def input_text_into_alert(self, text, action=ACCEPT): + def input_text_into_alert(self, text, action=ACCEPT, timeout=None): """Types the given ``text`` into an input field in an alert. The alert is accepted by default, but that behavior can be controlled by using the ``action`` argument same way as with `Handle Alert`. + ``timeout`` specifies how long to wait for the alert to appear. + If it is not given, the global default `timeout` is used instead. + New in SeleniumLibrary 3.0. """ - alert = self._wait_alert() + alert = self._wait_alert(timeout) alert.send_keys(text) self._handle_alert(alert, action) @keyword - def alert_should_be_present(self, text='', action=ACCEPT): + def alert_should_be_present(self, text='', action=ACCEPT, timeout=None): """Verifies that an alert is present and, by default, accepts it. Fails if no alert is present. If ``text`` is a non-empty string, @@ -59,25 +62,34 @@ def alert_should_be_present(self, text='', action=ACCEPT): by default, but that behavior can be controlled by using the ``action`` argument same way as with `Handle Alert`. - The ``action`` argument is new in SeleniumLibrary 3.0. In earlier - versions the alert was always accepted. + ``timeout`` specifies how long to wait for the alert to appear. + If it is not given, the global default `timeout` is used instead. + + ``action`` and ``timeout`` arguments are new in SeleniumLibrary 3.0. + In earlier versions the alert was always accepted and timeout was + hard coded to one second. """ - message = self.handle_alert(action) + message = self.handle_alert(action, timeout) if text and text != message: raise AssertionError("Alert message should have been '%s' but it " "was '%s'." % (text, message)) @keyword - def alert_should_not_be_present(self, action=ACCEPT): + def alert_should_not_be_present(self, action=ACCEPT, timeout=0): """Verifies that no alert is present. If the alert actually exists, ``action`` argument determines how it should be handled same way as with `Handle Alert`. + ``timeout`` specifies how long to wait for the alert to appear. + By default the alert is not waited at all, but a custom time can + be given if alert may be delayed. See the `time format` section + for information about the syntax. + New in SeleniumLibrary 3.0. """ try: - alert = self._wait_alert(timeout=0) + alert = self._wait_alert(timeout) except AssertionError: return text = self._handle_alert(alert, action) @@ -152,7 +164,7 @@ def dismiss_alert(self, accept=True): return False @keyword - def handle_alert(self, action=ACCEPT): + def handle_alert(self, action=ACCEPT, timeout=None): """Handles the current alert and returns its message. By default the alert is accepted, but this can be controlled @@ -163,18 +175,21 @@ def handle_alert(self, action=ACCEPT): - ``DISMISS``: Dismiss the alert i.e. press ``Cancel``. - ``LEAVE``: Leave the alert open. - Notice that alerts must be closed using this or some other keyword - before further actions can be done on the page. + The ``timeout`` argument specifies how long to wait for the alert + to appear. If it is not given, the global default `timeout` is used + instead. Examples: | Handle Alert | | | # Accept alert. | | Handle Alert | action=DISMISS | | # Dismiss alert. | + | Handle Alert | timeout=10 s | | # Use custom timeout and accept alert. | + | Handle Alert | DISMISS | 1 min | # Use custom timeout and dismiss alert. | | ${message} = | Handle Alert | | # Accept alert and get its message. | | ${message} = | Handle Alert | LEAVE | # Leave alert open and get its message. | New in SeleniumLibrary 3.0. """ - alert = self._wait_alert() + alert = self._wait_alert(timeout) return self._handle_alert(alert, action) def _handle_alert(self, alert, action): @@ -188,9 +203,11 @@ def _handle_alert(self, alert, action): raise ValueError("Invalid alert action '%s'." % action) return text - def _wait_alert(self, timeout=5): + def _wait_alert(self, timeout=None): + timeout = self.get_timeout(timeout) wait = WebDriverWait(self.browser, timeout) try: return wait.until(EC.alert_is_present()) except WebDriverException: - raise AssertionError('Expected alert not present.') + raise AssertionError('Alert not found in %s.' + % secs_to_timestr(timeout)) diff --git a/test/acceptance/keywords/alerts.robot b/test/acceptance/keywords/alerts.robot index faf92de92..763299c7c 100644 --- a/test/acceptance/keywords/alerts.robot +++ b/test/acceptance/keywords/alerts.robot @@ -1,10 +1,12 @@ *** Settings *** Force Tags Known Issue Safari +Suite Setup Set Global Timeout 1 second Test Setup Go To Page "javascript/alert.html" +Suite Teardown Restore Global Timeout Resource ../resource.robot *** Test Cases *** -Handle Alert accepts alert by default +Handle Alert accepts by default [Setup] Go To Page "javascript/dynamic_content.html" Click Button Change the title Handle Alert @@ -23,7 +25,7 @@ Handle Alert can leave open Handle Alert Leave Alert Should Be Present -Handler Alert with invalid action +Handle Alert with invalid action Click Link Click Me! Run Keyword And Expect Error ... ValueError: Invalid alert action 'INVALID'. @@ -39,6 +41,15 @@ Handle Alert returns message Should Be Equal ${message} MULTILINE ALERT! Alert Should Be Present +Handle Alert with custom timeout + Click Link Slow alert + Handle Alert timeout=1s + Click Link Slow alert + Run Keyword And Expect Error + ... Alert not found in 1 millisecond. + ... Handle Alert ACCEPT 1 ms + Handle Alert timeout=3.14 seconds + Alert Should Not Be Present Alert Should Not Be Present Click Link Click Me! @@ -57,9 +68,17 @@ Alert Should Not Be Present with custom actions ... Alert Should Not Be Present action=DISmiss Title Should Be Original +Alert Should Not Be Present with custom timeout + Alert Should Not Be Present timeout=0.1s + Click Link Slow alert + Alert Should Not Be Present DISMISS ${0.001} + Run Keyword And Expect Error + ... Alert with message 'Alert after 200ms!' present. + ... Alert Should Not Be Present timeout=0.2 + Alert Should Be Present Run Keyword And Expect Error - ... Expected alert not present. + ... Alert not found in 1 second. ... Alert Should Be Present Click Link Click Me! Alert Should Be Present @@ -94,6 +113,13 @@ Alert Should Be Present can leave alert open Alert Should Be Present action=LEAVE Alert Should Be Present +Alert Should Be Present with custom timeout + Click Link Slow alert + Run Keyword And Expect Error + ... Alert not found in 1 millisecond. + ... Alert Should Be Present timeout=1ms + Alert Should Be Present Alert after 200ms! ACCEPT 0.2s + Get Alert Message [Documentation] DEPRECATED! Click Link Click Me! @@ -103,10 +129,11 @@ Get Alert Message ${msg} = Get Alert Message Should Be Equal ${msg} MULTILINE ALERT! Run Keyword And Expect Error - ... Expected alert not present. + ... Alert not found in 1 second. ... Get Alert Message Get Alet Message dismisses by default + [Documentation] DEPRECATED! [Setup] Go To Page "javascript/dynamic_content.html" Click Button Change the title ${msg} = Get Alert Message @@ -141,6 +168,12 @@ Input Text Into Alert can dismiss Alert Should Not Be Present Page Should Not Contain Robot +Input Text Into Alert with custom timeout + [Setup] Go To Page "javascript/alert_prompt.html" + Run Keyword And Expect Error + ... Alert not found in 7 milliseconds. + ... Input Text Into Alert This is not found timeout=007ms + Input Text Into Prompt [Documentation] DEPRECATED! Always leaves the alert open. [Setup] Go To Page "javascript/alert_prompt.html" diff --git a/test/acceptance/resource.robot b/test/acceptance/resource.robot index 28e019d4a..882f8e96a 100644 --- a/test/acceptance/resource.robot +++ b/test/acceptance/resource.robot @@ -72,3 +72,11 @@ Set ${level} Loglevel Verify Location Is "${relative url}" [Documentation] Verifies location Wait Until Keyword Succeeds 5 1 Location Should Be ${ROOT}/${relative url} + +Set Global Timeout + [Arguments] ${timeout} + ${previous} = Set Selenium timeout ${timeout} + Set Suite Variable ${PREVIOUS TIMEOUT} ${previous} + +Restore Global Timeout + Set Selenium timeout ${PREVIOUS TIMEOUT} diff --git a/test/resources/html/javascript/alert.html b/test/resources/html/javascript/alert.html index bc0f14f32..b01040162 100644 --- a/test/resources/html/javascript/alert.html +++ b/test/resources/html/javascript/alert.html @@ -5,5 +5,6 @@

Click Me!

Click Me Too!

+

Slow alert

- \ No newline at end of file + From 97d6b7b15a20713e621b1f5ad41c9fdfdbf9e2d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pekka=20Kl=C3=A4rck?= Date: Wed, 4 Oct 2017 20:24:32 +0300 Subject: [PATCH 07/12] Firefox test fixes --- test/acceptance/keywords/alerts.robot | 14 +++++++------- test/resources/html/javascript/alert.html | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/acceptance/keywords/alerts.robot b/test/acceptance/keywords/alerts.robot index 763299c7c..036f33510 100644 --- a/test/acceptance/keywords/alerts.robot +++ b/test/acceptance/keywords/alerts.robot @@ -42,9 +42,9 @@ Handle Alert returns message Alert Should Be Present Handle Alert with custom timeout - Click Link Slow alert + Click Button Slow alert Handle Alert timeout=1s - Click Link Slow alert + Click Button Slow alert Run Keyword And Expect Error ... Alert not found in 1 millisecond. ... Handle Alert ACCEPT 1 ms @@ -70,11 +70,11 @@ Alert Should Not Be Present with custom actions Alert Should Not Be Present with custom timeout Alert Should Not Be Present timeout=0.1s - Click Link Slow alert + Click Button Slow alert Alert Should Not Be Present DISMISS ${0.001} Run Keyword And Expect Error - ... Alert with message 'Alert after 200ms!' present. - ... Alert Should Not Be Present timeout=0.2 + ... Alert with message 'Alert after 500ms!' present. + ... Alert Should Not Be Present timeout=0.99999 Alert Should Be Present Run Keyword And Expect Error @@ -114,11 +114,11 @@ Alert Should Be Present can leave alert open Alert Should Be Present Alert Should Be Present with custom timeout - Click Link Slow alert + Click Button Slow alert Run Keyword And Expect Error ... Alert not found in 1 millisecond. ... Alert Should Be Present timeout=1ms - Alert Should Be Present Alert after 200ms! ACCEPT 0.2s + Alert Should Be Present Alert after 500ms! ACCEPT 3s Get Alert Message [Documentation] DEPRECATED! diff --git a/test/resources/html/javascript/alert.html b/test/resources/html/javascript/alert.html index b01040162..0913271ed 100644 --- a/test/resources/html/javascript/alert.html +++ b/test/resources/html/javascript/alert.html @@ -5,6 +5,6 @@

Click Me!

Click Me Too!

-

Slow alert

+

From 1bd934585a684e1af0d51fa5f553977f4db95a90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pekka=20Kl=C3=A4rck?= Date: Thu, 5 Oct 2017 10:39:00 +0300 Subject: [PATCH 08/12] Try to make tests more stable with implicit wait --- test/acceptance/resource.robot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/acceptance/resource.robot b/test/acceptance/resource.robot index 882f8e96a..fc17d44ff 100644 --- a/test/acceptance/resource.robot +++ b/test/acceptance/resource.robot @@ -1,5 +1,5 @@ *** Setting *** -Library SeleniumLibrary run_on_failure=Nothing implicit_wait=0 +Library SeleniumLibrary run_on_failure=Nothing implicit_wait=0.2 seconds Library Collections Library OperatingSystem From 7a3b896ee53efe220f61a2680496e0dcfee6c269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pekka=20Kl=C3=A4rck?= Date: Thu, 5 Oct 2017 10:43:19 +0300 Subject: [PATCH 09/12] Doc tuning based on review comment --- src/SeleniumLibrary/keywords/alert.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/SeleniumLibrary/keywords/alert.py b/src/SeleniumLibrary/keywords/alert.py index c12da81a1..f64ab5c35 100644 --- a/src/SeleniumLibrary/keywords/alert.py +++ b/src/SeleniumLibrary/keywords/alert.py @@ -78,8 +78,10 @@ def alert_should_be_present(self, text='', action=ACCEPT, timeout=None): def alert_should_not_be_present(self, action=ACCEPT, timeout=0): """Verifies that no alert is present. - If the alert actually exists, ``action`` argument determines - how it should be handled same way as with `Handle Alert`. + If the alert actually exists, the ``action`` argument determines + how it should be handled. By default the alert is accepted, but + it can be also dismissed or left open the same way as with the + `Handle Alert` keyword. ``timeout`` specifies how long to wait for the alert to appear. By default the alert is not waited at all, but a custom time can From f9c5ae78891f331bcb266e09449bb73c831f2ce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pekka=20Kl=C3=A4rck?= Date: Thu, 5 Oct 2017 11:26:35 +0300 Subject: [PATCH 10/12] Yet another attempt to make alert tests pass w/ Firefox on CI --- test/acceptance/keywords/alerts.robot | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/test/acceptance/keywords/alerts.robot b/test/acceptance/keywords/alerts.robot index 036f33510..316618bcc 100644 --- a/test/acceptance/keywords/alerts.robot +++ b/test/acceptance/keywords/alerts.robot @@ -11,14 +11,14 @@ Handle Alert accepts by default Click Button Change the title Handle Alert Alert Should Not Be Present - Title Should Be Original Changed! + Wait For Title Change Original Changed! Handle Alert can dismiss [Setup] Go To Page "javascript/dynamic_content.html" Click Button Change the title Handle Alert action=DISMISS Alert Should Not Be Present - Title Should Be Original + Wait For Title Change Original Handle Alert can leave open Click Link Click Me! @@ -66,7 +66,7 @@ Alert Should Not Be Present with custom actions Run Keyword And Expect Error ... Alert with message 'Really change the title?' present. ... Alert Should Not Be Present action=DISmiss - Title Should Be Original + Wait For Title Change Original Alert Should Not Be Present with custom timeout Alert Should Not Be Present timeout=0.1s @@ -97,14 +97,14 @@ Alert Should Be Present accepts by default [Setup] Go To Page "javascript/dynamic_content.html" Click Button Change the title Alert Should Be Present Really change the title? - Title Should Be Original Changed! + Wait For Title Change Original Changed! Alert Should Not Be Present Alert Should Be Present can dismiss [Setup] Go To Page "javascript/dynamic_content.html" Click Button Change the title Alert Should Be Present Really change the title? action=DISMISS - Title Should Be Original + Wait For Title Change Original Alert Should Not Be Present Alert Should Be Present can leave alert open @@ -138,7 +138,7 @@ Get Alet Message dismisses by default Click Button Change the title ${msg} = Get Alert Message Should Be Equal ${msg} Really change the title? - Title Should Be Original + Wait For Title Change Original Get Alert Message can leave alert open [Documentation] DEPRECATED! @@ -188,7 +188,7 @@ Confirm Action Click Button Change the title ${msg}= Confirm Action Should Be Equal ${msg} Really change the title? - Title Should Be Original Changed! + Wait For Title Change Original Changed! Confirm Action multiple times [Documentation] DEPRECATED! @@ -217,11 +217,16 @@ Dismiss Alert Click Button Change the title ${accepted} = Dismiss Alert # This actually accepts the alert Should Be Equal ${accepted} ${TRUE} - Title Should Be Original Changed! + Wait For Title Change Original Changed! Click Button Change the title ${accepted} = Dismiss Alert accept=${FALSE} - Title Should Be Original Changed! + Wait For Title Change Original Changed! Should Be Equal ${accepted} ${FALSE} Click Button Change the title Dismiss Alert true - Title Should Be Original Changed! Changed! + Wait For Title Change Original Changed! Changed! + +*** Keywords *** +Wait For Title Change + [Arguments] ${expected} + Wait For Condition return document.title == '${expected}' From 861c5482ce21198bbc87d6424141d5e511d16a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pekka=20Kl=C3=A4rck?= Date: Thu, 5 Oct 2017 11:38:09 +0300 Subject: [PATCH 11/12] Run tests with --dotted on CI when supported. This makes it a lot easier to see failed tests. Would have changed run_tests.py, but we still want to support RF 2.8 where --dotted isn't supported. --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index a6d96d2d3..7e1299ec1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,13 +35,13 @@ matrix: - BROWSER=chrome - SELENIUM=3.5.0 - ROBOTFRAMEWORK=3.0.2 - - ROBOT_OPTIONS= + - ROBOT_OPTIONS=--dotted - python: "2.7" env: - BROWSER=chrome - SELENIUM=2.53.6 - ROBOTFRAMEWORK=2.9.2 - - ROBOT_OPTIONS= + - ROBOT_OPTIONS=--dotted - python: "2.7" env: - BROWSER=chrome @@ -53,13 +53,13 @@ matrix: - BROWSER=chrome - SELENIUM=2.53.6 - ROBOTFRAMEWORK=3.0.2 - - ROBOT_OPTIONS= + - ROBOT_OPTIONS=--dotted - python: "3.3" env: - BROWSER=firefox - SELENIUM=3.5.0 - ROBOTFRAMEWORK=3.0.2 - - ROBOT_OPTIONS="--exclude Known_Issue_Firefox" + - ROBOT_OPTIONS="--exclude Known_Issue_Firefox --dotted" before_script: - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" From 3c7e79d3e6ccac6d9bb4ec61dd98e1d635d50401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pekka=20Kl=C3=A4rck?= Date: Thu, 5 Oct 2017 12:10:33 +0300 Subject: [PATCH 12/12] run_test: Use given opts only w/ Robot, not w/ Rebot --- test/run_tests.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/run_tests.py b/test/run_tests.py index b622d3c74..c85787205 100755 --- a/test/run_tests.py +++ b/test/run_tests.py @@ -97,7 +97,7 @@ def acceptance_tests(interpreter, browser, rf_options=[], with http_server(): execute_tests(interpreter, browser, rf_options, sauce_username, sauce_key) - failures = process_output(browser, rf_options) + failures = process_output(browser) if failures: print('\n{} critical test{} failed.' .format(failures, 's' if failures != 1 else '')) @@ -162,13 +162,12 @@ def get_sauce_conf(browser, sauce_username, sauce_key): ] -def process_output(browser, rf_options): +def process_output(browser): print('Verifying results...') options = [] output = os.path.join(RESULTS_DIR, 'output.xml') robotstatuschecker.process_output(output, verbose=False) options.extend([opt.format(browser=browser) for opt in REBOT_OPTIONS]) - options += rf_options try: rebot_cli(options + [output]) except SystemExit as exit: