Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 54 additions & 39 deletions src/SeleniumLibrary/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,24 @@

import warnings

from .base import DynamicCore
from .keywords import AlertKeywords
from .keywords import BrowserManagementKeywords
from .keywords import CookieKeywords
from .keywords import ElementKeywords
from .keywords import FormElementKeywords
from .keywords import JavaScriptKeywords
from .keywords import RunOnFailureKeywords
from .keywords import ScreenshotKeywords
from .keywords import SelectElementKeywords
from .keywords import TableElementKeywords
from .keywords import WaitingKeywords
from .locators import ElementFinder
from .utils import BrowserCache
from .utils import LibraryListener
from robot.api import logger
from robot.libraries.BuiltIn import BuiltIn

from SeleniumLibrary.base import DynamicCore
from SeleniumLibrary.keywords import (AlertKeywords,
BrowserManagementKeywords,
CookieKeywords,
ElementKeywords,
FormElementKeywords,
JavaScriptKeywords,
RunOnFailureKeywords,
ScreenshotKeywords,
SelectElementKeywords,
TableElementKeywords,
WaitingKeywords)
from SeleniumLibrary.locators import ElementFinder
from SeleniumLibrary.utils import (BrowserCache, Deprecated, LibraryListener,
timestr_to_secs)


__version__ = '3.0.0b1'
Expand Down Expand Up @@ -194,7 +197,6 @@ class SeleniumLibrary(DynamicCore):
Note that prior to SeleniumLibrary 3.0, all non-empty strings, including
``false``, ``no`` and ``none``, were considered true.
"""

ROBOT_LIBRARY_SCOPE = 'GLOBAL'
ROBOT_LIBRARY_VERSION = __version__

Expand Down Expand Up @@ -234,11 +236,13 @@ def __init__(self,
| Library `|` SeleniumLibrary `|` implicit_wait=5 `|` run_on_failure=Log Source | # Sets default implicit_wait to 5 seconds and runs `Log Source` on failure |
| Library `|` SeleniumLibrary `|` timeout=10 `|` run_on_failure=Nothing | # Sets default timeout to 10 seconds and does nothing on failure |
"""
self._run_on_failure_keyword = None
self._running_on_failure_routine = False
self._speed_in_secs = 0.0
self._timeout_in_secs = 5.0
self._implicit_wait_in_secs = 5.0
self.timeout = timestr_to_secs(timeout)
self.implicit_wait = timestr_to_secs(implicit_wait)
self.speed = 0.0
self.run_on_failure_keyword \
= RunOnFailureKeywords.resolve_keyword(run_on_failure)
self._running_on_failure_keyword = False
self.screenshot_root_directory = screenshot_root_directory
libraries = [
AlertKeywords(self),
BrowserManagementKeywords(self),
Expand All @@ -254,35 +258,46 @@ def __init__(self,
]
self._browsers = BrowserCache()
DynamicCore.__init__(self, libraries)
self.screenshot_root_directory = screenshot_root_directory
self.set_selenium_timeout(timeout)
self.set_selenium_implicit_wait(implicit_wait)
self.register_keyword_to_run_on_failure(run_on_failure)
self.ROBOT_LIBRARY_LISTENER = LibraryListener()
self.element_finder = ElementFinder(self)

_speed_in_secs = Deprecated('_speed_in_secs', 'speed')
_timeout_in_secs = Deprecated('_timeout_in_secs', 'timeout')
_implicit_wait_in_secs = Deprecated('_implicit_wait_in_secs',
'implicit_wait')
_run_on_failure_keyword = Deprecated('_run_on_failure_keyword',
'run_on_failure_keyword')

def run_keyword(self, name, args, kwargs):
try:
return DynamicCore.run_keyword(self, name, args, kwargs)
except Exception:
self.run_on_failure()
self.failure_occurred()
raise

def register_browser(self, browser, alias):
return self._browsers.register(browser, alias)

def run_on_failure(self):
"""Executes the registered run on failure keyword.
def failure_occurred(self):
"""Method that is executed when a SeleniumLibrary keyword fails.

This is designed as an API when writing library which extends the
SeleniumLibrary with new functionality. If that new functionality
does not (always) relay on SeleniumLibrary keyword methods, then the
new functionality can use this method to execute the run on failure
functionality in SeleniumLibrary"""
RunOnFailureKeywords(self).run_on_failure()
By default executes the registered run-on-failure keyword.
Libraries extending SeleniumLibrary can overwrite this hook
method if they want to provide custom functionality instead.
"""
if self._running_on_failure_keyword or not self.run_on_failure_keyword:
return
try:
self._running_on_failure_keyword = True
BuiltIn().run_keyword(self.run_on_failure_keyword)
except Exception as err:
logger.warn("Keyword '%s' could not be run on failure: %s"
% (self.run_on_failure_keyword, err))
finally:
self._running_on_failure_keyword = False

@property
def _browser(self):
def browser(self):
"""Current active browser"""
if not self._browsers.current:
raise RuntimeError('No browser is open')
Expand All @@ -296,12 +311,12 @@ def _cache(self):

def _current_browser(self):
warnings.warn('"SeleniumLibrary._current_browser" is deprecated, '
'use "SeleniumLibrary._browser" instead.',
'use "SeleniumLibrary.browser" instead.',
DeprecationWarning)
return self._browser
return self.browser

def _run_on_failure(self):
warnings.warn('"SeleniumLibrary._run_on_failure" is deprecated, '
'use "SeleniumLibrary.run_on_failure" instead.',
'use "SeleniumLibrary.failure_occurred" instead.',
DeprecationWarning)
self.run_on_failure()
self.failure_occurred()
3 changes: 2 additions & 1 deletion src/SeleniumLibrary/base/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.


class ContextAware(object):

def __init__(self, ctx):
self.ctx = ctx

@property
def browser(self):
return self.ctx._browser
return self.ctx.browser

@property
def browsers(self):
Expand Down
40 changes: 21 additions & 19 deletions src/SeleniumLibrary/keywords/browsermanagement.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
import types

from robot.errors import DataError
from robot.utils import secs_to_timestr, timestr_to_secs
from selenium import webdriver
from selenium.common.exceptions import NoSuchWindowException

from SeleniumLibrary.base import LibraryComponent, keyword
from SeleniumLibrary.locators.windowmanager import WindowManager
from SeleniumLibrary.utils import is_truthy, is_falsy
from SeleniumLibrary.utils import (is_truthy, is_falsy,
secs_to_timestr, timestr_to_secs)


ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
Expand Down Expand Up @@ -480,22 +480,25 @@ def reload_page(self):
def get_selenium_speed(self):
"""Gets the delay in seconds that is waited after each Selenium command.

See `Set Selenium Speed` for an explanation."""
return secs_to_timestr(self.ctx._speed_in_secs)
See `Set Selenium Speed` for an explanation.
"""
return secs_to_timestr(self.ctx.speed)

@keyword
def get_selenium_timeout(self):
"""Gets the timeout in seconds that is used by various keywords.

See `Set Selenium Timeout` for an explanation."""
return secs_to_timestr(self.ctx._timeout_in_secs)
See `Set Selenium Timeout` for an explanation.
"""
return secs_to_timestr(self.ctx.timeout)

@keyword
def get_selenium_implicit_wait(self):
"""Gets the wait in seconds that is waited by Selenium.

See `Set Selenium Implicit Wait` for an explanation."""
return secs_to_timestr(self.ctx._implicit_wait_in_secs)
See `Set Selenium Implicit Wait` for an explanation.
"""
return secs_to_timestr(self.ctx.implicit_wait)

@keyword
def set_selenium_speed(self, seconds):
Expand All @@ -515,10 +518,10 @@ def set_selenium_speed(self, seconds):
Example:
| Set Selenium Speed | .5 seconds |
"""
old_speed = self.ctx._speed_in_secs
self.ctx._speed_in_secs = timestr_to_secs(seconds)
old_speed = self.ctx.speed
self.ctx.speed = timestr_to_secs(seconds)
for browser in self.browsers.browsers:
browser._speed = self.ctx._speed_in_secs
browser._speed = self.ctx.speed
self._monkey_patch_speed(browser)
return old_speed

Expand All @@ -541,9 +544,9 @@ def set_selenium_timeout(self, seconds):
| Set Selenium Timeout | ${orig timeout} |
"""
old_timeout = self.get_selenium_timeout()
self.ctx._timeout_in_secs = timestr_to_secs(seconds)
self.ctx.timeout = timestr_to_secs(seconds)
for browser in self.browsers.get_open_browsers():
browser.set_script_timeout(self.ctx._timeout_in_secs)
browser.set_script_timeout(self.ctx.timeout)
return old_timeout

@keyword
Expand All @@ -561,9 +564,9 @@ def set_selenium_implicit_wait(self, seconds):
| Set Selenium Implicit Wait | ${orig wait} |
"""
old_wait = self.get_selenium_implicit_wait()
self.ctx._implicit_wait_in_secs = timestr_to_secs(seconds)
self.ctx.implicit_wait = timestr_to_secs(seconds)
for browser in self.browsers.get_open_browsers():
browser.implicitly_wait(self.ctx._implicit_wait_in_secs)
browser.implicitly_wait(self.ctx.implicit_wait)
return old_wait

@keyword
Expand All @@ -579,8 +582,7 @@ def set_browser_implicit_wait(self, seconds):

See also `Set Selenium Implicit Wait`.
"""
implicit_wait_in_secs = timestr_to_secs(seconds)
self.browser.implicitly_wait(implicit_wait_in_secs)
self.browser.implicitly_wait(timestr_to_secs(seconds))

def _get_browser_creation_function(self, browser_name):
func_name = BROWSER_NAMES.get(browser_name.lower().replace(' ', ''))
Expand All @@ -594,8 +596,8 @@ def _make_browser(self, browser_name, desired_capabilities=None,
raise ValueError(browser_name + " is not a supported browser.")

browser = creation_func(remote, desired_capabilities, profile_dir)
browser.set_script_timeout(self.ctx._timeout_in_secs)
browser.implicitly_wait(self.ctx._implicit_wait_in_secs)
browser.set_script_timeout(self.ctx.timeout)
browser.implicitly_wait(self.ctx.implicit_wait)

return browser

Expand Down
3 changes: 1 addition & 2 deletions src/SeleniumLibrary/keywords/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@

from SeleniumLibrary.base import LibraryComponent, keyword
from SeleniumLibrary.keywords.formelement import FormElementKeywords
from SeleniumLibrary.utils import escape_xpath_value
from SeleniumLibrary.utils import is_truthy, is_falsy
from SeleniumLibrary.utils import escape_xpath_value, is_falsy, is_truthy


class ElementKeywords(LibraryComponent):
Expand Down
67 changes: 29 additions & 38 deletions src/SeleniumLibrary/keywords/runonfailure.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from robot.libraries.BuiltIn import BuiltIn

from SeleniumLibrary.base import LibraryComponent, keyword
from SeleniumLibrary.utils import is_falsy, is_string


class RunOnFailureKeywords(LibraryComponent):
Expand All @@ -25,53 +24,45 @@ class RunOnFailureKeywords(LibraryComponent):
def register_keyword_to_run_on_failure(self, keyword):
"""Sets the keyword to execute when a SeleniumLibrary keyword fails.

`keyword_name` is the name of a keyword (from any available
libraries) that will be executed if a SeleniumLibrary keyword fails.
It is not possible to use a keyword that requires arguments.
Using the value "Nothing" will disable this feature altogether.
`keyword` is the name of a keyword that will be executed if a
SeleniumLibrary keyword fails. It is possible to use any available
keyword, including user keywords or keywords from other libraries,
but the keyword must not take any arguments.

The initial keyword to use is set in `importing`, and the
keyword that is used by default is `Capture Page Screenshot`.
Taking a screenshot when something failed is a very useful
feature, but notice that it can slow down the execution.

It is possible to use string "Nothing" or "None", case-insensitively,
as well as any value considered false in Python to disable this
feature altogether.

This keyword returns the name of the previously registered
failure keyword. It can be used to restore the original
value later.
failure keyword or Python ``None`` if this functionality was
previously disabled. The return value can be always used to
restore the original value later.

Example:
| Register Keyword To Run On Failure | Log Source | # Run `Log Source` on failure. |
| ${previous kw}= | Register Keyword To Run On Failure | Nothing | # Disables run-on-failure functionality and stores the previous kw name in a variable. |
| Register Keyword To Run On Failure | ${previous kw} | # Restore to the previous keyword. |
"""
old_keyword = self.ctx._run_on_failure_keyword
old_keyword_text = old_keyword if old_keyword else "No keyword"

new_keyword = keyword if keyword.strip().lower() != "nothing" else None
new_keyword_text = new_keyword if new_keyword else "No keyword"

self.ctx._run_on_failure_keyword = new_keyword
self.info('%s will be run on failure.' % new_keyword_text)

return old_keyword_text

def run_on_failure(self):
if not self.ctx._run_on_failure_keyword:
return
if self.ctx._running_on_failure_routine:
return
self.ctx._running_on_failure_routine = True
try:
BuiltIn().run_keyword(self.ctx._run_on_failure_keyword)
except Exception as err:
self.run_on_failure_error(err)
finally:
self.ctx._running_on_failure_routine = False
Changes in version 3.0.0:
- Possible to use string "NONE" or any falsy value to disable the
feature.
- Return Python ``None`` when the functionality was disabled earlier.
In previous versions special value "No Keyword" was returned and
it could not be used to restore the original state.
"""
old_keyword = self.ctx.run_on_failure_keyword
keyword = self.resolve_keyword(keyword)
self.ctx.run_on_failure_keyword = keyword
self.info('%s will be run on failure.' % (keyword or 'No keyword'))
return old_keyword

def run_on_failure_error(self, err):
err = ("Keyword '%s' could not be run on failure: %s"
% (self.ctx._run_on_failure_keyword, err))
if hasattr(self, 'warn'):
self.warn(err)
return
raise Exception(err)
@staticmethod
def resolve_keyword(name):
if is_falsy(name) or is_string(name) and name.upper() == 'NOTHING':
return None
return name
6 changes: 2 additions & 4 deletions src/SeleniumLibrary/keywords/screenshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,11 @@
import os
import re

from robot.libraries.BuiltIn import BuiltIn
from robot.libraries.BuiltIn import RobotNotRunningError
from robot.libraries.BuiltIn import BuiltIn, RobotNotRunningError
from robot.utils import get_link_path

from SeleniumLibrary.base import LibraryComponent, keyword
from SeleniumLibrary.utils import events
from SeleniumLibrary.utils import is_falsy
from SeleniumLibrary.utils import events, is_falsy


class ScreenshotKeywords(LibraryComponent):
Expand Down
Loading