From 1663ca3408b7077ef1b8fea0cd85697004f384bc Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Tue, 6 Sep 2016 00:06:25 +0300 Subject: [PATCH 1/6] Improves alert hadling for Chrome (#652) In Chrome is often a random WebDriverException which caused by unknow reson. This commit creates a retry functionality that improves alert handling for Crome. Fixes #652 --- src/Selenium2Library/keywords/_alert.py | 69 +++++++++++++------------ 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/src/Selenium2Library/keywords/_alert.py b/src/Selenium2Library/keywords/_alert.py index f7a245f1f..e511864ec 100644 --- a/src/Selenium2Library/keywords/_alert.py +++ b/src/Selenium2Library/keywords/_alert.py @@ -1,3 +1,4 @@ +from time import sleep from selenium.common.exceptions import WebDriverException from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC @@ -30,7 +31,7 @@ def alert_should_be_present(self, text=''): will fail unless the alert is dismissed by this keyword or another like `Get Alert Message`. """ - alert_text = self.get_alert_message() + alert_text = self._handle_alert('accept') if text and alert_text != text: raise AssertionError("Alert text should have been " "'%s' but was '%s'" @@ -74,7 +75,10 @@ def confirm_action(self): | Click Button | Send | # Shows a confirmation dialog | | Confirm Action | | # Chooses Cancel | """ - text = self._close_alert(not self._cancel_on_next_confirmation) + if self._cancel_on_next_confirmation: + text = self._handle_alert('dismiss') + else: + text = self._handle_alert('accept') self._cancel_on_next_confirmation = False return text @@ -87,9 +91,9 @@ def get_alert_message(self, dismiss=True): dismissed by this keyword or another like `Get Alert Message`. """ if dismiss: - return self._close_alert() + return self._handle_alert('dismiss') else: - return self._read_alert() + return self._handle_alert() def dismiss_alert(self, accept=True): """ Returns true if alert was confirmed, false if it was dismissed @@ -98,38 +102,39 @@ def dismiss_alert(self, accept=True): following keywords will fail unless the alert is dismissed by this keyword or another like `Get Alert Message`. """ - return self._handle_alert(accept) + if accept: + return self._handle_alert('accept') + else: + return self._handle_alert() - # Private + def _handle_alert(self, dismiss_type=None): + """Alert re-try for Chrome - def _close_alert(self, confirm=True): - try: - text = self._read_alert() - self._handle_alert(confirm) - return text - except WebDriverException: - raise RuntimeError('There were no alerts') + Because Chrome has difficulties to handle alerts, like:: - def _read_alert(self): - alert = None - try: - alert = self._wait_alert() - # collapse new lines chars - return ' '.join(alert.text.splitlines()) - except WebDriverException: - raise RuntimeError('There were no alerts') + alert.text + alert.dismiss - def _handle_alert(self, confirm=True): - try: - alert = self._wait_alert() - if not confirm: - alert.dismiss() - return False - else: - alert.accept() - return True - except WebDriverException: - raise RuntimeError('There were no alerts') + This function creates a re-try funtionality to better support + alerts in Chrome. + """ + retry = 0 + while retry < 4: + try: + return self._alert_worker(dismiss_type) + except WebDriverException: + sleep(0.2) + retry += 1 + raise RuntimeError('There were no alerts') + + def _alert_worker(self, dismiss_type=None): + alert = self._wait_alert() + text = ' '.join(alert.text.splitlines()) + if dismiss_type == 'dismiss': + alert.dismiss() + elif dismiss_type == 'accept': + alert.accept() + return text def _wait_alert(self): return WebDriverWait(self._current_browser(), 1).until( From 674bc4dbd76b9c43534bd6f523de90ed269d7fb9 Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Tue, 6 Sep 2016 22:52:28 +0300 Subject: [PATCH 2/6] Refactored the code to simplify the logic --- src/Selenium2Library/keywords/_alert.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/Selenium2Library/keywords/_alert.py b/src/Selenium2Library/keywords/_alert.py index e511864ec..84207c4ee 100644 --- a/src/Selenium2Library/keywords/_alert.py +++ b/src/Selenium2Library/keywords/_alert.py @@ -8,7 +8,9 @@ class _AlertKeywords(KeywordGroup): def __init__(self): - self._cancel_on_next_confirmation = False + self._accept = 'accept' + self._dismiss = 'dismiss' + self._dismiss_on_next_confirmation = self._accept # Public @@ -31,7 +33,7 @@ def alert_should_be_present(self, text=''): will fail unless the alert is dismissed by this keyword or another like `Get Alert Message`. """ - alert_text = self._handle_alert('accept') + alert_text = self._handle_alert(self._accept) if text and alert_text != text: raise AssertionError("Alert text should have been " "'%s' but was '%s'" @@ -39,7 +41,7 @@ def alert_should_be_present(self, text=''): def choose_cancel_on_next_confirmation(self): """Cancel will be selected the next time `Confirm Action` is used.""" - self._cancel_on_next_confirmation = True + self._dismiss_on_next_confirmation = self._dismiss def choose_ok_on_next_confirmation(self): """Undo the effect of using keywords `Choose Cancel On Next Confirmation`. Note @@ -56,7 +58,7 @@ def choose_ok_on_next_confirmation(self): consume it by using a keywords such as `Get Alert Message`, or else the following selenium operations will fail. """ - self._cancel_on_next_confirmation = False + self._dismiss_on_next_confirmation = self._accept def confirm_action(self): """Dismisses currently shown confirmation dialog and returns it's message. @@ -75,11 +77,8 @@ def confirm_action(self): | Click Button | Send | # Shows a confirmation dialog | | Confirm Action | | # Chooses Cancel | """ - if self._cancel_on_next_confirmation: - text = self._handle_alert('dismiss') - else: - text = self._handle_alert('accept') - self._cancel_on_next_confirmation = False + text = self._handle_alert(self._dismiss_on_next_confirmation) + self._dismiss_on_next_confirmation = self._dismiss return text def get_alert_message(self, dismiss=True): @@ -91,7 +90,7 @@ def get_alert_message(self, dismiss=True): dismissed by this keyword or another like `Get Alert Message`. """ if dismiss: - return self._handle_alert('dismiss') + return self._handle_alert(self._dismiss) else: return self._handle_alert() @@ -103,7 +102,7 @@ def dismiss_alert(self, accept=True): dismissed by this keyword or another like `Get Alert Message`. """ if accept: - return self._handle_alert('accept') + return self._handle_alert(self._accept) else: return self._handle_alert() @@ -130,9 +129,9 @@ def _handle_alert(self, dismiss_type=None): def _alert_worker(self, dismiss_type=None): alert = self._wait_alert() text = ' '.join(alert.text.splitlines()) - if dismiss_type == 'dismiss': + if dismiss_type == self._dismiss: alert.dismiss() - elif dismiss_type == 'accept': + elif dismiss_type == self._accept: alert.accept() return text From 3ff647a25c49c7c9f6460cc072f435e07d7885cc Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Tue, 6 Sep 2016 23:25:24 +0300 Subject: [PATCH 3/6] Changed class attribute to better describe what it does --- src/Selenium2Library/keywords/_alert.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Selenium2Library/keywords/_alert.py b/src/Selenium2Library/keywords/_alert.py index 84207c4ee..ec7fb89b0 100644 --- a/src/Selenium2Library/keywords/_alert.py +++ b/src/Selenium2Library/keywords/_alert.py @@ -10,7 +10,7 @@ class _AlertKeywords(KeywordGroup): def __init__(self): self._accept = 'accept' self._dismiss = 'dismiss' - self._dismiss_on_next_confirmation = self._accept + self._next_alert_dismiss_type = self._accept # Public @@ -41,7 +41,7 @@ def alert_should_be_present(self, text=''): def choose_cancel_on_next_confirmation(self): """Cancel will be selected the next time `Confirm Action` is used.""" - self._dismiss_on_next_confirmation = self._dismiss + self._next_alert_dismiss_type = self._dismiss def choose_ok_on_next_confirmation(self): """Undo the effect of using keywords `Choose Cancel On Next Confirmation`. Note @@ -58,7 +58,7 @@ def choose_ok_on_next_confirmation(self): consume it by using a keywords such as `Get Alert Message`, or else the following selenium operations will fail. """ - self._dismiss_on_next_confirmation = self._accept + self._next_alert_dismiss_type = self._accept def confirm_action(self): """Dismisses currently shown confirmation dialog and returns it's message. @@ -77,8 +77,8 @@ def confirm_action(self): | Click Button | Send | # Shows a confirmation dialog | | Confirm Action | | # Chooses Cancel | """ - text = self._handle_alert(self._dismiss_on_next_confirmation) - self._dismiss_on_next_confirmation = self._dismiss + text = self._handle_alert(self._next_alert_dismiss_type) + self._next_alert_dismiss_type = self._dismiss return text def get_alert_message(self, dismiss=True): From 36633e4a4e160c989b0f041cc0dae3ad60125d2e Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Sat, 10 Sep 2016 12:31:40 +0300 Subject: [PATCH 4/6] Updates alert handling based on the comment review --- src/Selenium2Library/keywords/_alert.py | 28 ++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Selenium2Library/keywords/_alert.py b/src/Selenium2Library/keywords/_alert.py index ec7fb89b0..fd34ca7be 100644 --- a/src/Selenium2Library/keywords/_alert.py +++ b/src/Selenium2Library/keywords/_alert.py @@ -1,4 +1,4 @@ -from time import sleep +import time from selenium.common.exceptions import WebDriverException from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC @@ -7,16 +7,16 @@ class _AlertKeywords(KeywordGroup): + _ACCEPT = 'accept' + _DISMISS = 'dismiss' + def __init__(self): - self._accept = 'accept' - self._dismiss = 'dismiss' - self._next_alert_dismiss_type = self._accept + self._next_alert_dismiss_type = self._ACCEPT # Public def input_text_into_prompt(self, text): """Types the given `text` into alert box. """ - alert = None try: alert = self._wait_alert() alert.send_keys(text) @@ -33,7 +33,7 @@ def alert_should_be_present(self, text=''): will fail unless the alert is dismissed by this keyword or another like `Get Alert Message`. """ - alert_text = self._handle_alert(self._accept) + alert_text = self._handle_alert(self._ACCEPT) if text and alert_text != text: raise AssertionError("Alert text should have been " "'%s' but was '%s'" @@ -41,7 +41,7 @@ def alert_should_be_present(self, text=''): 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 + self._next_alert_dismiss_type = self._DISMISS def choose_ok_on_next_confirmation(self): """Undo the effect of using keywords `Choose Cancel On Next Confirmation`. Note @@ -58,7 +58,7 @@ def choose_ok_on_next_confirmation(self): consume it by using a keywords such as `Get Alert Message`, or else the following selenium operations will fail. """ - self._next_alert_dismiss_type = self._accept + self._next_alert_dismiss_type = self._ACCEPT def confirm_action(self): """Dismisses currently shown confirmation dialog and returns it's message. @@ -78,7 +78,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 + self._next_alert_dismiss_type = self._DISMISS return text def get_alert_message(self, dismiss=True): @@ -90,7 +90,7 @@ def get_alert_message(self, dismiss=True): dismissed by this keyword or another like `Get Alert Message`. """ if dismiss: - return self._handle_alert(self._dismiss) + return self._handle_alert(self._DISMISS) else: return self._handle_alert() @@ -102,7 +102,7 @@ def dismiss_alert(self, accept=True): dismissed by this keyword or another like `Get Alert Message`. """ if accept: - return self._handle_alert(self._accept) + return self._handle_alert(self._ACCEPT) else: return self._handle_alert() @@ -122,16 +122,16 @@ def _handle_alert(self, dismiss_type=None): try: return self._alert_worker(dismiss_type) except WebDriverException: - sleep(0.2) + time.sleep(0.2) retry += 1 raise RuntimeError('There were no alerts') def _alert_worker(self, dismiss_type=None): alert = self._wait_alert() text = ' '.join(alert.text.splitlines()) - if dismiss_type == self._dismiss: + if dismiss_type == self._DISMISS: alert.dismiss() - elif dismiss_type == self._accept: + elif dismiss_type == self._ACCEPT: alert.accept() return text From cb4837fe23f4969f56be917145f65810cefe6287 Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Sat, 10 Sep 2016 22:15:32 +0300 Subject: [PATCH 5/6] Changed class atributes names to describe better what they do --- src/Selenium2Library/keywords/_alert.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Selenium2Library/keywords/_alert.py b/src/Selenium2Library/keywords/_alert.py index fd34ca7be..a6747e18c 100644 --- a/src/Selenium2Library/keywords/_alert.py +++ b/src/Selenium2Library/keywords/_alert.py @@ -7,11 +7,11 @@ class _AlertKeywords(KeywordGroup): - _ACCEPT = 'accept' - _DISMISS = 'dismiss' + _ACCEPT_ALERT = 'accept' + _DISMISS_ALERT = 'dismiss' def __init__(self): - self._next_alert_dismiss_type = self._ACCEPT + self._next_alert_dismiss_type = self._ACCEPT_ALERT # Public @@ -33,7 +33,7 @@ def alert_should_be_present(self, text=''): will fail unless the alert is dismissed by this keyword or another like `Get Alert Message`. """ - alert_text = self._handle_alert(self._ACCEPT) + 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'" @@ -41,7 +41,7 @@ def alert_should_be_present(self, text=''): 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 + self._next_alert_dismiss_type = self._DISMISS_ALERT def choose_ok_on_next_confirmation(self): """Undo the effect of using keywords `Choose Cancel On Next Confirmation`. Note @@ -58,7 +58,7 @@ def choose_ok_on_next_confirmation(self): consume it by using a keywords such as `Get Alert Message`, or else the following selenium operations will fail. """ - self._next_alert_dismiss_type = self._ACCEPT + self._next_alert_dismiss_type = self._ACCEPT_ALERT def confirm_action(self): """Dismisses currently shown confirmation dialog and returns it's message. @@ -78,7 +78,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 + self._next_alert_dismiss_type = self._DISMISS_ALERT return text def get_alert_message(self, dismiss=True): @@ -90,7 +90,7 @@ def get_alert_message(self, dismiss=True): dismissed by this keyword or another like `Get Alert Message`. """ if dismiss: - return self._handle_alert(self._DISMISS) + return self._handle_alert(self._DISMISS_ALERT) else: return self._handle_alert() @@ -102,7 +102,7 @@ def dismiss_alert(self, accept=True): dismissed by this keyword or another like `Get Alert Message`. """ if accept: - return self._handle_alert(self._ACCEPT) + return self._handle_alert(self._ACCEPT_ALERT) else: return self._handle_alert() @@ -129,9 +129,9 @@ def _handle_alert(self, dismiss_type=None): def _alert_worker(self, dismiss_type=None): alert = self._wait_alert() text = ' '.join(alert.text.splitlines()) - if dismiss_type == self._DISMISS: + if dismiss_type == self._DISMISS_ALERT: alert.dismiss() - elif dismiss_type == self._ACCEPT: + elif dismiss_type == self._ACCEPT_ALERT: alert.accept() return text From 04b9665a1d1aac934600cdcc7a76347b79d28bb0 Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Fri, 16 Sep 2016 20:27:10 +0300 Subject: [PATCH 6/6] Fixed based on the comments --- src/Selenium2Library/keywords/_alert.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Selenium2Library/keywords/_alert.py b/src/Selenium2Library/keywords/_alert.py index a6747e18c..07f6faf30 100644 --- a/src/Selenium2Library/keywords/_alert.py +++ b/src/Selenium2Library/keywords/_alert.py @@ -7,11 +7,11 @@ class _AlertKeywords(KeywordGroup): - _ACCEPT_ALERT = 'accept' - _DISMISS_ALERT = 'dismiss' + __ACCEPT_ALERT = 'accept' + __DISMISS_ALERT = 'dismiss' def __init__(self): - self._next_alert_dismiss_type = self._ACCEPT_ALERT + self._next_alert_dismiss_type = self.__ACCEPT_ALERT # Public @@ -33,7 +33,7 @@ def alert_should_be_present(self, text=''): will fail unless the alert is dismissed by this keyword or another like `Get Alert Message`. """ - alert_text = self._handle_alert(self._ACCEPT_ALERT) + 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'" @@ -41,7 +41,7 @@ def alert_should_be_present(self, text=''): 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 + self._next_alert_dismiss_type = self.__DISMISS_ALERT def choose_ok_on_next_confirmation(self): """Undo the effect of using keywords `Choose Cancel On Next Confirmation`. Note @@ -58,7 +58,7 @@ def choose_ok_on_next_confirmation(self): consume it by using a keywords such as `Get Alert Message`, or else the following selenium operations will fail. """ - self._next_alert_dismiss_type = self._ACCEPT_ALERT + self._next_alert_dismiss_type = self.__ACCEPT_ALERT def confirm_action(self): """Dismisses currently shown confirmation dialog and returns it's message. @@ -78,7 +78,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.__DISMISS_ALERT return text def get_alert_message(self, dismiss=True): @@ -90,7 +90,7 @@ def get_alert_message(self, dismiss=True): dismissed by this keyword or another like `Get Alert Message`. """ if dismiss: - return self._handle_alert(self._DISMISS_ALERT) + return self._handle_alert(self.__DISMISS_ALERT) else: return self._handle_alert() @@ -102,7 +102,7 @@ def dismiss_alert(self, accept=True): dismissed by this keyword or another like `Get Alert Message`. """ if accept: - return self._handle_alert(self._ACCEPT_ALERT) + return self._handle_alert(self.__ACCEPT_ALERT) else: return self._handle_alert() @@ -129,9 +129,9 @@ def _handle_alert(self, dismiss_type=None): def _alert_worker(self, dismiss_type=None): alert = self._wait_alert() text = ' '.join(alert.text.splitlines()) - if dismiss_type == self._DISMISS_ALERT: + if dismiss_type == self.__DISMISS_ALERT: alert.dismiss() - elif dismiss_type == self._ACCEPT_ALERT: + elif dismiss_type == self.__ACCEPT_ALERT: alert.accept() return text