From 6e84c522724f5f7e914c1033509aef1fa93f859a Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Fri, 17 Feb 2017 13:35:27 +0200 Subject: [PATCH 01/16] POC for new architecture Not ready for production Remember to take the https://github.com/robotframework/PythonLibCore --- src/Selenium2Library/__init__.py | 77 +++-- src/Selenium2Library/base.py | 25 ++ src/Selenium2Library/keywords/__init__.py | 28 -- src/Selenium2Library/keywords/alert.py | 18 +- .../keywords/browsermanagement.py | 272 +++++++++------- src/Selenium2Library/keywords/cookie.py | 44 ++- src/Selenium2Library/keywords/element.py | 290 +++++++++++------- src/Selenium2Library/keywords/formelement.py | 97 +++--- src/Selenium2Library/keywords/javascript.py | 21 +- src/Selenium2Library/keywords/keywordgroup.py | 37 --- src/Selenium2Library/keywords/logging.py | 53 ---- src/Selenium2Library/keywords/runonfailure.py | 17 +- src/Selenium2Library/keywords/screenshot.py | 28 +- .../keywords/selectelement.py | 57 ++-- src/Selenium2Library/robotlibcore.py | 101 ++++++ 15 files changed, 684 insertions(+), 481 deletions(-) create mode 100644 src/Selenium2Library/base.py delete mode 100644 src/Selenium2Library/keywords/keywordgroup.py delete mode 100644 src/Selenium2Library/keywords/logging.py create mode 100644 src/Selenium2Library/robotlibcore.py diff --git a/src/Selenium2Library/__init__.py b/src/Selenium2Library/__init__.py index c237449e9..b373989be 100644 --- a/src/Selenium2Library/__init__.py +++ b/src/Selenium2Library/__init__.py @@ -1,25 +1,25 @@ -from .keywords import * +from robot.api import logger + +from Selenium2Library.utils import BrowserCache +from .keywords.alert import AlertKeywords +from .keywords.browsermanagement import BrowserManagementKeywords +from .keywords.cookie import CookieKeywords +from .keywords.element import ElementKeywords +from .keywords.formelement import FormElementKeywords +from .keywords.javascript import JavaScriptKeywords +from .keywords.runonfailure import RunOnFailureKeywords +from .keywords.screenshot import ScreenshotKeywords +from .keywords.selectelement import SelectElementKeywords +from .keywords.tableelement import TableElementKeywords +from .keywords.waiting import WaitingKeywords from .utils import LibraryListener from .version import VERSION - +from .robotlibcore import DynamicCore __version__ = VERSION -class Selenium2Library( - LoggingKeywords, - RunOnFailureKeywords, - BrowserManagementKeywords, - ElementKeywords, - TableElementKeywords, - FormElementKeywords, - SelectElementKeywords, - JavaScriptKeywords, - CookieKeywords, - ScreenshotKeywords, - WaitingKeywords, - AlertKeywords -): +class Selenium2Library(DynamicCore): """Selenium2Library is a web testing library for Robot Framework. This document is about using Selenium2Library. For information about @@ -151,8 +151,7 @@ def __init__(self, timeout=5.0, implicit_wait=0.0, run_on_failure='Capture Page Screenshot', - screenshot_root_directory=None - ): + screenshot_root_directory=None): """Selenium2Library can be imported with optional arguments. @@ -184,10 +183,42 @@ def __init__(self, | Library `|` Selenium2Library `|` implicit_wait=5 `|` run_on_failure=Log Source | # Sets default implicit_wait to 5 seconds and runs `Log Source` on failure | | Library `|` Selenium2Library `|` timeout=10 `|` run_on_failure=Nothing | # Sets default timeout to 10 seconds and does nothing on failure | """ - for base in Selenium2Library.__bases__: - base.__init__(self) + self.logger = logger + libraries = [ + AlertKeywords(self), + BrowserManagementKeywords(self), + RunOnFailureKeywords(self), + ElementKeywords(self), + TableElementKeywords(self), + FormElementKeywords(self), + SelectElementKeywords(self), + JavaScriptKeywords(self), + CookieKeywords(self), + ScreenshotKeywords(self), + WaitingKeywords(self) + ] + + DynamicCore.__init__(self, libraries) + self.cache = BrowserCache() + 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.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() + + def run_keyword(self, name, args, kwargs): + try: + return DynamicCore.run_keyword(self, name, args, kwargs) + except Exception as e: + print 'run failure here' + raise e + + def current_browser(self): + """returns the current active browser""" + if not self._cache.current: + raise RuntimeError('No browser is open') + return self.cache.current + + def register_browser(self, browser, alias): + self.cache.register(browser, alias) diff --git a/src/Selenium2Library/base.py b/src/Selenium2Library/base.py new file mode 100644 index 000000000..cd2b463e4 --- /dev/null +++ b/src/Selenium2Library/base.py @@ -0,0 +1,25 @@ +from robot.api import logger + + +class Base(object): + + def info(self, msg, html=False): + logger.info(msg, html) + + def debug(self, msg, html=False): + logger.debug(msg, html) + + def log(self, msg, level='INFO', html=False): + logger.write(msg, level, html) + + def warn(self, msg, html=False): + logger.warn(msg, html) + + def _log(self, message, level='INFO', html=False): + level = level.upper() + if (level == 'INFO'): + self.info(message, html) + elif (level == 'DEBUG'): + self.debug(message, html) + elif (level == 'WARN'): + self.warn(message, html) diff --git a/src/Selenium2Library/keywords/__init__.py b/src/Selenium2Library/keywords/__init__.py index cb9e38ea0..e69de29bb 100644 --- a/src/Selenium2Library/keywords/__init__.py +++ b/src/Selenium2Library/keywords/__init__.py @@ -1,28 +0,0 @@ -from .logging import LoggingKeywords -from .runonfailure import RunOnFailureKeywords -from .browsermanagement import BrowserManagementKeywords -from .element import ElementKeywords -from .tableelement import TableElementKeywords -from .formelement import FormElementKeywords -from .selectelement import SelectElementKeywords -from .javascript import JavaScriptKeywords -from .cookie import CookieKeywords -from .screenshot import ScreenshotKeywords -from .waiting import WaitingKeywords -from .alert import AlertKeywords - - -__all__ = [ - "LoggingKeywords", - "RunOnFailureKeywords", - "BrowserManagementKeywords", - "ElementKeywords", - "TableElementKeywords", - "FormElementKeywords", - "SelectElementKeywords", - "JavaScriptKeywords", - "CookieKeywords", - "ScreenshotKeywords", - "WaitingKeywords", - "AlertKeywords" -] diff --git a/src/Selenium2Library/keywords/alert.py b/src/Selenium2Library/keywords/alert.py index 3b299e40a..79c727927 100644 --- a/src/Selenium2Library/keywords/alert.py +++ b/src/Selenium2Library/keywords/alert.py @@ -1,21 +1,23 @@ import time + from selenium.common.exceptions import WebDriverException from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC -from .keywordgroup import KeywordGroup +from Selenium2Library.base import Base +from Selenium2Library.robotlibcore import keyword -class AlertKeywords(KeywordGroup): +class AlertKeywords(Base): __ACCEPT_ALERT = 'accept' __DISMISS_ALERT = 'dismiss' - def __init__(self): + def __init__(self, ctx): + self.ctx = ctx self._next_alert_dismiss_type = self.__ACCEPT_ALERT - # Public - + @keyword def input_text_into_prompt(self, text): """Types the given `text` into alert box. """ try: @@ -24,6 +26,7 @@ def input_text_into_prompt(self, text): except WebDriverException: raise RuntimeError('There were no alerts') + @keyword def alert_should_be_present(self, text=''): """Verifies an alert is present and dismisses it. @@ -40,10 +43,12 @@ def alert_should_be_present(self, text=''): "'%s' but was '%s'" % (text, alert_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 + @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 @@ -61,6 +66,7 @@ def choose_ok_on_next_confirmation(self): """ self._next_alert_dismiss_type = self.__ACCEPT_ALERT + @keyword def confirm_action(self): """Dismisses currently shown confirmation dialog and returns it's message. @@ -82,6 +88,7 @@ def confirm_action(self): self._next_alert_dismiss_type = self.__DISMISS_ALERT return text + @keyword def get_alert_message(self, dismiss=True): """Returns the text of current JavaScript alert. @@ -95,6 +102,7 @@ def get_alert_message(self, dismiss=True): else: return self._handle_alert() + @keyword def dismiss_alert(self, accept=True): """ Returns true if alert was confirmed, false if it was dismissed diff --git a/src/Selenium2Library/keywords/browsermanagement.py b/src/Selenium2Library/keywords/browsermanagement.py index edf969248..086d0a2c1 100644 --- a/src/Selenium2Library/keywords/browsermanagement.py +++ b/src/Selenium2Library/keywords/browsermanagement.py @@ -6,46 +6,45 @@ from robot.utils import secs_to_timestr, timestr_to_secs from selenium import webdriver -from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver from selenium.common.exceptions import NoSuchWindowException -from Selenium2Library.utils import BrowserCache +from Selenium2Library.base import Base from Selenium2Library.locators import WindowManager - -from .keywordgroup import KeywordGroup +from Selenium2Library.robotlibcore import keyword ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) FIREFOX_PROFILE_DIR = os.path.join(ROOT_DIR, 'resources', 'firefoxprofile') -BROWSER_NAMES = {'ff': "_make_ff", - 'firefox': "_make_ff", - 'ie': "_make_ie", - 'internetexplorer': "_make_ie", - 'googlechrome': "_make_chrome", - 'gc': "_make_chrome", - 'chrome': "_make_chrome", - 'opera' : "_make_opera", - 'phantomjs' : "_make_phantomjs", - 'htmlunit' : "_make_htmlunit", - 'htmlunitwithjs' : "_make_htmlunitwithjs", - 'android': "_make_android", - 'iphone': "_make_iphone", - 'safari': "_make_safari", - 'edge': "_make_edge" - } - - -class BrowserManagementKeywords(KeywordGroup): - - def __init__(self): - self._cache = BrowserCache() +BROWSER_NAMES = { + 'ff': "_make_ff", + 'firefox': "_make_ff", + 'ie': "_make_ie", + 'internetexplorer': "_make_ie", + 'googlechrome': "_make_chrome", + 'gc': "_make_chrome", + 'chrome': "_make_chrome", + 'opera': "_make_opera", + 'phantomjs': "_make_phantomjs", + 'htmlunit': "_make_htmlunit", + 'htmlunitwithjs': "_make_htmlunitwithjs", + 'android': "_make_android", + 'iphone': "_make_iphone", + 'safari': "_make_safari", + 'edge': "_make_edge" +} + + +class BrowserManagementKeywords(Base): + + def __init__(self, ctx): + Base.__init__(self) + self.ctx = ctx self._window_manager = WindowManager() self._speed_in_secs = 0.0 self._timeout_in_secs = float(5) self._implicit_wait_in_secs = float(0) - # Public, open and close - + @keyword def close_all_browsers(self): """Closes all open browsers and resets the browser cache. @@ -55,18 +54,24 @@ def close_all_browsers(self): This keyword should be used in test or suite teardown to make sure all browsers are closed. """ - self._debug('Closing all browsers') - self._cache.close_all() + self.debug('Closing all browsers') + self.ctx.cache.close_all() + @keyword def close_browser(self): """Closes the current browser.""" - if self._cache.current: - self._debug('Closing browser with session id %s' - % self._cache.current.session_id) - self._cache.close() - - def open_browser(self, url, browser='firefox', alias=None,remote_url=False, - desired_capabilities=None,ff_profile_dir=None): + if self.ctx.cache.current: + self.debug( + 'Closing browser with session id {}'.format( + self.ctx.cache.current.session_id + ) + ) + self.ctx.cache.close() + + @keyword + def open_browser( + self, url, browser='firefox', alias=None, remote_url=False, + desired_capabilities=None, ff_profile_dir=None): """Opens a new browser instance to given URL. Returns the index of this browser instance which can be used later to @@ -116,24 +121,25 @@ def open_browser(self, url, browser='firefox', alias=None,remote_url=False, wish to overwrite the default. """ if remote_url: - self._info("Opening browser '%s' to base url '%s' through remote server at '%s'" + self.info("Opening browser '%s' to base url '%s' through remote server at '%s'" % (browser, url, remote_url)) else: - self._info("Opening browser '%s' to base url '%s'" % (browser, url)) + self.info("Opening browser '%s' to base url '%s'" % (browser, url)) browser_name = browser browser = self._make_browser(browser_name,desired_capabilities,ff_profile_dir,remote_url) try: browser.get(url) except: - self._cache.register(browser, alias) - self._debug("Opened browser with session id %s but failed to open url '%s'" + self.ctx.register_browser(browser, alias) + self.debug("Opened browser with session id %s but failed to open url '%s'" % (browser.session_id, url)) raise - self._debug('Opened browser with session id %s' - % browser.session_id) - return self._cache.register(browser, alias) + self.debug('Opened browser with session id %s' % browser.session_id) + return self.ctx.register_browser(browser, alias) - def create_webdriver(self, driver_name, alias=None, kwargs={}, **init_kwargs): + @keyword + def create_webdriver(self, driver_name, alias=None, kwargs={}, + **init_kwargs): """Creates an instance of a WebDriver. Like `Open Browser`, but allows passing arguments to a WebDriver's @@ -182,11 +188,12 @@ def create_webdriver(self, driver_name, alias=None, kwargs={}, **init_kwargs): creation_func = getattr(webdriver, driver_name) except AttributeError: raise RuntimeError("'%s' is not a valid WebDriver name" % driver_name) - self._info("Creating an instance of the %s WebDriver" % driver_name) + self.info("Creating an instance of the %s WebDriver" % driver_name) driver = creation_func(**init_kwargs) - self._debug("Created %s WebDriver instance with session id %s" % (driver_name, driver.session_id)) - return self._cache.register(driver, alias) + self.debug("Created %s WebDriver instance with session id %s" % (driver_name, driver.session_id)) + return self.ctx.register_browser(driver, alias) + @keyword def switch_browser(self, index_or_alias): """Switches between active browsers using index or alias. @@ -213,26 +220,31 @@ def switch_browser(self, index_or_alias): | Switch Browser | ${id} | | | """ try: - self._cache.switch(index_or_alias) - self._debug('Switched to browser with Selenium session id %s' + self.ctx.cache.switch(index_or_alias) + self.debug('Switched to browser with Selenium session id %s' % self._cache.current.session_id) except (RuntimeError, DataError): # RF 2.6 uses RE, earlier DE raise RuntimeError("No browser with index or alias '%s' found." % index_or_alias) - # Public, window management - + @keyword def close_window(self): """Closes currently opened pop-up window.""" - self._current_browser().close() + self.ctx.current_browser().close() + @keyword def get_window_identifiers(self): """Returns and logs id attributes of all windows known to the browser.""" - return self._log_list(self._window_manager.get_window_ids(self._current_browser())) + return self._log_list( + self._window_manager.get_window_ids(self.ctx.current_browser()) + ) + @keyword def get_window_names(self): """Returns and logs names of all windows known to the browser.""" - values = self._window_manager.get_window_names(self._current_browser()) + values = self._window_manager.get_window_names( + self.ctx.current_browser() + ) # for backward compatibility, since Selenium 1 would always # return this constant value for the main window @@ -241,23 +253,29 @@ def get_window_names(self): return self._log_list(values) + @keyword def get_window_titles(self): """Returns and logs titles of all windows known to the browser.""" - return self._log_list(self._window_manager.get_window_titles(self._current_browser())) + return self._log_list( + self._window_manager.get_window_titles(self.ctx.current_browser()) + ) + @keyword def maximize_browser_window(self): """Maximizes current browser window.""" - self._current_browser().maximize_window() + self.ctx.current_browser().maximize_window() + @keyword def get_window_size(self): """Returns current window size as `width` then `height`. Example: | ${width} | ${height}= | Get Window Size | """ - size = self._current_browser().get_window_size() + size = self.ctx.current_browser().get_window_size() return size['width'], size['height'] + @keyword def set_window_size(self, width, height): """Sets the `width` and `height` of the current window to the specified values. @@ -267,17 +285,19 @@ def set_window_size(self, width, height): | Should Be Equal | ${width} | ${800} | | Should Be Equal | ${height} | ${600} | """ - return self._current_browser().set_window_size(width, height) + return self.ctx.current_browser().set_window_size(width, height) + @keyword def get_window_position(self): """Returns current window position as `x` then `y` (relative to the left and top of the screen). Example: | ${x} | ${y}= | Get Window Position | """ - position = self._current_browser().get_window_position() + position = self.ctx.current_browser().get_window_position() return position['x'], position['y'] + @keyword def set_window_position(self, x, y): """Sets the position x and y of the current window (relative to the left and top of the screen) to the specified values. @@ -287,18 +307,20 @@ def set_window_position(self, x, y): | Should Be Equal | ${x} | ${8} | | Should Be Equal | ${y} | ${10} | """ - return self._current_browser().set_window_position(x, y) + return self.ctx.current_browser().set_window_position(x, y) + @keyword def select_frame(self, locator): """Sets frame identified by `locator` as current frame. Key attributes for frames are `id` and `name.` See `introduction` for details about locating elements. """ - self._info("Selecting frame '%s'." % locator) + self.info("Selecting frame '%s'." % locator) element = self._element_find(locator, True, True) - self._current_browser().switch_to_frame(element) + self.ctx.current_browser().switch_to_frame(element) + @keyword def select_window(self, locator=None): """Selects the window matching locator and return previous window handle. @@ -334,122 +356,135 @@ def select_window(self, locator=None): | Select Window | | | # Chooses the main window again | """ try: - return self._current_browser().current_window_handle + return self.ctx.current_browser().current_window_handle except NoSuchWindowException: pass finally: - self._window_manager.select(self._current_browser(), locator) + self._window_manager.select(self.ctx.current_browser(), locator) + @keyword def list_windows(self): """Return all current window handles as a list""" - return self._current_browser().window_handles + return self.ctx.current_browser().window_handles + @keyword def unselect_frame(self): """Sets the top frame as the current frame.""" - self._current_browser().switch_to_default_content() - - # Public, browser/current page properties + self.ctx.current_browser().switch_to_default_content() + @keyword def get_location(self): """Returns the current location.""" - return self._current_browser().current_url + return self.ctx.current_browser().current_url + @keyword def get_locations(self): """Returns and logs current locations of all windows known to the browser.""" return self._log_list( [window_info[4] for window_info in - self._window_manager._get_window_infos(self._current_browser())] + self._window_manager._get_window_infos(self.ctx.current_browser())] ) + @keyword def get_source(self): """Returns the entire html source of the current page or frame.""" - return self._current_browser().page_source + return self.ctx.current_browser().page_source + @keyword def get_title(self): """Returns title of current page.""" - return self._current_browser().title + return self.ctx.current_browser().title + @keyword def location_should_be(self, url): """Verifies that current URL is exactly `url`.""" actual = self.get_location() - if actual != url: + if actual != url: raise AssertionError("Location should have been '%s' but was '%s'" % (url, actual)) - self._info("Current location is '%s'." % url) + self.info("Current location is '%s'." % url) + @keyword def location_should_contain(self, expected): """Verifies that current URL contains `expected`.""" actual = self.get_location() - if not expected in actual: + if expected not in actual: raise AssertionError("Location should have contained '%s' " "but it was '%s'." % (expected, actual)) - self._info("Current location contains '%s'." % expected) + self.info("Current location contains '%s'." % expected) + @keyword def log_location(self): """Logs and returns the current location.""" url = self.get_location() - self._info(url) + self.info(url) return url + @keyword def log_source(self, loglevel='INFO'): """Logs and returns the entire html source of the current page or frame. - The `loglevel` argument defines the used log level. Valid log levels are - WARN, INFO (default), DEBUG, and NONE (no logging). + The `loglevel` argument defines the used log level. Valid log levels + are WARN, INFO (default), DEBUG, and NONE (no logging). """ source = self.get_source() - self._log(source, loglevel.upper()) + self.log(source, loglevel.upper()) return source + @keyword def log_title(self): """Logs and returns the title of current page.""" title = self.get_title() - self._info(title) + self.info(title) return title + @keyword def title_should_be(self, title): """Verifies that current page title equals `title`.""" actual = self.get_title() if actual != title: raise AssertionError("Title should have been '%s' but was '%s'" % (title, actual)) - self._info("Page title is '%s'." % title) - - # Public, navigation + self.info("Page title is '%s'." % title) + @keyword def go_back(self): """Simulates the user clicking the "back" button on their browser.""" - self._current_browser().back() + self.ctx.current_browser().back() + @keyword def go_to(self, url): """Navigates the active browser instance to the provided URL.""" - self._info("Opening url '%s'" % url) - self._current_browser().get(url) + self.info("Opening url '%s'" % url) + self.ctx.current_browser().get(url) + @keyword def reload_page(self): """Simulates user reloading page.""" - self._current_browser().refresh() - - # Public, execution properties + self.ctx.current_browser().refresh() + @keyword 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._speed_in_secs) + @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._timeout_in_secs) + @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._implicit_wait_in_secs) + @keyword def set_selenium_speed(self, seconds): """Sets the delay in seconds that is waited after each Selenium command. @@ -469,11 +504,12 @@ def set_selenium_speed(self, seconds): """ old_speed = self._speed_in_secs self._speed_in_secs = timestr_to_secs(seconds) - for browser in self._cache.browsers: + for browser in self.ctx.cache.browsers: browser._speed = self._speed_in_secs self._monkey_patch_speed(browser) return old_speed + @keyword def set_selenium_timeout(self, seconds): """Sets the timeout in seconds used by various keywords. @@ -493,10 +529,11 @@ def set_selenium_timeout(self, seconds): """ old_timeout = self.get_selenium_timeout() self._timeout_in_secs = timestr_to_secs(seconds) - for browser in self._cache.get_open_browsers(): + for browser in self.ctc.cache.get_open_browsers(): browser.set_script_timeout(self._timeout_in_secs) return old_timeout + @keyword def set_selenium_implicit_wait(self, seconds): """Sets Selenium 2's default implicit wait in seconds and sets the implicit wait for all open browsers. @@ -512,11 +549,11 @@ def set_selenium_implicit_wait(self, seconds): """ old_wait = self.get_selenium_implicit_wait() self._implicit_wait_in_secs = timestr_to_secs(seconds) - for browser in self._cache.get_open_browsers(): + for browser in self.ctx.cache.get_open_browsers(): browser.implicitly_wait(self._implicit_wait_in_secs) return old_wait - + @keyword def set_browser_implicit_wait(self, seconds): """Sets current browser's implicit wait in seconds. @@ -530,14 +567,7 @@ def set_browser_implicit_wait(self, seconds): See also `Set Selenium Implicit Wait`. """ implicit_wait_in_secs = timestr_to_secs(seconds) - self._current_browser().implicitly_wait(implicit_wait_in_secs) - - # Private - - def _current_browser(self): - if not self._cache.current: - raise RuntimeError('No browser is open') - return self._cache.current + self.ctx.current_browser().implicitly_wait(implicit_wait_in_secs) def _get_browser_creation_function(self, browser_name): func_name = BROWSER_NAMES.get(browser_name.lower().replace(' ', '')) @@ -556,8 +586,7 @@ def _make_browser(self, browser_name, desired_capabilities=None, return browser - - def _make_ff(self , remote , desired_capabilites , profile_dir): + def _make_ff(self, remote, desired_capabilites, profile_dir): if not profile_dir: profile_dir = FIREFOX_PROFILE_DIR profile = webdriver.FirefoxProfile(profile_dir) @@ -568,43 +597,43 @@ def _make_ff(self , remote , desired_capabilites , profile_dir): browser = webdriver.Firefox(firefox_profile=profile) return browser - def _make_ie(self , remote , desired_capabilities , profile_dir): + def _make_ie(self, remote, desired_capabilities, profile_dir): return self._generic_make_browser(webdriver.Ie, webdriver.DesiredCapabilities.INTERNETEXPLORER, remote, desired_capabilities) - def _make_chrome(self , remote , desired_capabilities , profile_dir): + def _make_chrome(self, remote, desired_capabilities, profile_dir): return self._generic_make_browser(webdriver.Chrome, webdriver.DesiredCapabilities.CHROME, remote, desired_capabilities) - def _make_opera(self , remote , desired_capabilities , profile_dir): + def _make_opera(self, remote, desired_capabilities, profile_dir): return self._generic_make_browser(webdriver.Opera, webdriver.DesiredCapabilities.OPERA, remote, desired_capabilities) - def _make_phantomjs(self , remote , desired_capabilities , profile_dir): + def _make_phantomjs(self, remote, desired_capabilities, profile_dir): return self._generic_make_browser(webdriver.PhantomJS, webdriver.DesiredCapabilities.PHANTOMJS, remote, desired_capabilities) - def _make_htmlunit(self , remote , desired_capabilities , profile_dir): + def _make_htmlunit(self, remote, desired_capabilities, profile_dir): return self._generic_make_browser(webdriver.Remote, webdriver.DesiredCapabilities.HTMLUNIT, remote, desired_capabilities) - def _make_htmlunitwithjs(self , remote , desired_capabilities , profile_dir): + def _make_htmlunitwithjs(self, remote, desired_capabilities, profile_dir): return self._generic_make_browser(webdriver.Remote, webdriver.DesiredCapabilities.HTMLUNITWITHJS, remote, desired_capabilities) - def _make_android(self , remote , desired_capabilities , profile_dir): + def _make_android(self, remote, desired_capabilities, profile_dir): return self._generic_make_browser(webdriver.Remote, webdriver.DesiredCapabilities.ANDROID, remote, desired_capabilities) - def _make_iphone(self , remote , desired_capabilities , profile_dir): + def _make_iphone(self, remote, desired_capabilities, profile_dir): return self._generic_make_browser(webdriver.Remote, webdriver.DesiredCapabilities.IPHONE, remote, desired_capabilities) - def _make_safari(self , remote , desired_capabilities , profile_dir): + def _make_safari(self, remote, desired_capabilities, profile_dir): return self._generic_make_browser(webdriver.Safari, webdriver.DesiredCapabilities.SAFARI, remote, desired_capabilities) - def _make_edge(self , remote , desired_capabilities , profile_dir): + def _make_edge(self, remote, desired_capabilities, profile_dir): if hasattr(webdriver, 'Edge'): return self._generic_make_browser(webdriver.Edge, webdriver.DesiredCapabilities.EDGE, remote, desired_capabilities) @@ -620,7 +649,7 @@ def _generic_make_browser(self, webdriver_type , desired_cap_type, remote_url, d browser = self._create_remote_web_driver(desired_cap_type,remote_url , desired_caps) return browser - def _create_remote_web_driver(self , capabilities_type , remote_url , desired_capabilities=None , profile=None): + def _create_remote_web_driver(self, capabilities_type, remote_url, desired_capabilities=None, profile=None): '''parses the string based desired_capabilities if neccessary and creates the associated remote web driver''' @@ -662,3 +691,14 @@ def execute(self, driver_command, params=None): if not hasattr(browser, '_base_execute'): browser._base_execute = browser.execute browser.execute = types.MethodType(execute, browser) + + def _log_list(self, items, what='item'): + msg = [ + 'Altogether {} {}.'.format( + len(items), what if len(items) == 1 else '{}s'.format(what) + ) + ] + for index, item in enumerate(items): + msg.append('{}: {}'.format(index + 1, item)) + self.info('\n'.join(msg)) + return items diff --git a/src/Selenium2Library/keywords/cookie.py b/src/Selenium2Library/keywords/cookie.py index e8334a136..a122c6f63 100644 --- a/src/Selenium2Library/keywords/cookie.py +++ b/src/Selenium2Library/keywords/cookie.py @@ -1,46 +1,58 @@ -from .keywordgroup import KeywordGroup +from Selenium2Library.robotlibcore import keyword -class CookieKeywords(KeywordGroup): +class CookieKeywords(object): + def __init__(self, ctx): + self.ctx = ctx + + @keyword def delete_all_cookies(self): """Deletes all cookies.""" - self._current_browser().delete_all_cookies() + self.ctx.current_browser().delete_all_cookies() + @keyword def delete_cookie(self, name): """Deletes cookie matching `name`. If the cookie is not found, nothing happens. """ - self._current_browser().delete_cookie(name) + self.ctx.current_browser().delete_cookie(name) + @keyword def get_cookies(self): """Returns all cookies of the current page.""" pairs = [] - for cookie in self._current_browser().get_cookies(): + for cookie in self.ctx.current_browser().get_cookies(): pairs.append(cookie['name'] + "=" + cookie['value']) return '; '.join(pairs) + @keyword def get_cookie_value(self, name): """Returns value of cookie found with `name`. If no cookie is found with `name`, this keyword fails. """ - cookie = self._current_browser().get_cookie(name) + cookie = self.ctx.current_browser().get_cookie(name) if cookie is not None: return cookie['value'] raise ValueError("Cookie with name %s not found." % name) - def add_cookie(self,name, value, path=None, domain=None, secure=None, - expiry=None): + @keyword + def add_cookie(self, name, value, path=None, domain=None, secure=None, + expiry=None): """Adds a cookie to your current session. "name" and "value" are required, "path", "domain" and "secure" are optional""" - new_cookie = {'name' : name, - 'value' : value} - if path: new_cookie['path'] = path - if domain: new_cookie['domain'] = domain - #secure should be True or False so check explicitly for None - if not secure is None: new_cookie['secure'] = secure - - self._current_browser().add_cookie(new_cookie) + new_cookie = { + 'name': name, + 'value': value + } + if path: + new_cookie['path'] = path + if domain: + new_cookie['domain'] = domain + # secure should be True or False so check explicitly for None + if secure is not None: + new_cookie['secure'] = secure + self.ctx.current_browser().add_cookie(new_cookie) diff --git a/src/Selenium2Library/keywords/element.py b/src/Selenium2Library/keywords/element.py index 3c6bf57f3..3c06be0ae 100644 --- a/src/Selenium2Library/keywords/element.py +++ b/src/Selenium2Library/keywords/element.py @@ -5,56 +5,58 @@ from Selenium2Library import utils from Selenium2Library.locators import ElementFinder from Selenium2Library.locators import CustomLocator +from Selenium2Library.base import Base +from Selenium2Library.robotlibcore import keyword -from .keywordgroup import KeywordGroup +class ElementKeywords(Base): -class ElementKeywords(KeywordGroup): - - def __init__(self): - self._element_finder = ElementFinder() - - # Public, get element(s) + def __init__(self, ctx): + Base.__init__(self) + self.ctx = ctx + self.element_finder = ElementFinder() + @keyword def get_webelement(self, locator): """Returns the first WebElement matching the given locator. See `introduction` for details about locating elements. """ - return self._element_find(locator, True, True) + return self.element_find(locator, True, True) + @keyword def get_webelements(self, locator): """Returns list of WebElement objects matching locator. See `introduction` for details about locating elements. """ - return self._element_find(locator, False, True) - - # Public, element lookups + return self.element_find(locator, False, True) + @keyword def current_frame_contains(self, text, loglevel='INFO'): """Verifies that current frame contains `text`. See `Page Should Contain ` for explanation about `loglevel` argument. """ if not self._is_text_present(text): - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Page should have contained text '%s' " "but did not" % text) - self._info("Current page contains text '%s'." % text) - + self.info("Current page contains text '%s'." % text) + @keyword def current_frame_should_not_contain(self, text, loglevel='INFO'): """Verifies that current frame contains `text`. See `Page Should Contain ` for explanation about `loglevel` argument. """ if self._is_text_present(text): - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Page should not have contained text '%s' " "but it did" % text) - self._info("Current page should not contain text '%s'." % text) + self.info("Current page should not contain text '%s'." % text) + @keyword def element_should_contain(self, locator, expected, message=''): """Verifies element identified by `locator` contains text `expected`. @@ -66,15 +68,17 @@ def element_should_contain(self, locator, expected, message=''): Key attributes for arbitrary elements are `id` and `name`. See `introduction` for details about locating elements. """ - self._info("Verifying element '%s' contains text '%s'." - % (locator, expected)) + self.info( + "Verifying element '%s' contains text '%s'." % (locator, expected) + ) actual = self._get_text(locator) - if not expected in actual: + if expected not in actual: if not message: message = "Element '%s' should have contained text '%s' but "\ "its text was '%s'." % (locator, expected, actual) raise AssertionError(message) + @keyword def element_should_not_contain(self, locator, expected, message=''): """Verifies element identified by `locator` does not contain text `expected`. @@ -83,7 +87,8 @@ def element_should_not_contain(self, locator, expected, message=''): Key attributes for arbitrary elements are `id` and `name`. See `Element Should Contain` for more details. """ - self._info("Verifying element '%s' does not contain text '%s'." + self.info( + "Verifying element '%s' does not contain text '%s'." % (locator, expected)) actual = self._get_text(locator) if expected in actual: @@ -92,6 +97,7 @@ def element_should_not_contain(self, locator, expected, message=''): "it did." % (locator, expected) raise AssertionError(message) + @keyword def frame_should_contain(self, locator, text, loglevel='INFO'): """Verifies frame identified by `locator` contains `text`. @@ -101,11 +107,12 @@ def frame_should_contain(self, locator, text, loglevel='INFO'): details about locating elements. """ if not self._frame_contains(locator, text): - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Page should have contained text '%s' " "but did not" % text) - self._info("Current page contains text '%s'." % text) + self.info("Current page contains text '%s'." % text) + @keyword def page_should_contain(self, text, loglevel='INFO'): """Verifies that current page contains `text`. @@ -116,11 +123,12 @@ def page_should_contain(self, text, loglevel='INFO'): will not be logged. """ if not self._page_contains(text): - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Page should have contained text '%s' " "but did not" % text) - self._info("Current page contains text '%s'." % text) + self.info("Current page contains text '%s'." % text) + @keyword def page_should_contain_element(self, locator, message='', loglevel='INFO'): """Verifies element identified by `locator` is found on the current page. @@ -133,6 +141,7 @@ def page_should_contain_element(self, locator, message='', loglevel='INFO'): """ self._page_should_contain_element(locator, None, message, loglevel) + @keyword def locator_should_match_x_times(self, locator, expected_locator_count, message='', loglevel='INFO'): """Verifies that the page contains the given number of elements located by the given `locator`. @@ -141,26 +150,28 @@ def locator_should_match_x_times(self, locator, expected_locator_count, message= See `Page Should Contain Element` for explanation about `message` and `loglevel` arguments. """ - actual_locator_count = len(self._element_find(locator, False, False)) + actual_locator_count = len(self.element_find(locator, False, False)) if int(actual_locator_count) != int(expected_locator_count): if not message: message = "Locator %s should have matched %s times but matched %s times"\ %(locator, expected_locator_count, actual_locator_count) - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError(message) - self._info("Current page contains %s elements matching '%s'." + self.info("Current page contains %s elements matching '%s'." % (actual_locator_count, locator)) + @keyword def page_should_not_contain(self, text, loglevel='INFO'): """Verifies the current page does not contain `text`. See `Page Should Contain ` for explanation about `loglevel` argument. """ if self._page_contains(text): - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Page should not have contained text '%s'" % text) - self._info("Current page does not contain text '%s'." % text) + self.info("Current page does not contain text '%s'." % text) + @keyword def page_should_not_contain_element(self, locator, message='', loglevel='INFO'): """Verifies element identified by `locator` is not found on the current page. @@ -175,6 +186,7 @@ def page_should_not_contain_element(self, locator, message='', loglevel='INFO'): # Public, attributes + @keyword def assign_id_to_element(self, locator, id): """Assigns a temporary identifier to element specified by `locator`. @@ -185,10 +197,11 @@ def assign_id_to_element(self, locator, id): | Assign ID to Element | xpath=//div[@id="first_div"] | my id | | Page Should Contain Element | my id | """ - self._info("Assigning temporary id '%s' to element '%s'" % (id, locator)) - element = self._element_find(locator, True, True) - self._current_browser().execute_script("arguments[0].id = '%s';" % id, element) + self.info("Assigning temporary id '%s' to element '%s'" % (id, locator)) + element = self.element_find(locator, True, True) + self.ctx.current_browser().execute_script("arguments[0].id = '%s';" % id, element) + @keyword def element_should_be_disabled(self, locator): """Verifies that element identified with `locator` is disabled. @@ -198,6 +211,7 @@ def element_should_be_disabled(self, locator): if self._is_enabled(locator): raise AssertionError("Element '%s' is enabled." % (locator)) + @keyword def element_should_be_enabled(self, locator): """Verifies that element identified with `locator` is enabled. @@ -207,6 +221,7 @@ def element_should_be_enabled(self, locator): if not self._is_enabled(locator): raise AssertionError("Element '%s' is disabled." % (locator)) + @keyword def element_should_be_visible(self, locator, message=''): """Verifies that the element identified by `locator` is visible. @@ -220,7 +235,7 @@ def element_should_be_visible(self, locator, message=''): Key attributes for arbitrary elements are `id` and `name`. See `introduction` for details about locating elements. """ - self._info("Verifying element '%s' is visible." % locator) + self.info("Verifying element '%s' is visible." % locator) visible = self._is_visible(locator) if not visible: if not message: @@ -228,6 +243,7 @@ def element_should_be_visible(self, locator, message=''): "is not." % locator raise AssertionError(message) + @keyword def element_should_not_be_visible(self, locator, message=''): """Verifies that the element identified by `locator` is NOT visible. @@ -238,7 +254,7 @@ def element_should_not_be_visible(self, locator, message=''): Key attributes for arbitrary elements are `id` and `name`. See `introduction` for details about locating elements. """ - self._info("Verifying element '%s' is not visible." % locator) + self.info("Verifying element '%s' is not visible." % locator) visible = self._is_visible(locator) if visible: if not message: @@ -246,6 +262,7 @@ def element_should_not_be_visible(self, locator, message=''): "but it is." % locator raise AssertionError(message) + @keyword def element_text_should_be(self, locator, expected, message=''): """Verifies element identified by `locator` exactly contains text `expected`. @@ -257,9 +274,9 @@ def element_text_should_be(self, locator, expected, message=''): Key attributes for arbitrary elements are `id` and `name`. See `introduction` for details about locating elements. """ - self._info("Verifying element '%s' contains exactly text '%s'." + self.info("Verifying element '%s' contains exactly text '%s'." % (locator, expected)) - element = self._element_find(locator, True, True) + element = self.element_find(locator, True, True) actual = element.text if expected != actual: if not message: @@ -267,6 +284,7 @@ def element_text_should_be(self, locator, expected, message=''): "in fact it was '%s'." % (locator, expected, actual) raise AssertionError(message) + @keyword def get_element_attribute(self, locator, attribute_name=None): """Returns value of the element attribute. @@ -290,11 +308,12 @@ def get_element_attribute(self, locator, attribute_name=None): """ if not attribute_name: locator, attribute_name = self._parse_attribute_locator(locator) - element = self._element_find(locator, True, False) + element = self.element_find(locator, True, False) if not element: raise ValueError("Element '%s' not found." % (locator)) return element.get_attribute(attribute_name) + @keyword def get_horizontal_position(self, locator): """Returns horizontal position of element identified by `locator`. @@ -303,21 +322,23 @@ def get_horizontal_position(self, locator): See also `Get Vertical Position`. """ - element = self._element_find(locator, True, False) + element = self.element_find(locator, True, False) if element is None: raise AssertionError("Could not determine position for '%s'" % (locator)) return element.location['x'] + @keyword def get_element_size(self, locator): """Returns width and height of element identified by `locator`. The element width and height is returned. Fails if a matching element is not found. """ - element = self._element_find(locator, True, True) + element = self.element_find(locator, True, True) return element.size['width'], element.size['height'] + @keyword def get_value(self, locator): """Returns the value attribute of element identified by `locator`. @@ -325,6 +346,7 @@ def get_value(self, locator): """ return self._get_value(locator) + @keyword def get_text(self, locator): """Returns the text value of element identified by `locator`. @@ -332,14 +354,16 @@ def get_text(self, locator): """ return self._get_text(locator) + @keyword def clear_element_text(self, locator): """Clears the text value of text entry element identified by `locator`. See `introduction` for details about locating elements. """ - element = self._element_find(locator, True, True) + element = self.element_find(locator, True, True) element.clear() + @keyword def get_vertical_position(self, locator): """Returns vertical position of element identified by `locator`. @@ -348,22 +372,24 @@ def get_vertical_position(self, locator): See also `Get Horizontal Position`. """ - element = self._element_find(locator, True, False) + element = self.element_find(locator, True, False) if element is None: raise AssertionError("Could not determine position for '%s'" % (locator)) return element.location['y'] # Public, mouse input/events + @keyword def click_element(self, locator): """Click element identified by `locator`. Key attributes for arbitrary elements are `id` and `name`. See `introduction` for details about locating elements. """ - self._info("Clicking element '%s'." % locator) - self._element_find(locator, True, True).click() + self.info("Clicking element '%s'." % locator) + self.element_find(locator, True, True).click() + @keyword def click_element_at_coordinates(self, locator, xoffset, yoffset): """Click element identified by `locator` at x/y coordinates of the element. Cursor is moved and the center of the element and x/y coordinates are @@ -372,29 +398,35 @@ def click_element_at_coordinates(self, locator, xoffset, yoffset): Key attributes for arbitrary elements are `id` and `name`. See `introduction` for details about locating elements. """ - self._info("Click clicking element '%s' in coordinates '%s', '%s'." % (locator, xoffset, yoffset)) - element = self._element_find(locator, True, True) - action = ActionChains(self._current_browser()) + self.info("Click clicking element '%s' in coordinates '%s', '%s'." % (locator, xoffset, yoffset)) + element = self.element_find(locator, True, True) + action = ActionChains(self.ctx.current_browser()) action.move_to_element(element) action.move_by_offset(xoffset, yoffset) action.click() action.perform() + @keyword def double_click_element(self, locator): """Double click element identified by `locator`. Key attributes for arbitrary elements are `id` and `name`. See `introduction` for details about locating elements. """ - self._info("Double clicking element '%s'." % locator) - element = self._element_find(locator, True, True) - ActionChains(self._current_browser()).double_click(element).perform() + self.info("Double clicking element '%s'." % locator) + element = self.element_find(locator, True, True) + action = ActionChains(self.ctx.current_browser()) + action.double_click(element).perform() + @keyword def focus(self, locator): """Sets focus to element identified by `locator`.""" - element = self._element_find(locator, True, True) - self._current_browser().execute_script("arguments[0].focus();", element) + element = self.element_find(locator, True, True) + self.ctx.current_browser().execute_script( + "arguments[0].focus();", element + ) + @keyword def drag_and_drop(self, source, target): """Drags element identified with `source` which is a locator. @@ -407,10 +439,12 @@ def drag_and_drop(self, source, target): Examples: | Drag And Drop | elem1 | elem2 | # Move elem1 over elem2. | """ - src_elem = self._element_find(source, True, True) - trg_elem = self._element_find(target, True, True) - ActionChains(self._current_browser()).drag_and_drop(src_elem, trg_elem).perform() + src_elem = self.element_find(source, True, True) + trg_elem = self.element_find(target, True, True) + action = ActionChains(self.ctx.current_browser()) + action.drag_and_drop(src_elem, trg_elem).perform() + @keyword def drag_and_drop_by_offset(self, source, xoffset, yoffset): """Drags element identified with `source` which is a locator. @@ -420,11 +454,12 @@ def drag_and_drop_by_offset(self, source, xoffset, yoffset): Examples: | Drag And Drop By Offset | myElem | 50 | -35 | # Move myElem 50px right and 35px down. | """ - src_elem = self._element_find(source, True, True) - action = ActionChains(self._current_browser()) + src_elem = self.element_find(source, True, True) + action = ActionChains(self.ctx.current_browser()) action.drag_and_drop_by_offset(src_elem, xoffset, yoffset) action.perform() + @keyword def mouse_down(self, locator): """Simulates pressing the left mouse button on the element specified by `locator`. @@ -436,56 +471,66 @@ def mouse_down(self, locator): See also the more specific keywords `Mouse Down On Image` and `Mouse Down On Link`. """ - self._info("Simulating Mouse Down on element '%s'" % locator) - element = self._element_find(locator, True, False) + self.info("Simulating Mouse Down on element '%s'" % locator) + element = self.element_find(locator, True, False) if element is None: raise AssertionError("ERROR: Element %s not found." % (locator)) - ActionChains(self._current_browser()).click_and_hold(element).perform() + action = ActionChains(self.ctx.current_browser()) + action.click_and_hold(element).perform() + @keyword def mouse_out(self, locator): """Simulates moving mouse away from the element specified by `locator`. Key attributes for arbitrary elements are `id` and `name`. See `introduction` for details about locating elements. """ - self._info("Simulating Mouse Out on element '%s'" % locator) - element = self._element_find(locator, True, False) + self.info("Simulating Mouse Out on element '%s'" % locator) + element = self.element_find(locator, True, False) if element is None: raise AssertionError("ERROR: Element %s not found." % (locator)) size = element.size offsetx = (size['width'] / 2) + 1 offsety = (size['height'] / 2) + 1 - ActionChains(self._current_browser()).move_to_element(element).move_by_offset(offsetx, offsety).perform() + action = ActionChains(self.ctx.current_browser()) + action.move_to_element(element).move_by_offset(offsetx, offsety) + action.perform() + @keyword def mouse_over(self, locator): """Simulates hovering mouse over the element specified by `locator`. Key attributes for arbitrary elements are `id` and `name`. See `introduction` for details about locating elements. """ - self._info("Simulating Mouse Over on element '%s'" % locator) - element = self._element_find(locator, True, False) + self.info("Simulating Mouse Over on element '%s'" % locator) + element = self.element_find(locator, True, False) if element is None: raise AssertionError("ERROR: Element %s not found." % (locator)) - ActionChains(self._current_browser()).move_to_element(element).perform() + action = ActionChains(self.ctx.current_browser()) + action.move_to_element(element).perform() + @keyword def mouse_up(self, locator): """Simulates releasing the left mouse button on the element specified by `locator`. Key attributes for arbitrary elements are `id` and `name`. See `introduction` for details about locating elements. """ - self._info("Simulating Mouse Up on element '%s'" % locator) - element = self._element_find(locator, True, False) + self.info("Simulating Mouse Up on element '%s'" % locator) + element = self.element_find(locator, True, False) if element is None: raise AssertionError("ERROR: Element %s not found." % (locator)) - ActionChains(self._current_browser()).release(element).perform() + ActionChains(self.ctx.current_browser()).release(element).perform() + @keyword def open_context_menu(self, locator): """Opens context menu on element identified by `locator`.""" - element = self._element_find(locator, True, True) - ActionChains(self._current_browser()).context_click(element).perform() + element = self.element_find(locator, True, True) + action = ActionChains(self.ctx.current_browser()) + action.context_click(element).perform() + @keyword def simulate(self, locator, event): """Simulates `event` on element identified by `locator`. @@ -494,7 +539,7 @@ def simulate(self, locator, event): See `introduction` for details about locating elements. """ - element = self._element_find(locator, True, True) + element = self.element_find(locator, True, True) script = """ element = arguments[0]; eventName = arguments[1]; @@ -505,8 +550,9 @@ def simulate(self, locator, event): evt.initEvent(eventName, true, true); return !element.dispatchEvent(evt); """ - self._current_browser().execute_script(script, element, event) + self.ctx.current_browser().execute_script(script, element, event) + @keyword def press_key(self, locator, key): """Simulates user pressing key on element identified by `locator`. `key` is either a single character, a string, or a numerical ASCII code of the key @@ -518,41 +564,44 @@ def press_key(self, locator, key): """ if key.startswith('\\') and len(key) > 1: key = self._map_ascii_key_code_to_key(int(key[1:])) - element = self._element_find(locator, True, True) + element = self.element_find(locator, True, True) #select it element.send_keys(key) - # Public, links - + @keyword def click_link(self, locator): """Clicks a link identified by locator. Key attributes for links are `id`, `name`, `href` and link text. See `introduction` for details about locating elements. """ - self._info("Clicking link '%s'." % locator) - link = self._element_find(locator, True, True, tag='a') + self.info("Clicking link '%s'." % locator) + link = self.element_find(locator, True, True, tag='a') link.click() + @keyword def get_all_links(self): """Returns a list containing ids of all links found in current page. If a link has no id, an empty string will be in the list instead. """ links = [] - for anchor in self._element_find("tag=a", False, False, 'a'): + for anchor in self.element_find("tag=a", False, False, 'a'): links.append(anchor.get_attribute('id')) return links + @keyword def mouse_down_on_link(self, locator): """Simulates a mouse down event on a link. Key attributes for links are `id`, `name`, `href` and link text. See `introduction` for details about locating elements. """ - element = self._element_find(locator, True, True, 'link') - ActionChains(self._current_browser()).click_and_hold(element).perform() + element = self.element_find(locator, True, True, 'link') + action = ActionChains(self.ctx.current_browser()) + action.click_and_hold(element).perform() + @keyword def page_should_contain_link(self, locator, message='', loglevel='INFO'): """Verifies link identified by `locator` is found from current page. @@ -564,6 +613,7 @@ def page_should_contain_link(self, locator, message='', loglevel='INFO'): """ self._page_should_contain_element(locator, 'link', message, loglevel) + @keyword def page_should_not_contain_link(self, locator, message='', loglevel='INFO'): """Verifies image identified by `locator` is not found from current page. @@ -573,32 +623,36 @@ def page_should_not_contain_link(self, locator, message='', loglevel='INFO'): Key attributes for images are `id`, `src` and `alt`. See `introduction` for details about locating elements. """ - self._page_should_not_contain_element(locator, 'link', message, loglevel) - - # Public, images + self._page_should_not_contain_element( + locator, 'link', message, loglevel + ) + @keyword def click_image(self, locator): """Clicks an image found by `locator`. Key attributes for images are `id`, `src` and `alt`. See `introduction` for details about locating elements. """ - self._info("Clicking image '%s'." % locator) - element = self._element_find(locator, True, False, 'image') + self.info("Clicking image '%s'." % locator) + element = self.element_find(locator, True, False, 'image') if element is None: # A form may have an image as it's submit trigger. - element = self._element_find(locator, True, True, 'input') + element = self.element_find(locator, True, True, 'input') element.click() + @keyword def mouse_down_on_image(self, locator): """Simulates a mouse down event on an image. Key attributes for images are `id`, `src` and `alt`. See `introduction` for details about locating elements. """ - element = self._element_find(locator, True, True, 'image') - ActionChains(self._current_browser()).click_and_hold(element).perform() + element = self.element_find(locator, True, True, 'image') + action = ActionChains(self.ctx.current_browser()) + action.click_and_hold(element).perform() + @keyword def page_should_contain_image(self, locator, message='', loglevel='INFO'): """Verifies image identified by `locator` is found from current page. See `Page Should Contain Element` for explanation about `message` and @@ -609,6 +663,7 @@ def page_should_contain_image(self, locator, message='', loglevel='INFO'): """ self._page_should_contain_element(locator, 'image', message, loglevel) + @keyword def page_should_not_contain_image(self, locator, message='', loglevel='INFO'): """Verifies image identified by `locator` is found from current page. @@ -618,10 +673,11 @@ def page_should_not_contain_image(self, locator, message='', loglevel='INFO'): Key attributes for images are `id`, `src` and `alt`. See `introduction` for details about locating elements. """ - self._page_should_not_contain_element(locator, 'image', message, loglevel) - - # Public, xpath + self._page_should_not_contain_element( + locator, 'image', message, loglevel + ) + @keyword def get_matching_xpath_count(self, xpath, return_str=True): """Returns number of elements matching `xpath` @@ -638,9 +694,10 @@ def get_matching_xpath_count(self, xpath, return_str=True): If you wish to assert the number of matching elements, use `Xpath Should Match X Times`. """ - count = len(self._element_find("xpath=" + xpath, False, False)) + count = len(self.element_find("xpath=" + xpath, False, False)) return str(count) if return_str else count + @keyword def xpath_should_match_x_times(self, xpath, expected_xpath_count, message='', loglevel='INFO'): """Verifies that the page contains the given number of elements located by the given `xpath`. @@ -654,17 +711,17 @@ def xpath_should_match_x_times(self, xpath, expected_xpath_count, message='', lo See `Page Should Contain Element` for explanation about `message` and `loglevel` arguments. """ - actual_xpath_count = len(self._element_find("xpath=" + xpath, False, False)) + actual_xpath_count = len(self.element_find("xpath=" + xpath, False, False)) if int(actual_xpath_count) != int(expected_xpath_count): if not message: message = "Xpath %s should have matched %s times but matched %s times"\ %(xpath, expected_xpath_count, actual_xpath_count) - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError(message) - self._info("Current page contains %s elements matching '%s'." + self.info("Current page contains %s elements matching '%s'." % (actual_xpath_count, xpath)) - # Public, custom + @keyword def add_location_strategy(self, strategy_name, strategy_keyword, persist=False): """Adds a custom location strategy based on a user keyword. Location strategies are automatically removed after leaving the current scope by default. Setting `persist` @@ -686,22 +743,21 @@ def add_location_strategy(self, strategy_name, strategy_keyword, persist=False): See `Remove Location Strategy` for details about removing a custom location strategy. """ strategy = CustomLocator(strategy_name, strategy_keyword) - self._element_finder.register(strategy, persist) + self.element_finder.register(strategy, persist) + @keyword def remove_location_strategy(self, strategy_name): """Removes a previously added custom location strategy. Will fail if a default strategy is specified. See `Add Location Strategy` for details about adding a custom location strategy. """ - self._element_finder.unregister(strategy_name) - - # Private + self.element_finder.unregister(strategy_name) - def _element_find(self, locator, first_only, required, tag=None): - browser = self._current_browser() + def element_find(self, locator, first_only, required, tag=None): + browser = self.ctx.current_browser() if isinstance(locator, basestring): - elements = self._element_finder.find(browser, locator, tag) + elements = self.element_finder.find(browser, locator, tag) if required and len(elements) == 0: raise ValueError("Element locator '" + locator + "' did not match any elements.") if first_only: @@ -714,26 +770,26 @@ def _element_find(self, locator, first_only, required, tag=None): return elements def _frame_contains(self, locator, text): - browser = self._current_browser() - element = self._element_find(locator, True, True) + browser = self.ctx.current_browser() + element = self.element_find(locator, True, True) browser.switch_to_frame(element) - self._info("Searching for text from frame '%s'." % locator) + self.info("Searching for text from frame '%s'." % locator) found = self._is_text_present(text) browser.switch_to_default_content() return found def _get_text(self, locator): - element = self._element_find(locator, True, True) + element = self.element_find(locator, True, True) if element is not None: return element.text return None def _get_value(self, locator, tag=None): - element = self._element_find(locator, True, False, tag=tag) + element = self.element_find(locator, True, False, tag=tag) return element.get_attribute('value') if element is not None else None def _is_enabled(self, locator): - element = self._element_find(locator, True, True) + element = self.element_find(locator, True, True) if not self._is_form_element(element): raise AssertionError("ERROR: Element %s is not an input." % (locator)) if not element.is_enabled(): @@ -748,7 +804,7 @@ def _is_text_present(self, text): return self._is_element_present(locator) def _is_visible(self, locator): - element = self._element_find(locator, True, False) + element = self.element_find(locator, True, False) if element is not None: return element.is_displayed() return None @@ -795,16 +851,16 @@ def _parse_attribute_locator(self, attribute_locator): return parts[0], parts[2] def _is_element_present(self, locator, tag=None): - return self._element_find(locator, True, False, tag=tag) is not None + return self.element_find(locator, True, False, tag=tag) is not None def _page_contains(self, text): - browser = self._current_browser() + browser = self.ctx.current_browser() browser.switch_to_default_content() if self._is_text_present(text): return True - subframes = self._element_find("xpath=//frame|//iframe", False, False) + subframes = self.element_find("xpath=//frame|//iframe", False, False) self._debug('Current frame has %d subframes' % len(subframes)) for frame in subframes: browser.switch_to_frame(frame) @@ -820,9 +876,9 @@ def _page_should_contain_element(self, locator, tag, message, loglevel): if not message: message = "Page should have contained %s '%s' but did not"\ % (element_name, locator) - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError(message) - self._info("Current page contains %s '%s'." % (element_name, locator)) + self.info("Current page contains %s '%s'." % (element_name, locator)) def _page_should_not_contain_element(self, locator, tag, message, loglevel): element_name = tag if tag is not None else 'element' @@ -830,7 +886,7 @@ def _page_should_not_contain_element(self, locator, tag, message, loglevel): if not message: message = "Page should not have contained %s '%s'"\ % (element_name, locator) - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError(message) - self._info("Current page does not contain %s '%s'." + self.info("Current page does not contain %s '%s'." % (element_name, locator)) diff --git a/src/Selenium2Library/keywords/formelement.py b/src/Selenium2Library/keywords/formelement.py index 9371f34a2..15b70aa99 100644 --- a/src/Selenium2Library/keywords/formelement.py +++ b/src/Selenium2Library/keywords/formelement.py @@ -1,12 +1,18 @@ import os -from .keywordgroup import KeywordGroup +from Selenium2Library.base import Base +from Selenium2Library.robotlibcore import keyword +from Selenium2Library.keywords.element import ElementKeywords -class FormElementKeywords(KeywordGroup): +class FormElementKeywords(Base): - # Public, form + def __init__(self, ctx): + Base.__init__(self) + self.ctx = ctx + self.element_find = ElementKeywords(ctx).element_find + @keyword def submit_form(self, locator=None): """Submits a form identified by `locator`. @@ -14,38 +20,39 @@ def submit_form(self, locator=None): Key attributes for forms are `id` and `name`. See `introduction` for details about locating elements. """ - self._info("Submitting form '%s'." % locator) + self.info("Submitting form '%s'." % locator) if not locator: locator = 'xpath=//form' - element = self._element_find(locator, True, True, 'form') + element = self.element_find(locator, True, True, 'form') element.submit() - # Public, checkboxes - + @keyword def checkbox_should_be_selected(self, locator): """Verifies checkbox identified by `locator` is selected/checked. Key attributes for checkboxes are `id` and `name`. See `introduction` for details about locating elements. """ - self._info("Verifying checkbox '%s' is selected." % locator) + self.info("Verifying checkbox '%s' is selected." % locator) element = self._get_checkbox(locator) if not element.is_selected(): raise AssertionError("Checkbox '%s' should have been selected " "but was not" % locator) + @keyword def checkbox_should_not_be_selected(self, locator): """Verifies checkbox identified by `locator` is not selected/checked. Key attributes for checkboxes are `id` and `name`. See `introduction` for details about locating elements. """ - self._info("Verifying checkbox '%s' is not selected." % locator) + self.info("Verifying checkbox '%s' is not selected." % locator) element = self._get_checkbox(locator) if element.is_selected(): raise AssertionError("Checkbox '%s' should not have been selected" % locator) + @keyword def page_should_contain_checkbox(self, locator, message='', loglevel='INFO'): """Verifies checkbox identified by `locator` is found from current page. @@ -57,6 +64,7 @@ def page_should_contain_checkbox(self, locator, message='', loglevel='INFO'): """ self._page_should_contain_element(locator, 'checkbox', message, loglevel) + @keyword def page_should_not_contain_checkbox(self, locator, message='', loglevel='INFO'): """Verifies checkbox identified by `locator` is not found from current page. @@ -68,6 +76,7 @@ def page_should_not_contain_checkbox(self, locator, message='', loglevel='INFO') """ self._page_should_not_contain_element(locator, 'checkbox', message, loglevel) + @keyword def select_checkbox(self, locator): """Selects checkbox identified by `locator`. @@ -75,11 +84,12 @@ def select_checkbox(self, locator): checkboxes are `id` and `name`. See `introduction` for details about locating elements. """ - self._info("Selecting checkbox '%s'." % locator) + self.info("Selecting checkbox '%s'." % locator) element = self._get_checkbox(locator) if not element.is_selected(): element.click() + @keyword def unselect_checkbox(self, locator): """Removes selection of checkbox identified by `locator`. @@ -87,13 +97,12 @@ def unselect_checkbox(self, locator): checkboxes are `id` and `name`. See `introduction` for details about locating elements. """ - self._info("Unselecting checkbox '%s'." % locator) + self.info("Unselecting checkbox '%s'." % locator) element = self._get_checkbox(locator) if element.is_selected(): element.click() - # Public, radio buttons - + @keyword def page_should_contain_radio_button(self, locator, message='', loglevel='INFO'): """Verifies radio button identified by `locator` is found from current page. @@ -105,6 +114,7 @@ def page_should_contain_radio_button(self, locator, message='', loglevel='INFO') """ self._page_should_contain_element(locator, 'radio button', message, loglevel) + @keyword def page_should_not_contain_radio_button(self, locator, message='', loglevel='INFO'): """Verifies radio button identified by `locator` is not found from current page. @@ -116,13 +126,14 @@ def page_should_not_contain_radio_button(self, locator, message='', loglevel='IN """ self._page_should_not_contain_element(locator, 'radio button', message, loglevel) + @keyword def radio_button_should_be_set_to(self, group_name, value): """Verifies radio button group identified by `group_name` has its selection set to `value`. See `Select Radio Button` for information about how radio buttons are located. """ - self._info("Verifying radio button '%s' has selection '%s'." \ + self.info("Verifying radio button '%s' has selection '%s'." \ % (group_name, value)) elements = self._get_radio_buttons(group_name) actual_value = self._get_value_from_radio_buttons(elements) @@ -131,13 +142,14 @@ def radio_button_should_be_set_to(self, group_name, value): "been '%s' but was '%s'" % (group_name, value, actual_value)) + @keyword def radio_button_should_not_be_selected(self, group_name): """Verifies radio button group identified by `group_name` has no selection. See `Select Radio Button` for information about how radio buttons are located. """ - self._info("Verifying radio button '%s' has no selection." % group_name) + self.info("Verifying radio button '%s' has no selection." % group_name) elements = self._get_radio_buttons(group_name) actual_value = self._get_value_from_radio_buttons(elements) if actual_value is not None: @@ -145,6 +157,7 @@ def radio_button_should_not_be_selected(self, group_name): "selection, but '%s' was selected" % (group_name, actual_value)) + @keyword def select_radio_button(self, group_name, value): """Sets selection of radio button group identified by `group_name` to `value`. @@ -159,13 +172,12 @@ def select_radio_button(self, group_name, value): | Select Radio Button | size | XL | # Matches HTML like XL | | Select Radio Button | size | sizeXL | # Matches HTML like XL | """ - self._info("Selecting '%s' from radio button '%s'." % (value, group_name)) + self.info("Selecting '%s' from radio button '%s'." % (value, group_name)) element = self._get_radio_button_with_value(group_name, value) if not element.is_selected(): element.click() - # Public, text fields - + @keyword def choose_file(self, locator, file_path): """Inputs the `file_path` into file input field found by `locator`. @@ -179,8 +191,9 @@ def choose_file(self, locator, file_path): if not os.path.isfile(file_path): raise AssertionError("File '%s' does not exist on the local file system" % file_path) - self._element_find(locator, True, True).send_keys(file_path) + self.element_find(locator, True, True).send_keys(file_path) + @keyword def input_password(self, locator, text): """Types the given password into text field identified by `locator`. @@ -188,17 +201,19 @@ def input_password(self, locator, text): does not log the given password. See `introduction` for details about locating elements. """ - self._info("Typing password into text field '%s'" % locator) + self.info("Typing password into text field '%s'" % locator) self._input_text_into_text_field(locator, text) + @keyword def input_text(self, locator, text): """Types the given `text` into text field identified by `locator`. See `introduction` for details about locating elements. """ - self._info("Typing text '%s' into text field '%s'" % (text, locator)) + self.info("Typing text '%s' into text field '%s'" % (text, locator)) self._input_text_into_text_field(locator, text) + @keyword def page_should_contain_textfield(self, locator, message='', loglevel='INFO'): """Verifies text field identified by `locator` is found from current page. @@ -210,6 +225,7 @@ def page_should_contain_textfield(self, locator, message='', loglevel='INFO'): """ self._page_should_contain_element(locator, 'text field', message, loglevel) + @keyword def page_should_not_contain_textfield(self, locator, message='', loglevel='INFO'): """Verifies text field identified by `locator` is not found from current page. @@ -221,6 +237,7 @@ def page_should_not_contain_textfield(self, locator, message='', loglevel='INFO' """ self._page_should_not_contain_element(locator, 'text field', message, loglevel) + @keyword def textfield_should_contain(self, locator, expected, message=''): """Verifies text field identified by `locator` contains text `expected`. @@ -230,13 +247,14 @@ def textfield_should_contain(self, locator, expected, message=''): for details about locating elements. """ actual = self._get_value(locator, 'text field') - if not expected in actual: + if expected not in actual: if not message: message = "Text field '%s' should have contained text '%s' "\ "but it contained '%s'" % (locator, expected, actual) raise AssertionError(message) - self._info("Text field '%s' contains text '%s'." % (locator, expected)) + self.info("Text field '%s' contains text '%s'." % (locator, expected)) + @keyword def textfield_value_should_be(self, locator, expected, message=''): """Verifies the value in text field identified by `locator` is exactly `expected`. @@ -245,16 +263,17 @@ def textfield_value_should_be(self, locator, expected, message=''): Key attributes for text fields are `id` and `name`. See `introduction` for details about locating elements. """ - element = self._element_find(locator, True, False, 'text field') - if element is None: element = self._element_find(locator, True, False, 'file upload') + element = self.element_find(locator, True, False, 'text field') + if element is None: element = self.element_find(locator, True, False, 'file upload') actual = element.get_attribute('value') if element is not None else None if actual != expected: if not message: message = "Value of text field '%s' should have been '%s' "\ "but was '%s'" % (locator, expected, actual) raise AssertionError(message) - self._info("Content of text field '%s' is '%s'." % (locator, expected)) + self.info("Content of text field '%s' is '%s'." % (locator, expected)) + @keyword def textarea_should_contain(self, locator, expected, message=''): """Verifies text area identified by `locator` contains text `expected`. @@ -272,8 +291,9 @@ def textarea_should_contain(self, locator, expected, message=''): raise AssertionError(message) else: raise ValueError("Element locator '" + locator + "' did not match any elements.") - self._info("Text area '%s' contains text '%s'." % (locator, expected)) + self.info("Text area '%s' contains text '%s'." % (locator, expected)) + @keyword def textarea_value_should_be(self, locator, expected, message=''): """Verifies the value in text area identified by `locator` is exactly `expected`. @@ -291,22 +311,22 @@ def textarea_value_should_be(self, locator, expected, message=''): raise AssertionError(message) else: raise ValueError("Element locator '" + locator + "' did not match any elements.") - self._info("Content of text area '%s' is '%s'." % (locator, expected)) - - # Public, buttons + self.info("Content of text area '%s' is '%s'." % (locator, expected)) + @keyword def click_button(self, locator): """Clicks a button identified by `locator`. Key attributes for buttons are `id`, `name` and `value`. See `introduction` for details about locating elements. """ - self._info("Clicking button '%s'." % locator) - element = self._element_find(locator, True, False, 'input') + self.info("Clicking button '%s'." % locator) + element = self.element_find(locator, True, False, 'input') if element is None: - element = self._element_find(locator, True, True, 'button') + element = self.element_find(locator, True, True, 'button') element.click() + @keyword def page_should_contain_button(self, locator, message='', loglevel='INFO'): """Verifies button identified by `locator` is found from current page. @@ -323,6 +343,7 @@ def page_should_contain_button(self, locator, message='', loglevel='INFO'): except AssertionError: self._page_should_contain_element(locator, 'button', message, loglevel) + @keyword def page_should_not_contain_button(self, locator, message='', loglevel='INFO'): """Verifies button identified by `locator` is not found from current page. @@ -337,21 +358,19 @@ def page_should_not_contain_button(self, locator, message='', loglevel='INFO'): self._page_should_not_contain_element(locator, 'button', message, loglevel) self._page_should_not_contain_element(locator, 'input', message, loglevel) - # Private - def _get_checkbox(self, locator): - return self._element_find(locator, True, True, tag='input') + return self.element_find(locator, True, True, tag='input') def _get_radio_buttons(self, group_name): xpath = "xpath=//input[@type='radio' and @name='%s']" % group_name self._debug('Radio group locator: ' + xpath) - return self._element_find(xpath, False, True) + return self.element_find(xpath, False, True) def _get_radio_button_with_value(self, group_name, value): xpath = "xpath=//input[@type='radio' and @name='%s' and (@value='%s' or @id='%s')]" \ % (group_name, value, value) self._debug('Radio group locator: ' + xpath) - return self._element_find(xpath, True, True) + return self.element_find(xpath, True, True) def _get_value_from_radio_buttons(self, elements): for element in elements: @@ -360,7 +379,7 @@ def _get_value_from_radio_buttons(self, elements): return None def _input_text_into_text_field(self, locator, text): - element = self._element_find(locator, True, True) + element = self.element_find(locator, True, True) element.clear() element.send_keys(text) diff --git a/src/Selenium2Library/keywords/javascript.py b/src/Selenium2Library/keywords/javascript.py index 203d58686..868fb1df1 100644 --- a/src/Selenium2Library/keywords/javascript.py +++ b/src/Selenium2Library/keywords/javascript.py @@ -1,12 +1,16 @@ import os -from .keywordgroup import KeywordGroup +from Selenium2Library.base import Base +from Selenium2Library.robotlibcore import keyword -class JavaScriptKeywords(KeywordGroup): +class JavaScriptKeywords(Base): - # Public + def __init__(self, ctx): + Base.__init__(self) + self.ctx = ctx + @keyword def execute_javascript(self, *code): """Executes the given JavaScript code. @@ -35,9 +39,10 @@ def execute_javascript(self, *code): | Should Be Equal | ${sum} | ${2} | """ js = self._get_javascript_to_execute(''.join(code)) - self._info("Executing JavaScript:\n%s" % js) - return self._current_browser().execute_script(js) + self.info("Executing JavaScript:\n%s" % js) + return self.ctx.current_browser().execute_script(js) + @keyword def execute_async_javascript(self, *code): """Executes asynchronous JavaScript code. @@ -59,10 +64,8 @@ def execute_async_javascript(self, *code): | Should Be Equal | ${retval} | text | """ js = self._get_javascript_to_execute(''.join(code)) - self._info("Executing Asynchronous JavaScript:\n%s" % js) - return self._current_browser().execute_async_script(js) - - # Private + self.info("Executing Asynchronous JavaScript:\n%s" % js) + return self.ctx.current_browser().execute_async_script(js) def _get_javascript_to_execute(self, code): codepath = code.replace('/', os.sep) diff --git a/src/Selenium2Library/keywords/keywordgroup.py b/src/Selenium2Library/keywords/keywordgroup.py deleted file mode 100644 index 47b43486f..000000000 --- a/src/Selenium2Library/keywords/keywordgroup.py +++ /dev/null @@ -1,37 +0,0 @@ -import sys -import inspect - -from decorator import decorator - - -def _run_on_failure_decorator(method, *args, **kwargs): - self = args[0] - already_in_keyword = getattr(self, "_already_in_keyword", False) # If False, we are in the outermost keyword (or in `run_keyword`, if it's a dynamic library) - self._already_in_keyword = True # Set a flag on the instance so that as we call keywords inside this call and this gets run again, we know we're at least one level in. - try: - return method(*args, **kwargs) - except Exception as err: - if hasattr(self, '_run_on_failure') and not self._has_run_on_failure: - # If we're in an inner keyword, track the fact that we've already run on failure once - self._has_run_on_failure = True - self._run_on_failure() - raise - finally: - if not already_in_keyword: - # If we are in the outer call, reset the flags. - self._already_in_keyword = False - self._has_run_on_failure = False - - -class KeywordGroupMetaClass(type): - - def __new__(cls, clsname, bases, dict): - if decorator: - for name, method in list(dict.items()): - if not name.startswith('_') and inspect.isroutine(method): - dict[name] = decorator(_run_on_failure_decorator, method) - return type.__new__(cls, clsname, bases, dict) - - -class KeywordGroup(object): - __metaclass__ = KeywordGroupMetaClass diff --git a/src/Selenium2Library/keywords/logging.py b/src/Selenium2Library/keywords/logging.py deleted file mode 100644 index 962dbf1c1..000000000 --- a/src/Selenium2Library/keywords/logging.py +++ /dev/null @@ -1,53 +0,0 @@ -import os - -from robot.api import logger -from robot.libraries.BuiltIn import BuiltIn -try: - from robot.libraries.BuiltIn import RobotNotRunningError -except ImportError: # Support RF < 2.8.5 - RobotNotRunningError = AttributeError - -from .keywordgroup import KeywordGroup - - -class LoggingKeywords(KeywordGroup): - - # Private - - def _debug(self, message): - logger.debug(message) - - def _get_log_dir(self): - try: - variables = BuiltIn().get_variables() - - logfile = variables['${LOG FILE}'] - if logfile != 'NONE': - return os.path.dirname(logfile) - return variables['${OUTPUTDIR}'] - - except RobotNotRunningError: - return os.getcwd() - - def _html(self, message): - logger.info(message, True, False) - - def _info(self, message): - logger.info(message) - - def _log(self, message, level='INFO'): - level = level.upper() - if (level == 'INFO'): self._info(message) - elif (level == 'DEBUG'): self._debug(message) - elif (level == 'WARN'): self._warn(message) - elif (level == 'HTML'): self._html(message) - - def _log_list(self, items, what='item'): - msg = ['Altogether %d %s%s.' % (len(items), what, ['s',''][len(items)==1])] - for index, item in enumerate(items): - msg.append('%d: %s' % (index+1, item)) - self._info('\n'.join(msg)) - return items - - def _warn(self, message): - logger.warn(message) diff --git a/src/Selenium2Library/keywords/runonfailure.py b/src/Selenium2Library/keywords/runonfailure.py index 8c6a26ccb..5c65b2711 100644 --- a/src/Selenium2Library/keywords/runonfailure.py +++ b/src/Selenium2Library/keywords/runonfailure.py @@ -1,16 +1,18 @@ from robot.libraries.BuiltIn import BuiltIn -from .keywordgroup import KeywordGroup +from Selenium2Library.base import Base +from Selenium2Library.robotlibcore import keyword -class RunOnFailureKeywords(KeywordGroup): +class RunOnFailureKeywords(Base): - def __init__(self): + def __init__(self, ctx): + Base.__init__(self) + self.ctx = ctx self._run_on_failure_keyword = None self._running_on_failure_routine = False - # Public - + @keyword def register_keyword_to_run_on_failure(self, keyword): """Sets the keyword to execute when a Selenium2Library keyword fails. @@ -40,13 +42,12 @@ def register_keyword_to_run_on_failure(self, keyword): new_keyword_text = new_keyword if new_keyword is not None else "No keyword" self._run_on_failure_keyword = new_keyword - self._info('%s will be run on failure.' % new_keyword_text) + self.info('%s will be run on failure.' % new_keyword_text) return old_keyword_text - # Private - def _run_on_failure(self): + self._run_on_failure_keyword = True if self._run_on_failure_keyword is None: return if self._running_on_failure_routine: diff --git a/src/Selenium2Library/keywords/screenshot.py b/src/Selenium2Library/keywords/screenshot.py index e0d573070..a072b2de6 100644 --- a/src/Selenium2Library/keywords/screenshot.py +++ b/src/Selenium2Library/keywords/screenshot.py @@ -3,20 +3,21 @@ from robot.utils import get_link_path +from Selenium2Library.base import Base +from Selenium2Library.robotlibcore import keyword from Selenium2Library.utils import events -from .keywordgroup import KeywordGroup +class ScreenshotKeywords(Base): -class ScreenshotKeywords(KeywordGroup): - - def __init__(self): + def __init__(self, ctx): + Base.__init__(self) + self.ctx = ctx self._screenshot_index = {} self._screenshot_path_stack = [] self.screenshot_root_directory = None - # Public - + @keyword def set_screenshot_directory(self, path, persist=False): """Sets the root output directory for captured screenshots. @@ -35,6 +36,7 @@ def set_screenshot_directory(self, path, persist=False): self._restore_screenshot_directory) self.screenshot_root_directory = path + @keyword def capture_page_screenshot(self, filename='selenium-screenshot-{index}.png'): """Takes a screenshot of the current page and embeds it into the log. @@ -89,18 +91,20 @@ def capture_page_screenshot(self, """ path, link = self._get_screenshot_paths(filename) self._create_directory(path) - if hasattr(self._current_browser(), 'get_screenshot_as_file'): - if not self._current_browser().get_screenshot_as_file(path): + if hasattr(self.ctx.current_browser(), 'get_screenshot_as_file'): + if not self.ctx.current_browser().get_screenshot_as_file(path): raise RuntimeError('Failed to save screenshot ' + link) else: - if not self._current_browser().save_screenshot(path): + if not self.ctx.current_browser().save_screenshot(path): raise RuntimeError('Failed to save screenshot ' + link) # Image is shown on its own row and thus prev row is closed on purpose - self._html('' - '' % (link, link)) + msg = ( + '' + ''.format(link, link) + ) + self.info(msg, html=True) return path - # Private def _create_directory(self, path): target_dir = os.path.dirname(path) if not os.path.exists(target_dir): diff --git a/src/Selenium2Library/keywords/selectelement.py b/src/Selenium2Library/keywords/selectelement.py index bc39f98ea..20323e8bc 100644 --- a/src/Selenium2Library/keywords/selectelement.py +++ b/src/Selenium2Library/keywords/selectelement.py @@ -1,13 +1,19 @@ from selenium.common.exceptions import NoSuchElementException from selenium.webdriver.support.ui import Select -from .keywordgroup import KeywordGroup +from Selenium2Library.base import Base +from Selenium2Library.keywords.element import ElementKeywords +from Selenium2Library.robotlibcore import keyword -class SelectElementKeywords(KeywordGroup): +class SelectElementKeywords(Base): - # Public + def __init__(self, ctx): + Base.__init__(self) + self.ctx = ctx + self.element_find = ElementKeywords(ctx).element_find + @keyword def get_list_items(self, locator, value=False): """Returns the labels or values in the select list identified by `locator`. @@ -27,6 +33,7 @@ def get_list_items(self, locator, value=False): else: return self._get_labels_for_options(options) + @keyword def get_selected_list_label(self, locator): """Returns the visible label of the selected element from the select list identified by `locator`. @@ -37,6 +44,7 @@ def get_selected_list_label(self, locator): select = self._get_select_list(locator) return select.first_selected_option.text + @keyword def get_selected_list_labels(self, locator): """Returns the visible labels of selected elements (as a list) from the select list identified by `locator`. @@ -51,6 +59,7 @@ def get_selected_list_labels(self, locator): raise ValueError("Select list with locator '%s' does not have any selected values") return self._get_labels_for_options(options) + @keyword def get_selected_list_value(self, locator): """Returns the value of the selected element from the select list identified by `locator`. @@ -63,6 +72,7 @@ def get_selected_list_value(self, locator): select = self._get_select_list(locator) return select.first_selected_option.get_attribute('value') + @keyword def get_selected_list_values(self, locator): """Returns the values of selected elements (as a list) from the select list identified by `locator`. @@ -77,6 +87,7 @@ def get_selected_list_values(self, locator): raise ValueError("Select list with locator '%s' does not have any selected values") return self._get_values_for_options(options) + @keyword def list_selection_should_be(self, locator, *items): """Verifies the selection of select list identified by `locator` is exactly `*items`. @@ -87,7 +98,7 @@ def list_selection_should_be(self, locator, *items): locating elements. """ items_str = items and "option(s) [ %s ]" % " | ".join(items) or "no options" - self._info("Verifying list '%s' has %s selected." % (locator, items_str)) + self.info("Verifying list '%s' has %s selected." % (locator, items_str)) items = list(items) self.page_should_contain_list(locator) select, options = self._get_select_list_options_selected(locator) @@ -104,6 +115,7 @@ def list_selection_should_be(self, locator, *items): if selected_value not in items and selected_label not in items: raise AssertionError(err) + @keyword def list_should_have_no_selections(self, locator): """Verifies select list identified by `locator` has no selections. @@ -111,7 +123,7 @@ def list_should_have_no_selections(self, locator): select lists are `id` and `name`. See `introduction` for details about locating elements. """ - self._info("Verifying list '%s' has no selection." % locator) + self.info("Verifying list '%s' has no selection." % locator) select, options = self._get_select_list_options_selected(locator) if options: selected_labels = self._get_labels_for_options(options) @@ -119,6 +131,7 @@ def list_should_have_no_selections(self, locator): raise AssertionError("List '%s' should have had no selection " "(selection was [ %s ])" % (locator, items_str)) + @keyword def page_should_contain_list(self, locator, message='', loglevel='INFO'): """Verifies select list identified by `locator` is found from current page. @@ -130,6 +143,7 @@ def page_should_contain_list(self, locator, message='', loglevel='INFO'): """ self._page_should_contain_element(locator, 'list', message, loglevel) + @keyword def page_should_not_contain_list(self, locator, message='', loglevel='INFO'): """Verifies select list identified by `locator` is not found from current page. @@ -141,13 +155,14 @@ def page_should_not_contain_list(self, locator, message='', loglevel='INFO'): """ self._page_should_not_contain_element(locator, 'list', message, loglevel) + @keyword def select_all_from_list(self, locator): """Selects all values from multi-select list identified by `id`. Key attributes for lists are `id` and `name`. See `introduction` for details about locating elements. """ - self._info("Selecting all options from list '%s'." % locator) + self.info("Selecting all options from list '%s'." % locator) select = self._get_select_list(locator) if not select.is_multiple: @@ -156,6 +171,7 @@ def select_all_from_list(self, locator): for i in range(len(select.options)): select.select_by_index(i) + @keyword def select_from_list(self, locator, *items): """Selects `*items` from list identified by `locator` @@ -179,7 +195,7 @@ def select_from_list(self, locator, *items): non_existing_items = [] items_str = items and "option(s) '%s'" % ", ".join(items) or "all options" - self._info("Selecting %s from list '%s'." % (items_str, locator)) + self.info("Selecting %s from list '%s'." % (items_str, locator)) select = self._get_select_list(locator) @@ -208,6 +224,7 @@ def select_from_list(self, locator, *items): if items and items[-1] in non_existing_items: raise ValueError("Option '%s' not in list '%s'." % (items[-1], locator)) + @keyword def select_from_list_by_index(self, locator, *indexes): """Selects `*indexes` from list identified by `locator` @@ -218,12 +235,13 @@ def select_from_list_by_index(self, locator, *indexes): if not indexes: raise ValueError("No index given.") items_str = "index(es) '%s'" % ", ".join(indexes) - self._info("Selecting %s from list '%s'." % (items_str, locator)) + self.info("Selecting %s from list '%s'." % (items_str, locator)) select = self._get_select_list(locator) for index in indexes: select.select_by_index(int(index)) + @keyword def select_from_list_by_value(self, locator, *values): """Selects `*values` from list identified by `locator` @@ -234,12 +252,13 @@ def select_from_list_by_value(self, locator, *values): if not values: raise ValueError("No value given.") items_str = "value(s) '%s'" % ", ".join(values) - self._info("Selecting %s from list '%s'." % (items_str, locator)) + self.info("Selecting %s from list '%s'." % (items_str, locator)) select = self._get_select_list(locator) for value in values: select.select_by_value(value) + @keyword def select_from_list_by_label(self, locator, *labels): """Selects `*labels` from list identified by `locator` @@ -250,12 +269,13 @@ def select_from_list_by_label(self, locator, *labels): if not labels: raise ValueError("No value given.") items_str = "label(s) '%s'" % ", ".join(labels) - self._info("Selecting %s from list '%s'." % (items_str, locator)) + self.info("Selecting %s from list '%s'." % (items_str, locator)) select = self._get_select_list(locator) for label in labels: select.select_by_visible_text(label) + @keyword def unselect_from_list(self, locator, *items): """Unselects given values from select list identified by locator. @@ -271,7 +291,7 @@ def unselect_from_list(self, locator, *items): locating elements. """ items_str = items and "option(s) '%s'" % ", ".join(items) or "all options" - self._info("Unselecting %s from list '%s'." % (items_str, locator)) + self.info("Unselecting %s from list '%s'." % (items_str, locator)) select = self._get_select_list(locator) if not select.is_multiple: @@ -294,6 +314,7 @@ def unselect_from_list(self, locator, *items): except NoSuchElementException: pass + @keyword def unselect_from_list_by_index(self, locator, *indexes): """Unselects `*indexes` from list identified by `locator` @@ -305,7 +326,7 @@ def unselect_from_list_by_index(self, locator, *indexes): raise ValueError("No index given.") items_str = "index(es) '%s'" % ", ".join(indexes) - self._info("Unselecting %s from list '%s'." % (items_str, locator)) + self.info("Unselecting %s from list '%s'." % (items_str, locator)) select = self._get_select_list(locator) if not select.is_multiple: @@ -314,6 +335,7 @@ def unselect_from_list_by_index(self, locator, *indexes): for index in indexes: select.deselect_by_index(int(index)) + @keyword def unselect_from_list_by_value(self, locator, *values): """Unselects `*values` from list identified by `locator` @@ -324,7 +346,7 @@ def unselect_from_list_by_value(self, locator, *values): if not values: raise ValueError("No value given.") items_str = "value(s) '%s'" % ", ".join(values) - self._info("Unselecting %s from list '%s'." % (items_str, locator)) + self.info("Unselecting %s from list '%s'." % (items_str, locator)) select = self._get_select_list(locator) if not select.is_multiple: @@ -333,6 +355,7 @@ def unselect_from_list_by_value(self, locator, *values): for value in values: select.deselect_by_value(value) + @keyword def unselect_from_list_by_label(self, locator, *labels): """Unselects `*labels` from list identified by `locator` @@ -343,7 +366,7 @@ def unselect_from_list_by_label(self, locator, *labels): if not labels: raise ValueError("No value given.") items_str = "label(s) '%s'" % ", ".join(labels) - self._info("Unselecting %s from list '%s'." % (items_str, locator)) + self.info("Unselecting %s from list '%s'." % (items_str, locator)) select = self._get_select_list(locator) if not select.is_multiple: @@ -352,8 +375,6 @@ def unselect_from_list_by_label(self, locator, *labels): for label in labels: select.deselect_by_visible_text(label) - # Private - def _get_labels_for_options(self, options): labels = [] for option in options: @@ -361,7 +382,7 @@ def _get_labels_for_options(self, options): return labels def _get_select_list(self, locator): - el = self._element_find(locator, True, True, 'select') + el = self.element_find(locator, True, True, 'select') return Select(el) def _get_select_list_options(self, select_list_or_locator): @@ -389,7 +410,7 @@ def _is_multiselect_list(self, select): return False def _unselect_all_options_from_multi_select_list(self, select): - self._current_browser().execute_script("arguments[0].selectedIndex = -1;", select) + self.ctx.current_browser().execute_script("arguments[0].selectedIndex = -1;", select) def _unselect_option_from_multi_select_list(self, select, options, index): if options[index].is_selected(): diff --git a/src/Selenium2Library/robotlibcore.py b/src/Selenium2Library/robotlibcore.py new file mode 100644 index 000000000..e0b8ee6af --- /dev/null +++ b/src/Selenium2Library/robotlibcore.py @@ -0,0 +1,101 @@ +import inspect +import sys + +try: + from robot.api.deco import keyword +except ImportError: # Support RF < 2.9 + def keyword(name=None, tags=()): + if callable(name): + return keyword()(name) + def decorator(func): + func.robot_name = name + func.robot_tags = tags + return func + return decorator + + +PY2 = sys.version_info < (3,) + + +class HybridCore(object): + + def __init__(self, libraries): + self.keywords = dict(self._find_keywords(*libraries)) + self.keywords.update(self._find_keywords(self)) + + def _find_keywords(self, *libraries): + for library in libraries: + for name, func in self._get_members(library): + if callable(func) and hasattr(func, 'robot_name'): + kw_name = func.robot_name or name + yield kw_name, getattr(library, name) + + def _get_members(self, library): + # Avoid calling properties by getting members from class, not instance. + if inspect.isclass(library): + library = type(library) + return inspect.getmembers(library) + + def __getattr__(self, name): + if name in self.keywords: + return self.keywords[name] + raise AttributeError('{!r} object has no attribute {!r}' + .format(type(self).__name__, name)) + + def __dir__(self): + if PY2: + my_attrs = dir(type(self)) + list(self.__dict__) + else: + my_attrs = super().__dir__() + return sorted(set(my_attrs + list(self.keywords))) + + def get_keyword_names(self): + return sorted(self.keywords) + + +class DynamicCore(HybridCore): + _get_keyword_tags_supported = False # get_keyword_tags is new in RF 3.0.2 + + def run_keyword(self, name, args, kwargs): + return self.keywords[name](*args, **kwargs) + + def get_keyword_arguments(self, name): + kw = self.keywords[name] if name != '__init__' else self.__init__ + args, defaults, varargs, kwargs = self._get_arg_spec(kw) + args += ['{}={}'.format(name, value) for name, value in defaults] + if varargs: + args.append('*{}'.format(varargs)) + if kwargs: + args.append('**{}'.format(kwargs)) + return args + + def _get_arg_spec(self, kw): + spec = inspect.getargspec(kw) + args = spec.args[1:] if inspect.ismethod(kw) else spec.args # drop self + defaults = spec.defaults or () + nargs = len(args) - len(defaults) + mandatory = args[:nargs] + defaults = zip(args[nargs:], defaults) + return mandatory, defaults, spec.varargs, spec.keywords + + def get_keyword_tags(self, name): + self._get_keyword_tags_supported = True + return self.keywords[name].robot_tags + + def get_keyword_documentation(self, name): + if name == '__intro__': + return inspect.getdoc(self) or '' + if name == '__init__': + return inspect.getdoc(self.__init__) or '' + kw = self.keywords[name] + doc = inspect.getdoc(kw) or '' + if kw.robot_tags and not self._get_keyword_tags_supported: + tags = 'Tags: {}'.format(', '.join(kw.robot_tags)) + doc = '{}\n\n{}'.format(doc, tags) if doc else tags + return doc + + +class StaticCore(HybridCore): + + def __init__(self): + HybridCore.__init__(self, []) From 0edb1076b6b4570c92e31d476b1dff77f60bcc3e Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Mon, 6 Mar 2017 23:41:52 +0200 Subject: [PATCH 02/16] Fixing keyword classes to use new architecture --- src/Selenium2Library/__init__.py | 4 +- .../keywords/browsermanagement.py | 10 +- src/Selenium2Library/keywords/element.py | 26 +++--- src/Selenium2Library/keywords/tableelement.py | 57 +++++++----- src/Selenium2Library/keywords/waiting.py | 47 ++++++---- src/Selenium2Library/utils/browsercache.py | 2 +- test/unit/keywords/test_browsermanagement.py | 91 +++++++++++-------- 7 files changed, 135 insertions(+), 102 deletions(-) diff --git a/src/Selenium2Library/__init__.py b/src/Selenium2Library/__init__.py index b373989be..ae67b861f 100644 --- a/src/Selenium2Library/__init__.py +++ b/src/Selenium2Library/__init__.py @@ -216,9 +216,9 @@ def run_keyword(self, name, args, kwargs): def current_browser(self): """returns the current active browser""" - if not self._cache.current: + if not self.cache.current: raise RuntimeError('No browser is open') return self.cache.current def register_browser(self, browser, alias): - self.cache.register(browser, alias) + return self.cache.register(browser, alias) diff --git a/src/Selenium2Library/keywords/browsermanagement.py b/src/Selenium2Library/keywords/browsermanagement.py index 086d0a2c1..1b9a561ac 100644 --- a/src/Selenium2Library/keywords/browsermanagement.py +++ b/src/Selenium2Library/keywords/browsermanagement.py @@ -60,13 +60,13 @@ def close_all_browsers(self): @keyword def close_browser(self): """Closes the current browser.""" - if self.ctx.cache.current: + if self.current_browser(): self.debug( 'Closing browser with session id {}'.format( - self.ctx.cache.current.session_id + self.current_browser().session_id ) ) - self.ctx.cache.close() + self.current_browser().close() @keyword def open_browser( @@ -222,7 +222,7 @@ def switch_browser(self, index_or_alias): try: self.ctx.cache.switch(index_or_alias) self.debug('Switched to browser with Selenium session id %s' - % self._cache.current.session_id) + % self.ctx.current_browser().current.session_id) except (RuntimeError, DataError): # RF 2.6 uses RE, earlier DE raise RuntimeError("No browser with index or alias '%s' found." % index_or_alias) @@ -529,7 +529,7 @@ def set_selenium_timeout(self, seconds): """ old_timeout = self.get_selenium_timeout() self._timeout_in_secs = timestr_to_secs(seconds) - for browser in self.ctc.cache.get_open_browsers(): + for browser in self.ctx.cache.get_open_browsers(): browser.set_script_timeout(self._timeout_in_secs) return old_timeout diff --git a/src/Selenium2Library/keywords/element.py b/src/Selenium2Library/keywords/element.py index 3c06be0ae..af1af2ea6 100644 --- a/src/Selenium2Library/keywords/element.py +++ b/src/Selenium2Library/keywords/element.py @@ -38,7 +38,7 @@ def current_frame_contains(self, text, loglevel='INFO'): See `Page Should Contain ` for explanation about `loglevel` argument. """ - if not self._is_text_present(text): + if not self.is_text_present(text): self.ctx.log_source(loglevel) raise AssertionError("Page should have contained text '%s' " "but did not" % text) @@ -50,7 +50,7 @@ def current_frame_should_not_contain(self, text, loglevel='INFO'): See `Page Should Contain ` for explanation about `loglevel` argument. """ - if self._is_text_present(text): + if self.is_text_present(text): self.ctx.log_source(loglevel) raise AssertionError("Page should not have contained text '%s' " "but it did" % text) @@ -236,7 +236,7 @@ def element_should_be_visible(self, locator, message=''): `introduction` for details about locating elements. """ self.info("Verifying element '%s' is visible." % locator) - visible = self._is_visible(locator) + visible = self.is_visible(locator) if not visible: if not message: message = "The element '%s' should be visible, but it "\ @@ -255,7 +255,7 @@ def element_should_not_be_visible(self, locator, message=''): `introduction` for details about locating elements. """ self.info("Verifying element '%s' is not visible." % locator) - visible = self._is_visible(locator) + visible = self.is_visible(locator) if visible: if not message: message = "The element '%s' should not be visible, "\ @@ -774,7 +774,7 @@ def _frame_contains(self, locator, text): element = self.element_find(locator, True, True) browser.switch_to_frame(element) self.info("Searching for text from frame '%s'." % locator) - found = self._is_text_present(text) + found = self.is_text_present(text) browser.switch_to_default_content() return found @@ -799,11 +799,11 @@ def _is_enabled(self, locator): return False return True - def _is_text_present(self, text): + def is_text_present(self, text): locator = "xpath=//*[contains(., %s)]" % utils.escape_xpath_value(text) - return self._is_element_present(locator) + return self.is_element_present(locator) - def _is_visible(self, locator): + def is_visible(self, locator): element = self.element_find(locator, True, False) if element is not None: return element.is_displayed() @@ -850,21 +850,21 @@ def _parse_attribute_locator(self, attribute_locator): raise ValueError("Attribute locator '%s' does not contain an attribute name." % (attribute_locator)) return parts[0], parts[2] - def _is_element_present(self, locator, tag=None): + def is_element_present(self, locator, tag=None): return self.element_find(locator, True, False, tag=tag) is not None def _page_contains(self, text): browser = self.ctx.current_browser() browser.switch_to_default_content() - if self._is_text_present(text): + if self.is_text_present(text): return True subframes = self.element_find("xpath=//frame|//iframe", False, False) self._debug('Current frame has %d subframes' % len(subframes)) for frame in subframes: browser.switch_to_frame(frame) - found_text = self._is_text_present(text) + found_text = self.is_text_present(text) browser.switch_to_default_content() if found_text: return True @@ -872,7 +872,7 @@ def _page_contains(self, text): def _page_should_contain_element(self, locator, tag, message, loglevel): element_name = tag if tag is not None else 'element' - if not self._is_element_present(locator, tag): + if not self.is_element_present(locator, tag): if not message: message = "Page should have contained %s '%s' but did not"\ % (element_name, locator) @@ -882,7 +882,7 @@ def _page_should_contain_element(self, locator, tag, message, loglevel): def _page_should_not_contain_element(self, locator, tag, message, loglevel): element_name = tag if tag is not None else 'element' - if self._is_element_present(locator, tag): + if self.is_element_present(locator, tag): if not message: message = "Page should not have contained %s '%s'"\ % (element_name, locator) diff --git a/src/Selenium2Library/keywords/tableelement.py b/src/Selenium2Library/keywords/tableelement.py index 6408344e5..087aa9f29 100644 --- a/src/Selenium2Library/keywords/tableelement.py +++ b/src/Selenium2Library/keywords/tableelement.py @@ -1,15 +1,16 @@ +from Selenium2Library.base import Base from Selenium2Library.locators import TableElementFinder +from Selenium2Library.robotlibcore import keyword -from .keywordgroup import KeywordGroup +class TableElementKeywords(Base): -class TableElementKeywords(KeywordGroup): - - def __init__(self): + def __init__(self, ctx): + Base.__init__(self) + self.ctx = ctx self._table_element_finder = TableElementFinder() - # Public - + @keyword def get_table_cell(self, table_locator, row, column, loglevel='INFO'): """Returns the content from a table cell. @@ -23,11 +24,13 @@ def get_table_cell(self, table_locator, row, column, loglevel='INFO'): """ row = int(row) row_index = row - if row > 0: row_index = row - 1 + if row > 0: + row_index = row - 1 column = int(column) column_index = column - if column > 0: column_index = column - 1 - table = self._table_element_finder.find(self._current_browser(), table_locator) + if column > 0: + column_index = column - 1 + table = self._table_element_finder.find(self.ctx.current_browser(), table_locator) if table is not None: rows = table.find_elements_by_xpath("./thead/tr") if row_index >= len(rows) or row_index < 0: @@ -40,10 +43,11 @@ def get_table_cell(self, table_locator, row, column, loglevel='INFO'): columns.extend(rows[row_index].find_elements_by_tag_name('td')) if column_index < len(columns): return columns[column_index].text - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Cell in table %s in row #%s and column #%s could not be found." % (table_locator, str(row), str(column))) + @keyword def table_cell_should_contain(self, table_locator, row, column, expected, loglevel='INFO'): """Verifies that a certain cell in a table contains `expected`. @@ -65,14 +69,15 @@ def table_cell_should_contain(self, table_locator, row, column, expected, loglev try: content = self.get_table_cell(table_locator, row, column, loglevel='NONE') except AssertionError as err: - self._info(err) - self.log_source(loglevel) + self.info(err) + self.ctx.log_source(loglevel) raise AssertionError(message) - self._info("Cell contains %s." % (content)) + self.info("Cell contains %s." % (content)) if expected not in content: - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError(message) + @keyword def table_column_should_contain(self, table_locator, col, expected, loglevel='INFO'): """Verifies that a specific column contains `expected`. @@ -93,13 +98,14 @@ def table_column_should_contain(self, table_locator, col, expected, loglevel='IN See `Page Should Contain Element` for explanation about `loglevel` argument. """ - element = self._table_element_finder.find_by_col(self._current_browser(), table_locator, col, expected) + element = self._table_element_finder.find_by_col(self.ctx.current_browser(), table_locator, col, expected) if element is None: - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Column #%s in table identified by '%s' " "should have contained text '%s'." % (col, table_locator, expected)) + @keyword def table_footer_should_contain(self, table_locator, expected, loglevel='INFO'): """Verifies that the table footer contains `expected`. @@ -110,12 +116,13 @@ def table_footer_should_contain(self, table_locator, expected, loglevel='INFO'): See `Page Should Contain Element` for explanation about `loglevel` argument. """ - element = self._table_element_finder.find_by_footer(self._current_browser(), table_locator, expected) + element = self._table_element_finder.find_by_footer(self.ctx.current_browser(), table_locator, expected) if element is None: - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Footer in table identified by '%s' should have contained " "text '%s'." % (table_locator, expected)) + @keyword def table_header_should_contain(self, table_locator, expected, loglevel='INFO'): """Verifies that the table header, i.e. any ... element, contains `expected`. @@ -125,12 +132,13 @@ def table_header_should_contain(self, table_locator, expected, loglevel='INFO'): See `Page Should Contain Element` for explanation about `loglevel` argument. """ - element = self._table_element_finder.find_by_header(self._current_browser(), table_locator, expected) + element = self._table_element_finder.find_by_header(self.ctx.current_browser(), table_locator, expected) if element is None: - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Header in table identified by '%s' should have contained " "text '%s'." % (table_locator, expected)) + @keyword def table_row_should_contain(self, table_locator, row, expected, loglevel='INFO'): """Verifies that a specific table row contains `expected`. @@ -148,12 +156,13 @@ def table_row_should_contain(self, table_locator, row, expected, loglevel='INFO' See `Page Should Contain Element` for explanation about `loglevel` argument. """ - element = self._table_element_finder.find_by_row(self._current_browser(), table_locator, row, expected) + element = self._table_element_finder.find_by_row(self.ctx.current_browser(), table_locator, row, expected) if element is None: - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Row #%s in table identified by '%s' should have contained " "text '%s'." % (row, table_locator, expected)) + @keyword def table_should_contain(self, table_locator, expected, loglevel='INFO'): """Verifies that `expected` can be found somewhere in the table. @@ -163,8 +172,8 @@ def table_should_contain(self, table_locator, expected, loglevel='INFO'): See `Page Should Contain Element` for explanation about `loglevel` argument. """ - element = self._table_element_finder.find_by_content(self._current_browser(), table_locator, expected) + element = self._table_element_finder.find_by_content(self.ctx.current_browser(), table_locator, expected) if element is None: - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Table identified by '%s' should have contained text '%s'." \ % (table_locator, expected)) diff --git a/src/Selenium2Library/keywords/waiting.py b/src/Selenium2Library/keywords/waiting.py index 935fb8d59..491639b58 100644 --- a/src/Selenium2Library/keywords/waiting.py +++ b/src/Selenium2Library/keywords/waiting.py @@ -2,13 +2,19 @@ from robot.utils import secs_to_timestr, timestr_to_secs -from .keywordgroup import KeywordGroup +from Selenium2Library.base import Base +from Selenium2Library.keywords.element import ElementKeywords +from Selenium2Library.robotlibcore import keyword -class WaitingKeywords(KeywordGroup): +class WaitingKeywords(Base): - # Public + def __init__(self, ctx): + Base.__init__(self) + self.ctx = ctx + self.element = ElementKeywords(self.ctx) + @keyword def wait_for_condition(self, condition, timeout=None, error=None): """Waits until the given `condition` is true or `timeout` expires. @@ -28,9 +34,11 @@ def wait_for_condition(self, condition, timeout=None, error=None): """ if not error: error = "Condition '%s' did not become true in " % condition - self._wait_until(timeout, error, - lambda: self._current_browser().execute_script(condition) == True) + self._wait_until( + timeout, error, + lambda: self.ctx.current_browser().execute_script(condition) is True) + @keyword def wait_until_page_contains(self, text, timeout=None, error=None): """Waits until `text` appears on current page. @@ -46,8 +54,9 @@ def wait_until_page_contains(self, text, timeout=None, error=None): """ if not error: error = "Text '%s' did not appear in " % text - self._wait_until(timeout, error, self._is_text_present, text) + self._wait_until(timeout, error, self.element.is_text_present, text) + @keyword def wait_until_page_does_not_contain(self, text, timeout=None, error=None): """Waits until `text` disappears from current page. @@ -62,13 +71,14 @@ def wait_until_page_does_not_contain(self, text, timeout=None, error=None): Keyword Succeeds`. """ def check_present(): - present = self._is_text_present(text) + present = self.element.is_text_present(text) if not present: return else: return error or "Text '%s' did not disappear in %s" % (text, self._format_timeout(timeout)) self._wait_until_no_error(timeout, check_present) + @keyword def wait_until_page_contains_element(self, locator, timeout=None, error=None): """Waits until element specified with `locator` appears on current page. @@ -86,6 +96,7 @@ def wait_until_page_contains_element(self, locator, timeout=None, error=None): error = "Element '%s' did not appear in " % locator self._wait_until(timeout, error, self._is_element_present, locator) + @keyword def wait_until_page_does_not_contain_element(self, locator, timeout=None, error=None): """Waits until element specified with `locator` disappears from current page. @@ -100,13 +111,14 @@ def wait_until_page_does_not_contain_element(self, locator, timeout=None, error= Keyword Succeeds`. """ def check_present(): - present = self._is_element_present(locator) + present = self.element.is_element_present(locator) if not present: return else: return error or "Element '%s' did not disappear in %s" % (locator, self._format_timeout(timeout)) self._wait_until_no_error(timeout, check_present) + @keyword def wait_until_element_is_visible(self, locator, timeout=None, error=None): """Waits until element specified with `locator` is visible. @@ -121,7 +133,7 @@ def wait_until_element_is_visible(self, locator, timeout=None, error=None): Succeeds`. """ def check_visibility(): - visible = self._is_visible(locator) + visible = self.element.is_visible(locator) if visible: return elif visible is None: @@ -130,6 +142,7 @@ def check_visibility(): return error or "Element '%s' was not visible in %s" % (locator, self._format_timeout(timeout)) self._wait_until_no_error(timeout, check_visibility) + @keyword def wait_until_element_is_not_visible(self, locator, timeout=None, error=None): """Waits until element specified with `locator` is not visible. @@ -144,7 +157,7 @@ def wait_until_element_is_not_visible(self, locator, timeout=None, error=None): Succeeds`. """ def check_hidden(): - visible = self._is_visible(locator) + visible = self.element.is_visible(locator) if not visible: return elif visible is None: @@ -153,6 +166,7 @@ def check_hidden(): return error or "Element '%s' was still visible in %s" % (locator, self._format_timeout(timeout)) self._wait_until_no_error(timeout, check_hidden) + @keyword def wait_until_element_is_enabled(self, locator, timeout=None, error=None): """Waits until element specified with `locator` is enabled. @@ -167,7 +181,7 @@ def wait_until_element_is_enabled(self, locator, timeout=None, error=None): Succeeds`. """ def check_enabled(): - element = self._element_find(locator, True, False) + element = self.element.element_find(locator, True, False) if not element: return error or "Element locator '%s' did not match any elements after %s" % (locator, self._format_timeout(timeout)) @@ -179,6 +193,7 @@ def check_enabled(): self._wait_until_no_error(timeout, check_enabled) + @keyword def wait_until_element_contains(self, locator, text, timeout=None, error=None): """Waits until given element contains `text`. @@ -192,7 +207,7 @@ def wait_until_element_contains(self, locator, text, timeout=None, error=None): `Wait Until Element Is Visible` and BuiltIn keyword `Wait Until Keyword Succeeds`. """ - element = self._element_find(locator, True, True) + element = self.element.element_find(locator, True, True) def check_text(): actual = element.text if text in actual: @@ -202,7 +217,7 @@ def check_text(): "Its text was '%s'." % (text, self._format_timeout(timeout), locator, actual) self._wait_until_no_error(timeout, check_text) - + @keyword def wait_until_element_does_not_contain(self, locator, text, timeout=None, error=None): """Waits until given element does not contain `text`. @@ -216,17 +231,15 @@ def wait_until_element_does_not_contain(self, locator, text, timeout=None, error `Wait Until Element Is Visible` and BuiltIn keyword `Wait Until Keyword Succeeds`. """ - element = self._element_find(locator, True, True) + element = self.element.element_find(locator, True, True) def check_text(): actual = element.text - if not text in actual: + if text not in actual: return else: return error or "Text '%s' did not disappear in %s from element '%s'." % (text, self._format_timeout(timeout), locator) self._wait_until_no_error(timeout, check_text) - # Private - def _wait_until(self, timeout, error, function, *args): error = error.replace('', self._format_timeout(timeout)) def wait_func(): diff --git a/src/Selenium2Library/utils/browsercache.py b/src/Selenium2Library/utils/browsercache.py index 14667000b..35a2af79b 100644 --- a/src/Selenium2Library/utils/browsercache.py +++ b/src/Selenium2Library/utils/browsercache.py @@ -17,7 +17,7 @@ def get_open_browsers(self): if browser not in self._closed: open_browsers.append(browser) return open_browsers - + def close(self): if self.current: browser = self.current diff --git a/test/unit/keywords/test_browsermanagement.py b/test/unit/keywords/test_browsermanagement.py index 2e52ec1ff..69aee5186 100644 --- a/test/unit/keywords/test_browsermanagement.py +++ b/test/unit/keywords/test_browsermanagement.py @@ -8,9 +8,11 @@ class BrowserManagementTests(unittest.TestCase): - def test_create_firefox_browser(self): - test_browsers = ((webdriver.Firefox, "ff"), (webdriver.Firefox, "firEfOx")) + test_browsers = ( + (webdriver.Firefox, "ff"), + (webdriver.Firefox, "firEfOx") + ) for test_browser in test_browsers: self.verify_browser(*test_browser) @@ -20,14 +22,20 @@ def mock_createProfile(self, profile_directory=None): return self.old_profile_init(profile_directory) def test_create_ie_browser(self): - test_browsers = ((webdriver.Ie, "ie"), (webdriver.Ie, "Internet Explorer")) + test_browsers = ( + (webdriver.Ie, "ie"), + (webdriver.Ie, "Internet Explorer") + ) for test_browser in test_browsers: self.verify_browser(*test_browser) def test_create_chrome_browser(self): - test_browsers = ((webdriver.Chrome, "gOOglEchrOmE"),(webdriver.Chrome,"gc"), - (webdriver.Chrome, "chrome")) + test_browsers = ( + (webdriver.Chrome, "gOOglEchrOmE"), + (webdriver.Chrome, "gc"), + (webdriver.Chrome, "chrome") + ) for test_browser in test_browsers: self.verify_browser(*test_browser) @@ -39,7 +47,9 @@ def test_create_phantomjs_browser(self): self.verify_browser(webdriver.PhantomJS, "PHANTOMJS") def test_create_remote_browser(self): - self.verify_browser(webdriver.Remote, "chrome", remote="http://127.0.0.1/wd/hub") + self.verify_browser( + webdriver.Remote, "chrome", remote="http://127.0.0.1/wd/hub" + ) def test_create_htmlunit_browser(self): self.verify_browser(webdriver.Remote, "htmlunit") @@ -48,7 +58,8 @@ def test_create_htmlunitwihtjs_browser(self): self.verify_browser(webdriver.Remote, "htmlunitwithjs") def test_parse_capabilities_string(self): - bm = BrowserManagementKeywords() + ctx = mock() + bm = BrowserManagementKeywords(ctx) expected_caps = "key1:val1,key2:val2" capabilities = bm._parse_capabilities_string(expected_caps) self.assertTrue("val1", capabilities["key1"]) @@ -56,7 +67,8 @@ def test_parse_capabilities_string(self): self.assertTrue(2, len(capabilities)) def test_parse_complex_capabilities_string(self): - bm = BrowserManagementKeywords() + ctx = mock() + bm = BrowserManagementKeywords(ctx) expected_caps = "proxyType:manual,httpProxy:IP:port" capabilities = bm._parse_capabilities_string(expected_caps) self.assertTrue("manual", capabilities["proxyType"]) @@ -64,38 +76,45 @@ def test_parse_complex_capabilities_string(self): self.assertTrue(2, len(capabilities)) def test_create_remote_browser_with_desired_prefs(self): - expected_caps = {"key1":"val1","key2":"val2"} - self.verify_browser(webdriver.Remote, "chrome", remote="http://127.0.0.1/wd/hub", - desired_capabilities=expected_caps) + expected_caps = {"key1": "val1", "key2": "val2"} + self.verify_browser( + webdriver.Remote, "chrome", remote="http://127.0.0.1/wd/hub", + desired_capabilities=expected_caps + ) def test_create_remote_browser_with_string_desired_prefs(self): expected_caps = "key1:val1,key2:val2" - self.verify_browser(webdriver.Remote, "chrome", remote="http://127.0.0.1/wd/hub", + self.verify_browser( + webdriver.Remote, "chrome", remote="http://127.0.0.1/wd/hub", desired_capabilities=expected_caps) def test_capabilities_attribute_not_modified(self): - expected_caps = {"some_cap":"42"} - self.verify_browser(webdriver.Remote, "chrome", remote="http://127.0.0.1/wd/hub", - desired_capabilities=expected_caps) + expected_caps = {"some_cap": "42"} + self.verify_browser( + webdriver.Remote, "chrome", remote="http://127.0.0.1/wd/hub", + desired_capabilities=expected_caps + ) self.assertFalse("some_cap" in webdriver.DesiredCapabilities.CHROME) def test_set_selenium_timeout_only_affects_open_browsers(self): - bm = BrowserManagementKeywords() + ctx = mock() + bm = BrowserManagementKeywords(ctx) first_browser, second_browser = mock(), mock() - bm._cache.register(first_browser) - bm._cache.close() + bm.ctx.cache.register(first_browser) + bm.ctx.cache.close() verify(first_browser).quit() - bm._cache.register(second_browser) + bm.ctx.cache.register(second_browser) bm.set_selenium_timeout("10 seconds") verify(second_browser).set_script_timeout(10.0) - bm._cache.close_all() + bm.ctx.cache.close_all() verify(second_browser).quit() bm.set_selenium_timeout("20 seconds") verifyNoMoreInteractions(first_browser) verifyNoMoreInteractions(second_browser) def test_bad_browser_name(self): - bm = BrowserManagementKeywords() + ctx = mock() + bm = BrowserManagementKeywords(ctx) try: bm._make_browser("fireox") self.fail("Exception not raised") @@ -103,7 +122,8 @@ def test_bad_browser_name(self): self.assertEquals(str(e), "fireox is not a supported browser.") def test_create_webdriver(self): - bm = _BrowserManagementWithLoggingStubs() + ctx = mock() + bm = BrowserManagementKeywords(ctx) FakeWebDriver = mock() driver = mock() when(FakeWebDriver).__call__(some_arg=1).thenReturn(driver) @@ -111,18 +131,19 @@ def test_create_webdriver(self): webdriver.FakeWebDriver = FakeWebDriver try: index = bm.create_webdriver('FakeWebDriver', 'fake1', some_arg=1) - self.assertEqual(bm._current_browser(), driver) - self.assertEquals(bm._cache.get_connection(index), driver) - self.assertEquals(bm._cache.get_connection('fake1'), driver) + self.assertEqual(bm.ctx.current_browser(), driver) + self.assertEquals(bm.ctx.cache.get_connection(index), driver) + self.assertEquals(bm.ctx.cache.get_connection('fake1'), driver) my_kwargs = {'some_arg': 2} bm.create_webdriver('FakeWebDriver', 'fake2', kwargs=my_kwargs) - self.assertEquals(bm._current_browser(), driver) + self.assertEquals(bm.ctx.current_browser(), driver) finally: del webdriver.FakeWebDriver - def verify_browser(self , webdriver_type , browser_name, **kw): - #todo try lambda *x: was_called = true - bm = BrowserManagementKeywords() + def verify_browser(self, webdriver_type, browser_name, **kw): + # todo try lambda *x: was_called = true + ctx = mock() + bm = BrowserManagementKeywords(ctx) old_init = webdriver_type.__init__ webdriver_type.__init__ = self.mock_init @@ -130,20 +151,10 @@ def verify_browser(self , webdriver_type , browser_name, **kw): self.was_called = False bm._make_browser(browser_name, **kw) except AttributeError: - pass #kinda dangerous but I'm too lazy to mock out all the set_timeout calls + pass #kinda dangerous but I'm too lazy to mock out all the set_timeout calls finally: webdriver_type.__init__ = old_init self.assertTrue(self.was_called) def mock_init(self, *args, **kw): self.was_called = True - - -class _BrowserManagementWithLoggingStubs(BrowserManagementKeywords): - - def __init__(self): - BrowserManagementKeywords.__init__(self) - def mock_logging_method(self, *args, **kwargs): - pass - for name in ['_info', '_debug', '_warn', '_log', '_html']: - setattr(self, name, mock_logging_method) From e109dd61b9b4bb31176401cf80a422a8952409f9 Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Wed, 8 Mar 2017 23:39:39 +0200 Subject: [PATCH 03/16] Fixed unit tests for the new architecture --- requirements.txt | 1 - test/unit/keywords/test_browsermanagement.py | 36 +++++++++++--------- test/unit/utils/test_browsercache.py | 11 +++--- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/requirements.txt b/requirements.txt index 1965bcaa2..c1c32b663 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ -decorator >= 3.3.2 selenium robotframework >= 2.6.0 diff --git a/test/unit/keywords/test_browsermanagement.py b/test/unit/keywords/test_browsermanagement.py index 69aee5186..adb6729f1 100644 --- a/test/unit/keywords/test_browsermanagement.py +++ b/test/unit/keywords/test_browsermanagement.py @@ -1,6 +1,6 @@ import unittest -from mockito import when, mock, verify, verifyNoMoreInteractions +from mockito import when, mock, verify, verifyNoMoreInteractions, unstub from selenium import webdriver from Selenium2Library.keywords.browsermanagement import BrowserManagementKeywords @@ -17,10 +17,6 @@ def test_create_firefox_browser(self): for test_browser in test_browsers: self.verify_browser(*test_browser) - def mock_createProfile(self, profile_directory=None): - self.ff_profile_dir = profile_directory - return self.old_profile_init(profile_directory) - def test_create_ie_browser(self): test_browsers = ( (webdriver.Ie, "ie"), @@ -65,6 +61,7 @@ def test_parse_capabilities_string(self): self.assertTrue("val1", capabilities["key1"]) self.assertTrue("val2", capabilities["key2"]) self.assertTrue(2, len(capabilities)) + unstub() def test_parse_complex_capabilities_string(self): ctx = mock() @@ -74,6 +71,7 @@ def test_parse_complex_capabilities_string(self): self.assertTrue("manual", capabilities["proxyType"]) self.assertTrue("IP:port", capabilities["httpProxy"]) self.assertTrue(2, len(capabilities)) + unstub() def test_create_remote_browser_with_desired_prefs(self): expected_caps = {"key1": "val1", "key2": "val2"} @@ -98,19 +96,23 @@ def test_capabilities_attribute_not_modified(self): def test_set_selenium_timeout_only_affects_open_browsers(self): ctx = mock() - bm = BrowserManagementKeywords(ctx) + cache = mock() + ctx.cache = cache first_browser, second_browser = mock(), mock() - bm.ctx.cache.register(first_browser) - bm.ctx.cache.close() - verify(first_browser).quit() - bm.ctx.cache.register(second_browser) + when(cache).get_open_browsers().thenReturn( + [first_browser, second_browser] + ) + bm = BrowserManagementKeywords(ctx) bm.set_selenium_timeout("10 seconds") + verify(first_browser).set_script_timeout(10.0) verify(second_browser).set_script_timeout(10.0) - bm.ctx.cache.close_all() - verify(second_browser).quit() + when(cache).get_open_browsers().thenReturn( + [] + ) bm.set_selenium_timeout("20 seconds") verifyNoMoreInteractions(first_browser) verifyNoMoreInteractions(second_browser) + unstub() def test_bad_browser_name(self): ctx = mock() @@ -128,17 +130,18 @@ def test_create_webdriver(self): driver = mock() when(FakeWebDriver).__call__(some_arg=1).thenReturn(driver) when(FakeWebDriver).__call__(some_arg=2).thenReturn(driver) + when(ctx).register_browser(driver, 'fake1').thenReturn(0) webdriver.FakeWebDriver = FakeWebDriver try: index = bm.create_webdriver('FakeWebDriver', 'fake1', some_arg=1) - self.assertEqual(bm.ctx.current_browser(), driver) - self.assertEquals(bm.ctx.cache.get_connection(index), driver) - self.assertEquals(bm.ctx.cache.get_connection('fake1'), driver) + verify(ctx).register_browser(driver, 'fake1') + self.assertEqual(index, 0) my_kwargs = {'some_arg': 2} bm.create_webdriver('FakeWebDriver', 'fake2', kwargs=my_kwargs) - self.assertEquals(bm.ctx.current_browser(), driver) + verify(ctx).register_browser(driver, 'fake2') finally: del webdriver.FakeWebDriver + unstub() def verify_browser(self, webdriver_type, browser_name, **kw): # todo try lambda *x: was_called = true @@ -155,6 +158,7 @@ def verify_browser(self, webdriver_type, browser_name, **kw): finally: webdriver_type.__init__ = old_init self.assertTrue(self.was_called) + unstub() def mock_init(self, *args, **kw): self.was_called = True diff --git a/test/unit/utils/test_browsercache.py b/test/unit/utils/test_browsercache.py index 4365c5236..bb1c6d6c2 100644 --- a/test/unit/utils/test_browsercache.py +++ b/test/unit/utils/test_browsercache.py @@ -21,14 +21,17 @@ def test_browsers_property(self): browser2 = mock() browser3 = mock() - cache.register(browser1) - cache.register(browser2) - cache.register(browser3) + index1 = cache.register(browser1) + index2 = cache.register(browser2) + index3 = cache.register(browser3) self.assertEqual(len(cache.browsers), 3) self.assertEqual(cache.browsers[0], browser1) self.assertEqual(cache.browsers[1], browser2) self.assertEqual(cache.browsers[2], browser3) + self.assertEqual(index1, 1) + self.assertEqual(index2, 2) + self.assertEqual(index3, 3) def test_get_open_browsers(self): cache = BrowserCache() @@ -58,7 +61,7 @@ def test_close(self): browser = mock() cache.register(browser) - verify(browser, times=0).quit() # sanity check + verify(browser, times=0).quit() # sanity check cache.close() verify(browser, times=1).quit() From c0f43a0c8fd21e308a0db776522fa421c2a78903 Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Thu, 9 Mar 2017 22:44:53 +0200 Subject: [PATCH 04/16] Fixed bug caused by the change. --- src/Selenium2Library/__init__.py | 11 ++-- src/Selenium2Library/base.py | 9 ---- src/Selenium2Library/keywords/alert.py | 24 ++++----- .../keywords/browsermanagement.py | 52 +++++++++---------- src/Selenium2Library/keywords/cookie.py | 13 ++--- src/Selenium2Library/keywords/element.py | 40 +++++++------- src/Selenium2Library/keywords/formelement.py | 4 +- src/Selenium2Library/keywords/javascript.py | 4 +- src/Selenium2Library/keywords/screenshot.py | 6 +-- .../keywords/selectelement.py | 4 +- src/Selenium2Library/keywords/tableelement.py | 14 ++--- src/Selenium2Library/keywords/waiting.py | 2 +- 12 files changed, 93 insertions(+), 90 deletions(-) diff --git a/src/Selenium2Library/__init__.py b/src/Selenium2Library/__init__.py index ae67b861f..4348d3160 100644 --- a/src/Selenium2Library/__init__.py +++ b/src/Selenium2Library/__init__.py @@ -210,15 +210,20 @@ def __init__(self, def run_keyword(self, name, args, kwargs): try: return DynamicCore.run_keyword(self, name, args, kwargs) - except Exception as e: + except Exception: print 'run failure here' - raise e + raise - def current_browser(self): + @property + def browser(self): """returns the current active browser""" if not self.cache.current: raise RuntimeError('No browser is open') return self.cache.current + def _current_browser(self): + logger.warn('Deprecated, please use self.browser') + return self.browser + def register_browser(self, browser, alias): return self.cache.register(browser, alias) diff --git a/src/Selenium2Library/base.py b/src/Selenium2Library/base.py index cd2b463e4..b9fd81866 100644 --- a/src/Selenium2Library/base.py +++ b/src/Selenium2Library/base.py @@ -14,12 +14,3 @@ def log(self, msg, level='INFO', html=False): def warn(self, msg, html=False): logger.warn(msg, html) - - def _log(self, message, level='INFO', html=False): - level = level.upper() - if (level == 'INFO'): - self.info(message, html) - elif (level == 'DEBUG'): - self.debug(message, html) - elif (level == 'WARN'): - self.warn(message, html) diff --git a/src/Selenium2Library/keywords/alert.py b/src/Selenium2Library/keywords/alert.py index 79c727927..97c79f212 100644 --- a/src/Selenium2Library/keywords/alert.py +++ b/src/Selenium2Library/keywords/alert.py @@ -10,12 +10,12 @@ class AlertKeywords(Base): - __ACCEPT_ALERT = 'accept' - __DISMISS_ALERT = 'dismiss' + ACCEPT_ALERT = 'accept' + DISMISS_ALERT = 'dismiss' def __init__(self, ctx): self.ctx = ctx - self._next_alert_dismiss_type = self.__ACCEPT_ALERT + self._next_alert_dismiss_type = self.ACCEPT_ALERT @keyword def input_text_into_prompt(self, text): @@ -37,7 +37,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'" @@ -46,7 +46,7 @@ def alert_should_be_present(self, 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 + self._next_alert_dismiss_type = self.DISMISS_ALERT @keyword def choose_ok_on_next_confirmation(self): @@ -64,7 +64,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 @keyword def confirm_action(self): @@ -85,7 +85,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 @keyword @@ -98,7 +98,7 @@ def get_alert_message(self, dismiss=True): dismissed by this keyword or another like `Dismiss Alert`. """ if dismiss: - return self._handle_alert(self.__DISMISS_ALERT) + return self._handle_alert(self.DISMISS_ALERT) else: return self._handle_alert() @@ -111,7 +111,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() @@ -138,12 +138,12 @@ 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 def _wait_alert(self): - return WebDriverWait(self._current_browser(), 1).until( + return WebDriverWait(self.ctx.browser, 1).until( EC.alert_is_present()) diff --git a/src/Selenium2Library/keywords/browsermanagement.py b/src/Selenium2Library/keywords/browsermanagement.py index 1b9a561ac..47d336463 100644 --- a/src/Selenium2Library/keywords/browsermanagement.py +++ b/src/Selenium2Library/keywords/browsermanagement.py @@ -60,13 +60,13 @@ def close_all_browsers(self): @keyword def close_browser(self): """Closes the current browser.""" - if self.current_browser(): + if self.ctx.browser: self.debug( 'Closing browser with session id {}'.format( - self.current_browser().session_id + self.ctx.browser.session_id ) ) - self.current_browser().close() + self.ctx.close() @keyword def open_browser( @@ -222,7 +222,7 @@ def switch_browser(self, index_or_alias): try: self.ctx.cache.switch(index_or_alias) self.debug('Switched to browser with Selenium session id %s' - % self.ctx.current_browser().current.session_id) + % self.ctx.browser.current.session_id) except (RuntimeError, DataError): # RF 2.6 uses RE, earlier DE raise RuntimeError("No browser with index or alias '%s' found." % index_or_alias) @@ -230,20 +230,20 @@ def switch_browser(self, index_or_alias): @keyword def close_window(self): """Closes currently opened pop-up window.""" - self.ctx.current_browser().close() + self.ctx.browser.close() @keyword def get_window_identifiers(self): """Returns and logs id attributes of all windows known to the browser.""" return self._log_list( - self._window_manager.get_window_ids(self.ctx.current_browser()) + self._window_manager.get_window_ids(self.ctx.browser) ) @keyword def get_window_names(self): """Returns and logs names of all windows known to the browser.""" values = self._window_manager.get_window_names( - self.ctx.current_browser() + self.ctx.browser ) # for backward compatibility, since Selenium 1 would always @@ -257,13 +257,13 @@ def get_window_names(self): def get_window_titles(self): """Returns and logs titles of all windows known to the browser.""" return self._log_list( - self._window_manager.get_window_titles(self.ctx.current_browser()) + self._window_manager.get_window_titles(self.ctx.browser) ) @keyword def maximize_browser_window(self): """Maximizes current browser window.""" - self.ctx.current_browser().maximize_window() + self.ctx.browser.maximize_window() @keyword def get_window_size(self): @@ -272,7 +272,7 @@ def get_window_size(self): Example: | ${width} | ${height}= | Get Window Size | """ - size = self.ctx.current_browser().get_window_size() + size = self.ctx.browser.get_window_size() return size['width'], size['height'] @keyword @@ -285,7 +285,7 @@ def set_window_size(self, width, height): | Should Be Equal | ${width} | ${800} | | Should Be Equal | ${height} | ${600} | """ - return self.ctx.current_browser().set_window_size(width, height) + return self.ctx.browser.set_window_size(width, height) @keyword def get_window_position(self): @@ -294,7 +294,7 @@ def get_window_position(self): Example: | ${x} | ${y}= | Get Window Position | """ - position = self.ctx.current_browser().get_window_position() + position = self.ctx.browser.get_window_position() return position['x'], position['y'] @keyword @@ -307,7 +307,7 @@ def set_window_position(self, x, y): | Should Be Equal | ${x} | ${8} | | Should Be Equal | ${y} | ${10} | """ - return self.ctx.current_browser().set_window_position(x, y) + return self.ctx.browser.set_window_position(x, y) @keyword def select_frame(self, locator): @@ -318,7 +318,7 @@ def select_frame(self, locator): """ self.info("Selecting frame '%s'." % locator) element = self._element_find(locator, True, True) - self.ctx.current_browser().switch_to_frame(element) + self.ctx.browser.switch_to_frame(element) @keyword def select_window(self, locator=None): @@ -356,44 +356,44 @@ def select_window(self, locator=None): | Select Window | | | # Chooses the main window again | """ try: - return self.ctx.current_browser().current_window_handle + return self.ctx.browser.current_window_handle except NoSuchWindowException: pass finally: - self._window_manager.select(self.ctx.current_browser(), locator) + self._window_manager.select(self.ctx.browser, locator) @keyword def list_windows(self): """Return all current window handles as a list""" - return self.ctx.current_browser().window_handles + return self.ctx.browser.window_handles @keyword def unselect_frame(self): """Sets the top frame as the current frame.""" - self.ctx.current_browser().switch_to_default_content() + self.ctx.browser.switch_to_default_content() @keyword def get_location(self): """Returns the current location.""" - return self.ctx.current_browser().current_url + return self.ctx.browser.current_url @keyword def get_locations(self): """Returns and logs current locations of all windows known to the browser.""" return self._log_list( [window_info[4] for window_info in - self._window_manager._get_window_infos(self.ctx.current_browser())] + self._window_manager._get_window_infos(self.ctx.browser)] ) @keyword def get_source(self): """Returns the entire html source of the current page or frame.""" - return self.ctx.current_browser().page_source + return self.ctx.browser.page_source @keyword def get_title(self): """Returns title of current page.""" - return self.ctx.current_browser().title + return self.ctx.browser.title @keyword def location_should_be(self, url): @@ -450,18 +450,18 @@ def title_should_be(self, title): @keyword def go_back(self): """Simulates the user clicking the "back" button on their browser.""" - self.ctx.current_browser().back() + self.ctx.browser.back() @keyword def go_to(self, url): """Navigates the active browser instance to the provided URL.""" self.info("Opening url '%s'" % url) - self.ctx.current_browser().get(url) + self.ctx.browser.get(url) @keyword def reload_page(self): """Simulates user reloading page.""" - self.ctx.current_browser().refresh() + self.ctx.browser.refresh() @keyword def get_selenium_speed(self): @@ -567,7 +567,7 @@ def set_browser_implicit_wait(self, seconds): See also `Set Selenium Implicit Wait`. """ implicit_wait_in_secs = timestr_to_secs(seconds) - self.ctx.current_browser().implicitly_wait(implicit_wait_in_secs) + self.ctx.browser.implicitly_wait(implicit_wait_in_secs) def _get_browser_creation_function(self, browser_name): func_name = BROWSER_NAMES.get(browser_name.lower().replace(' ', '')) diff --git a/src/Selenium2Library/keywords/cookie.py b/src/Selenium2Library/keywords/cookie.py index a122c6f63..27c71815a 100644 --- a/src/Selenium2Library/keywords/cookie.py +++ b/src/Selenium2Library/keywords/cookie.py @@ -1,7 +1,8 @@ +from Selenium2Library.base import Base from Selenium2Library.robotlibcore import keyword -class CookieKeywords(object): +class CookieKeywords(Base): def __init__(self, ctx): self.ctx = ctx @@ -9,7 +10,7 @@ def __init__(self, ctx): @keyword def delete_all_cookies(self): """Deletes all cookies.""" - self.ctx.current_browser().delete_all_cookies() + self.ctx.browser.delete_all_cookies() @keyword def delete_cookie(self, name): @@ -17,13 +18,13 @@ def delete_cookie(self, name): If the cookie is not found, nothing happens. """ - self.ctx.current_browser().delete_cookie(name) + self.ctx.browser.delete_cookie(name) @keyword def get_cookies(self): """Returns all cookies of the current page.""" pairs = [] - for cookie in self.ctx.current_browser().get_cookies(): + for cookie in self.ctx.browser.get_cookies(): pairs.append(cookie['name'] + "=" + cookie['value']) return '; '.join(pairs) @@ -33,7 +34,7 @@ def get_cookie_value(self, name): If no cookie is found with `name`, this keyword fails. """ - cookie = self.ctx.current_browser().get_cookie(name) + cookie = self.ctx.browser.get_cookie(name) if cookie is not None: return cookie['value'] raise ValueError("Cookie with name %s not found." % name) @@ -55,4 +56,4 @@ def add_cookie(self, name, value, path=None, domain=None, secure=None, # secure should be True or False so check explicitly for None if secure is not None: new_cookie['secure'] = secure - self.ctx.current_browser().add_cookie(new_cookie) + self.ctx.browser.add_cookie(new_cookie) diff --git a/src/Selenium2Library/keywords/element.py b/src/Selenium2Library/keywords/element.py index af1af2ea6..3c07eb540 100644 --- a/src/Selenium2Library/keywords/element.py +++ b/src/Selenium2Library/keywords/element.py @@ -199,7 +199,9 @@ def assign_id_to_element(self, locator, id): """ self.info("Assigning temporary id '%s' to element '%s'" % (id, locator)) element = self.element_find(locator, True, True) - self.ctx.current_browser().execute_script("arguments[0].id = '%s';" % id, element) + self.ctx.browser.execute_script( + "arguments[0].id = '%s';" % id, element + ) @keyword def element_should_be_disabled(self, locator): @@ -400,7 +402,7 @@ def click_element_at_coordinates(self, locator, xoffset, yoffset): """ self.info("Click clicking element '%s' in coordinates '%s', '%s'." % (locator, xoffset, yoffset)) element = self.element_find(locator, True, True) - action = ActionChains(self.ctx.current_browser()) + action = ActionChains(self.ctx.browser) action.move_to_element(element) action.move_by_offset(xoffset, yoffset) action.click() @@ -415,14 +417,14 @@ def double_click_element(self, locator): """ self.info("Double clicking element '%s'." % locator) element = self.element_find(locator, True, True) - action = ActionChains(self.ctx.current_browser()) + action = ActionChains(self.ctx.browser) action.double_click(element).perform() @keyword def focus(self, locator): """Sets focus to element identified by `locator`.""" element = self.element_find(locator, True, True) - self.ctx.current_browser().execute_script( + self.ctx.browser.execute_script( "arguments[0].focus();", element ) @@ -441,7 +443,7 @@ def drag_and_drop(self, source, target): """ src_elem = self.element_find(source, True, True) trg_elem = self.element_find(target, True, True) - action = ActionChains(self.ctx.current_browser()) + action = ActionChains(self.ctx.browser) action.drag_and_drop(src_elem, trg_elem).perform() @keyword @@ -455,7 +457,7 @@ def drag_and_drop_by_offset(self, source, xoffset, yoffset): | Drag And Drop By Offset | myElem | 50 | -35 | # Move myElem 50px right and 35px down. | """ src_elem = self.element_find(source, True, True) - action = ActionChains(self.ctx.current_browser()) + action = ActionChains(self.ctx.browser) action.drag_and_drop_by_offset(src_elem, xoffset, yoffset) action.perform() @@ -475,7 +477,7 @@ def mouse_down(self, locator): element = self.element_find(locator, True, False) if element is None: raise AssertionError("ERROR: Element %s not found." % (locator)) - action = ActionChains(self.ctx.current_browser()) + action = ActionChains(self.ctx.browser) action.click_and_hold(element).perform() @keyword @@ -492,7 +494,7 @@ def mouse_out(self, locator): size = element.size offsetx = (size['width'] / 2) + 1 offsety = (size['height'] / 2) + 1 - action = ActionChains(self.ctx.current_browser()) + action = ActionChains(self.ctx.browser) action.move_to_element(element).move_by_offset(offsetx, offsety) action.perform() @@ -507,7 +509,7 @@ def mouse_over(self, locator): element = self.element_find(locator, True, False) if element is None: raise AssertionError("ERROR: Element %s not found." % (locator)) - action = ActionChains(self.ctx.current_browser()) + action = ActionChains(self.ctx.browser) action.move_to_element(element).perform() @keyword @@ -521,13 +523,13 @@ def mouse_up(self, locator): element = self.element_find(locator, True, False) if element is None: raise AssertionError("ERROR: Element %s not found." % (locator)) - ActionChains(self.ctx.current_browser()).release(element).perform() + ActionChains(self.ctx.browser).release(element).perform() @keyword def open_context_menu(self, locator): """Opens context menu on element identified by `locator`.""" element = self.element_find(locator, True, True) - action = ActionChains(self.ctx.current_browser()) + action = ActionChains(self.ctx.browser) action.context_click(element).perform() @keyword @@ -550,7 +552,7 @@ def simulate(self, locator, event): evt.initEvent(eventName, true, true); return !element.dispatchEvent(evt); """ - self.ctx.current_browser().execute_script(script, element, event) + self.ctx.browser.execute_script(script, element, event) @keyword def press_key(self, locator, key): @@ -598,7 +600,7 @@ def mouse_down_on_link(self, locator): `introduction` for details about locating elements. """ element = self.element_find(locator, True, True, 'link') - action = ActionChains(self.ctx.current_browser()) + action = ActionChains(self.ctx.browser) action.click_and_hold(element).perform() @keyword @@ -649,7 +651,7 @@ def mouse_down_on_image(self, locator): `introduction` for details about locating elements. """ element = self.element_find(locator, True, True, 'image') - action = ActionChains(self.ctx.current_browser()) + action = ActionChains(self.ctx.browser) action.click_and_hold(element).perform() @keyword @@ -755,7 +757,7 @@ def remove_location_strategy(self, strategy_name): self.element_finder.unregister(strategy_name) def element_find(self, locator, first_only, required, tag=None): - browser = self.ctx.current_browser() + browser = self.ctx.browser if isinstance(locator, basestring): elements = self.element_finder.find(browser, locator, tag) if required and len(elements) == 0: @@ -770,7 +772,7 @@ def element_find(self, locator, first_only, required, tag=None): return elements def _frame_contains(self, locator, text): - browser = self.ctx.current_browser() + browser = self.ctx.browser element = self.element_find(locator, True, True) browser.switch_to_frame(element) self.info("Searching for text from frame '%s'." % locator) @@ -839,7 +841,7 @@ def _map_named_key_code_to_special_key(self, key_name): return getattr(Keys, key_name) except AttributeError: message = "Unknown key named '%s'." % (key_name) - self._debug(message) + self.ctx.debug(message) raise ValueError(message) def _parse_attribute_locator(self, attribute_locator): @@ -854,14 +856,14 @@ def is_element_present(self, locator, tag=None): return self.element_find(locator, True, False, tag=tag) is not None def _page_contains(self, text): - browser = self.ctx.current_browser() + browser = self.ctx.browser browser.switch_to_default_content() if self.is_text_present(text): return True subframes = self.element_find("xpath=//frame|//iframe", False, False) - self._debug('Current frame has %d subframes' % len(subframes)) + self.ctx.debug('Current frame has %d subframes' % len(subframes)) for frame in subframes: browser.switch_to_frame(frame) found_text = self.is_text_present(text) diff --git a/src/Selenium2Library/keywords/formelement.py b/src/Selenium2Library/keywords/formelement.py index 15b70aa99..c64473a97 100644 --- a/src/Selenium2Library/keywords/formelement.py +++ b/src/Selenium2Library/keywords/formelement.py @@ -363,13 +363,13 @@ def _get_checkbox(self, locator): def _get_radio_buttons(self, group_name): xpath = "xpath=//input[@type='radio' and @name='%s']" % group_name - self._debug('Radio group locator: ' + xpath) + self.debug('Radio group locator: ' + xpath) return self.element_find(xpath, False, True) def _get_radio_button_with_value(self, group_name, value): xpath = "xpath=//input[@type='radio' and @name='%s' and (@value='%s' or @id='%s')]" \ % (group_name, value, value) - self._debug('Radio group locator: ' + xpath) + self.debug('Radio group locator: ' + xpath) return self.element_find(xpath, True, True) def _get_value_from_radio_buttons(self, elements): diff --git a/src/Selenium2Library/keywords/javascript.py b/src/Selenium2Library/keywords/javascript.py index 868fb1df1..4861d1edb 100644 --- a/src/Selenium2Library/keywords/javascript.py +++ b/src/Selenium2Library/keywords/javascript.py @@ -40,7 +40,7 @@ def execute_javascript(self, *code): """ js = self._get_javascript_to_execute(''.join(code)) self.info("Executing JavaScript:\n%s" % js) - return self.ctx.current_browser().execute_script(js) + return self.ctx.browser.execute_script(js) @keyword def execute_async_javascript(self, *code): @@ -65,7 +65,7 @@ def execute_async_javascript(self, *code): """ js = self._get_javascript_to_execute(''.join(code)) self.info("Executing Asynchronous JavaScript:\n%s" % js) - return self.ctx.current_browser().execute_async_script(js) + return self.ctx.browser.execute_async_script(js) def _get_javascript_to_execute(self, code): codepath = code.replace('/', os.sep) diff --git a/src/Selenium2Library/keywords/screenshot.py b/src/Selenium2Library/keywords/screenshot.py index a072b2de6..c7c22a238 100644 --- a/src/Selenium2Library/keywords/screenshot.py +++ b/src/Selenium2Library/keywords/screenshot.py @@ -91,11 +91,11 @@ def capture_page_screenshot(self, """ path, link = self._get_screenshot_paths(filename) self._create_directory(path) - if hasattr(self.ctx.current_browser(), 'get_screenshot_as_file'): - if not self.ctx.current_browser().get_screenshot_as_file(path): + if hasattr(self.ctx.browser, 'get_screenshot_as_file'): + if not self.ctx.browser.get_screenshot_as_file(path): raise RuntimeError('Failed to save screenshot ' + link) else: - if not self.ctx.current_browser().save_screenshot(path): + if not self.ctx.browser.save_screenshot(path): raise RuntimeError('Failed to save screenshot ' + link) # Image is shown on its own row and thus prev row is closed on purpose msg = ( diff --git a/src/Selenium2Library/keywords/selectelement.py b/src/Selenium2Library/keywords/selectelement.py index 20323e8bc..56b0575d0 100644 --- a/src/Selenium2Library/keywords/selectelement.py +++ b/src/Selenium2Library/keywords/selectelement.py @@ -410,7 +410,9 @@ def _is_multiselect_list(self, select): return False def _unselect_all_options_from_multi_select_list(self, select): - self.ctx.current_browser().execute_script("arguments[0].selectedIndex = -1;", select) + self.ctx.browser.execute_script( + "arguments[0].selectedIndex = -1;", select + ) def _unselect_option_from_multi_select_list(self, select, options, index): if options[index].is_selected(): diff --git a/src/Selenium2Library/keywords/tableelement.py b/src/Selenium2Library/keywords/tableelement.py index 087aa9f29..c4b78cfe1 100644 --- a/src/Selenium2Library/keywords/tableelement.py +++ b/src/Selenium2Library/keywords/tableelement.py @@ -30,7 +30,9 @@ def get_table_cell(self, table_locator, row, column, loglevel='INFO'): column_index = column if column > 0: column_index = column - 1 - table = self._table_element_finder.find(self.ctx.current_browser(), table_locator) + table = self._table_element_finder.find( + self.ctx.browser, table_locator + ) if table is not None: rows = table.find_elements_by_xpath("./thead/tr") if row_index >= len(rows) or row_index < 0: @@ -98,7 +100,7 @@ def table_column_should_contain(self, table_locator, col, expected, loglevel='IN See `Page Should Contain Element` for explanation about `loglevel` argument. """ - element = self._table_element_finder.find_by_col(self.ctx.current_browser(), table_locator, col, expected) + element = self._table_element_finder.find_by_col(self.ctx.browser, table_locator, col, expected) if element is None: self.ctx.log_source(loglevel) raise AssertionError("Column #%s in table identified by '%s' " @@ -116,7 +118,7 @@ def table_footer_should_contain(self, table_locator, expected, loglevel='INFO'): See `Page Should Contain Element` for explanation about `loglevel` argument. """ - element = self._table_element_finder.find_by_footer(self.ctx.current_browser(), table_locator, expected) + element = self._table_element_finder.find_by_footer(self.ctx.browser, table_locator, expected) if element is None: self.ctx.log_source(loglevel) raise AssertionError("Footer in table identified by '%s' should have contained " @@ -132,7 +134,7 @@ def table_header_should_contain(self, table_locator, expected, loglevel='INFO'): See `Page Should Contain Element` for explanation about `loglevel` argument. """ - element = self._table_element_finder.find_by_header(self.ctx.current_browser(), table_locator, expected) + element = self._table_element_finder.find_by_header(self.ctx.browser, table_locator, expected) if element is None: self.ctx.log_source(loglevel) raise AssertionError("Header in table identified by '%s' should have contained " @@ -156,7 +158,7 @@ def table_row_should_contain(self, table_locator, row, expected, loglevel='INFO' See `Page Should Contain Element` for explanation about `loglevel` argument. """ - element = self._table_element_finder.find_by_row(self.ctx.current_browser(), table_locator, row, expected) + element = self._table_element_finder.find_by_row(self.ctx.browser, table_locator, row, expected) if element is None: self.ctx.log_source(loglevel) raise AssertionError("Row #%s in table identified by '%s' should have contained " @@ -172,7 +174,7 @@ def table_should_contain(self, table_locator, expected, loglevel='INFO'): See `Page Should Contain Element` for explanation about `loglevel` argument. """ - element = self._table_element_finder.find_by_content(self.ctx.current_browser(), table_locator, expected) + element = self._table_element_finder.find_by_content(self.ctx.browser, table_locator, expected) if element is None: self.ctx.log_source(loglevel) raise AssertionError("Table identified by '%s' should have contained text '%s'." \ diff --git a/src/Selenium2Library/keywords/waiting.py b/src/Selenium2Library/keywords/waiting.py index 491639b58..ecb37c0e8 100644 --- a/src/Selenium2Library/keywords/waiting.py +++ b/src/Selenium2Library/keywords/waiting.py @@ -36,7 +36,7 @@ def wait_for_condition(self, condition, timeout=None, error=None): error = "Condition '%s' did not become true in " % condition self._wait_until( timeout, error, - lambda: self.ctx.current_browser().execute_script(condition) is True) + lambda: self.ctx.browser.execute_script(condition) is True) @keyword def wait_until_page_contains(self, text, timeout=None, error=None): From f1edd9f7c60c5948a42a617297998abc3fa82768 Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Thu, 9 Mar 2017 23:41:36 +0200 Subject: [PATCH 05/16] Fixed logging source --- src/Selenium2Library/base.py | 5 +++- .../keywords/browsermanagement.py | 2 +- src/Selenium2Library/keywords/element.py | 26 ++++++++++--------- src/Selenium2Library/keywords/tableelement.py | 18 +++++++------ 4 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/Selenium2Library/base.py b/src/Selenium2Library/base.py index b9fd81866..74edc4a99 100644 --- a/src/Selenium2Library/base.py +++ b/src/Selenium2Library/base.py @@ -1,5 +1,7 @@ from robot.api import logger +ROBOT_LOG_LEVELS = ['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'] + class Base(object): @@ -10,7 +12,8 @@ def debug(self, msg, html=False): logger.debug(msg, html) def log(self, msg, level='INFO', html=False): - logger.write(msg, level, html) + if level.upper() in ROBOT_LOG_LEVELS: + logger.write(msg, level, html) def warn(self, msg, html=False): logger.warn(msg, html) diff --git a/src/Selenium2Library/keywords/browsermanagement.py b/src/Selenium2Library/keywords/browsermanagement.py index 47d336463..91969bd1d 100644 --- a/src/Selenium2Library/keywords/browsermanagement.py +++ b/src/Selenium2Library/keywords/browsermanagement.py @@ -66,7 +66,7 @@ def close_browser(self): self.ctx.browser.session_id ) ) - self.ctx.close() + self.ctx.cache.close() @keyword def open_browser( diff --git a/src/Selenium2Library/keywords/element.py b/src/Selenium2Library/keywords/element.py index 3c07eb540..59dd5d094 100644 --- a/src/Selenium2Library/keywords/element.py +++ b/src/Selenium2Library/keywords/element.py @@ -3,9 +3,10 @@ from selenium.webdriver.remote.webelement import WebElement from Selenium2Library import utils +from Selenium2Library.base import Base from Selenium2Library.locators import ElementFinder from Selenium2Library.locators import CustomLocator -from Selenium2Library.base import Base +from Selenium2Library.keywords.browsermanagement import BrowserManagementKeywords from Selenium2Library.robotlibcore import keyword @@ -15,6 +16,7 @@ def __init__(self, ctx): Base.__init__(self) self.ctx = ctx self.element_finder = ElementFinder() + self.log_source = BrowserManagementKeywords(self.ctx).log_source @keyword def get_webelement(self, locator): @@ -39,7 +41,7 @@ def current_frame_contains(self, text, loglevel='INFO'): See `Page Should Contain ` for explanation about `loglevel` argument. """ if not self.is_text_present(text): - self.ctx.log_source(loglevel) + self.log_source(loglevel) raise AssertionError("Page should have contained text '%s' " "but did not" % text) self.info("Current page contains text '%s'." % text) @@ -51,7 +53,7 @@ def current_frame_should_not_contain(self, text, loglevel='INFO'): See `Page Should Contain ` for explanation about `loglevel` argument. """ if self.is_text_present(text): - self.ctx.log_source(loglevel) + self.log_source(loglevel) raise AssertionError("Page should not have contained text '%s' " "but it did" % text) self.info("Current page should not contain text '%s'." % text) @@ -107,7 +109,7 @@ def frame_should_contain(self, locator, text, loglevel='INFO'): details about locating elements. """ if not self._frame_contains(locator, text): - self.ctx.log_source(loglevel) + self.log_source(loglevel) raise AssertionError("Page should have contained text '%s' " "but did not" % text) self.info("Current page contains text '%s'." % text) @@ -123,7 +125,7 @@ def page_should_contain(self, text, loglevel='INFO'): will not be logged. """ if not self._page_contains(text): - self.ctx.log_source(loglevel) + self.log_source(loglevel) raise AssertionError("Page should have contained text '%s' " "but did not" % text) self.info("Current page contains text '%s'." % text) @@ -155,7 +157,7 @@ def locator_should_match_x_times(self, locator, expected_locator_count, message= if not message: message = "Locator %s should have matched %s times but matched %s times"\ %(locator, expected_locator_count, actual_locator_count) - self.ctx.log_source(loglevel) + self.log_source(loglevel) raise AssertionError(message) self.info("Current page contains %s elements matching '%s'." % (actual_locator_count, locator)) @@ -167,7 +169,7 @@ def page_should_not_contain(self, text, loglevel='INFO'): See `Page Should Contain ` for explanation about `loglevel` argument. """ if self._page_contains(text): - self.ctx.log_source(loglevel) + self.log_source(loglevel) raise AssertionError("Page should not have contained text '%s'" % text) self.info("Current page does not contain text '%s'." % text) @@ -718,7 +720,7 @@ def xpath_should_match_x_times(self, xpath, expected_xpath_count, message='', lo if not message: message = "Xpath %s should have matched %s times but matched %s times"\ %(xpath, expected_xpath_count, actual_xpath_count) - self.ctx.log_source(loglevel) + self.log_source(loglevel) raise AssertionError(message) self.info("Current page contains %s elements matching '%s'." % (actual_xpath_count, xpath)) @@ -841,7 +843,7 @@ def _map_named_key_code_to_special_key(self, key_name): return getattr(Keys, key_name) except AttributeError: message = "Unknown key named '%s'." % (key_name) - self.ctx.debug(message) + self.debug(message) raise ValueError(message) def _parse_attribute_locator(self, attribute_locator): @@ -863,7 +865,7 @@ def _page_contains(self, text): return True subframes = self.element_find("xpath=//frame|//iframe", False, False) - self.ctx.debug('Current frame has %d subframes' % len(subframes)) + self.debug('Current frame has %d subframes' % len(subframes)) for frame in subframes: browser.switch_to_frame(frame) found_text = self.is_text_present(text) @@ -878,7 +880,7 @@ def _page_should_contain_element(self, locator, tag, message, loglevel): if not message: message = "Page should have contained %s '%s' but did not"\ % (element_name, locator) - self.ctx.log_source(loglevel) + self.log_source(loglevel) raise AssertionError(message) self.info("Current page contains %s '%s'." % (element_name, locator)) @@ -888,7 +890,7 @@ def _page_should_not_contain_element(self, locator, tag, message, loglevel): if not message: message = "Page should not have contained %s '%s'"\ % (element_name, locator) - self.ctx.log_source(loglevel) + self.log_source(loglevel) raise AssertionError(message) self.info("Current page does not contain %s '%s'." % (element_name, locator)) diff --git a/src/Selenium2Library/keywords/tableelement.py b/src/Selenium2Library/keywords/tableelement.py index c4b78cfe1..528eca129 100644 --- a/src/Selenium2Library/keywords/tableelement.py +++ b/src/Selenium2Library/keywords/tableelement.py @@ -1,5 +1,6 @@ from Selenium2Library.base import Base from Selenium2Library.locators import TableElementFinder +from Selenium2Library.keywords.browsermanagement import BrowserManagementKeywords from Selenium2Library.robotlibcore import keyword @@ -9,6 +10,7 @@ def __init__(self, ctx): Base.__init__(self) self.ctx = ctx self._table_element_finder = TableElementFinder() + self.log_source = BrowserManagementKeywords(self.ctx).log_source @keyword def get_table_cell(self, table_locator, row, column, loglevel='INFO'): @@ -45,7 +47,7 @@ def get_table_cell(self, table_locator, row, column, loglevel='INFO'): columns.extend(rows[row_index].find_elements_by_tag_name('td')) if column_index < len(columns): return columns[column_index].text - self.ctx.log_source(loglevel) + self.log_source(loglevel) raise AssertionError("Cell in table %s in row #%s and column #%s could not be found." % (table_locator, str(row), str(column))) @@ -72,11 +74,11 @@ def table_cell_should_contain(self, table_locator, row, column, expected, loglev content = self.get_table_cell(table_locator, row, column, loglevel='NONE') except AssertionError as err: self.info(err) - self.ctx.log_source(loglevel) + self.log_source(loglevel) raise AssertionError(message) self.info("Cell contains %s." % (content)) if expected not in content: - self.ctx.log_source(loglevel) + self.log_source(loglevel) raise AssertionError(message) @keyword @@ -102,7 +104,7 @@ def table_column_should_contain(self, table_locator, col, expected, loglevel='IN """ element = self._table_element_finder.find_by_col(self.ctx.browser, table_locator, col, expected) if element is None: - self.ctx.log_source(loglevel) + self.log_source(loglevel) raise AssertionError("Column #%s in table identified by '%s' " "should have contained text '%s'." % (col, table_locator, expected)) @@ -120,7 +122,7 @@ def table_footer_should_contain(self, table_locator, expected, loglevel='INFO'): """ element = self._table_element_finder.find_by_footer(self.ctx.browser, table_locator, expected) if element is None: - self.ctx.log_source(loglevel) + self.log_source(loglevel) raise AssertionError("Footer in table identified by '%s' should have contained " "text '%s'." % (table_locator, expected)) @@ -136,7 +138,7 @@ def table_header_should_contain(self, table_locator, expected, loglevel='INFO'): """ element = self._table_element_finder.find_by_header(self.ctx.browser, table_locator, expected) if element is None: - self.ctx.log_source(loglevel) + self.log_source(loglevel) raise AssertionError("Header in table identified by '%s' should have contained " "text '%s'." % (table_locator, expected)) @@ -160,7 +162,7 @@ def table_row_should_contain(self, table_locator, row, expected, loglevel='INFO' """ element = self._table_element_finder.find_by_row(self.ctx.browser, table_locator, row, expected) if element is None: - self.ctx.log_source(loglevel) + self.log_source(loglevel) raise AssertionError("Row #%s in table identified by '%s' should have contained " "text '%s'." % (row, table_locator, expected)) @@ -176,6 +178,6 @@ def table_should_contain(self, table_locator, expected, loglevel='INFO'): """ element = self._table_element_finder.find_by_content(self.ctx.browser, table_locator, expected) if element is None: - self.ctx.log_source(loglevel) + self.log_source(loglevel) raise AssertionError("Table identified by '%s' should have contained text '%s'." \ % (table_locator, expected)) From 44c5ab9ab35c3cc46fd0082d43d2d9613b54dc89 Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Sat, 11 Mar 2017 01:40:37 +0200 Subject: [PATCH 06/16] Fixes for page should* and close browser keywords --- src/Selenium2Library/__init__.py | 2 +- .../keywords/browsermanagement.py | 10 +- src/Selenium2Library/keywords/element.py | 113 ++++++++++-------- src/Selenium2Library/keywords/formelement.py | 77 ++++++++---- .../keywords/selectelement.py | 14 ++- test/acceptance/open_and_close.robot | 33 +++++ 6 files changed, 164 insertions(+), 85 deletions(-) diff --git a/src/Selenium2Library/__init__.py b/src/Selenium2Library/__init__.py index 4348d3160..7a8d6ac8a 100644 --- a/src/Selenium2Library/__init__.py +++ b/src/Selenium2Library/__init__.py @@ -1,6 +1,6 @@ from robot.api import logger -from Selenium2Library.utils import BrowserCache +from .utils import BrowserCache from .keywords.alert import AlertKeywords from .keywords.browsermanagement import BrowserManagementKeywords from .keywords.cookie import CookieKeywords diff --git a/src/Selenium2Library/keywords/browsermanagement.py b/src/Selenium2Library/keywords/browsermanagement.py index 91969bd1d..ab7c90157 100644 --- a/src/Selenium2Library/keywords/browsermanagement.py +++ b/src/Selenium2Library/keywords/browsermanagement.py @@ -60,10 +60,10 @@ def close_all_browsers(self): @keyword def close_browser(self): """Closes the current browser.""" - if self.ctx.browser: + if self.ctx.cache.current: self.debug( 'Closing browser with session id {}'.format( - self.ctx.browser.session_id + self.ctx.cache.current.session_id ) ) self.ctx.cache.close() @@ -221,8 +221,10 @@ def switch_browser(self, index_or_alias): """ try: self.ctx.cache.switch(index_or_alias) - self.debug('Switched to browser with Selenium session id %s' - % self.ctx.browser.current.session_id) + self.debug( + 'Switched to browser with Selenium session id %s' + % self.ctx.browser.session_id + ) except (RuntimeError, DataError): # RF 2.6 uses RE, earlier DE raise RuntimeError("No browser with index or alias '%s' found." % index_or_alias) diff --git a/src/Selenium2Library/keywords/element.py b/src/Selenium2Library/keywords/element.py index 59dd5d094..193fbed17 100644 --- a/src/Selenium2Library/keywords/element.py +++ b/src/Selenium2Library/keywords/element.py @@ -24,7 +24,7 @@ def get_webelement(self, locator): See `introduction` for details about locating elements. """ - return self.element_find(locator, True, True) + return self.element_find(locator) @keyword def get_webelements(self, locator): @@ -32,7 +32,7 @@ def get_webelements(self, locator): See `introduction` for details about locating elements. """ - return self.element_find(locator, False, True) + return self.element_find(locator, first_only=False) @keyword def current_frame_contains(self, text, loglevel='INFO'): @@ -152,7 +152,9 @@ def locator_should_match_x_times(self, locator, expected_locator_count, message= See `Page Should Contain Element` for explanation about `message` and `loglevel` arguments. """ - actual_locator_count = len(self.element_find(locator, False, False)) + actual_locator_count = len(self.element_find( + locator, first_only=False, required=False) + ) if int(actual_locator_count) != int(expected_locator_count): if not message: message = "Locator %s should have matched %s times but matched %s times"\ @@ -200,8 +202,8 @@ def assign_id_to_element(self, locator, id): | Page Should Contain Element | my id | """ self.info("Assigning temporary id '%s' to element '%s'" % (id, locator)) - element = self.element_find(locator, True, True) - self.ctx.browser.execute_script( + element = self.self.element_find(locator) + self.ctx( "arguments[0].id = '%s';" % id, element ) @@ -280,7 +282,7 @@ def element_text_should_be(self, locator, expected, message=''): """ self.info("Verifying element '%s' contains exactly text '%s'." % (locator, expected)) - element = self.element_find(locator, True, True) + element = self.element_find(locator) actual = element.text if expected != actual: if not message: @@ -312,7 +314,7 @@ def get_element_attribute(self, locator, attribute_name=None): """ if not attribute_name: locator, attribute_name = self._parse_attribute_locator(locator) - element = self.element_find(locator, True, False) + element = self.element_find(locator, required=False) if not element: raise ValueError("Element '%s' not found." % (locator)) return element.get_attribute(attribute_name) @@ -326,7 +328,7 @@ def get_horizontal_position(self, locator): See also `Get Vertical Position`. """ - element = self.element_find(locator, True, False) + element = self.element_find(locator, required=False) if element is None: raise AssertionError("Could not determine position for '%s'" % (locator)) return element.location['x'] @@ -338,8 +340,7 @@ def get_element_size(self, locator): The element width and height is returned. Fails if a matching element is not found. """ - element = self.element_find(locator, True, True) - + element = self.element_find(locator) return element.size['width'], element.size['height'] @keyword @@ -364,7 +365,7 @@ def clear_element_text(self, locator): See `introduction` for details about locating elements. """ - element = self.element_find(locator, True, True) + element = self.element_find(locator) element.clear() @keyword @@ -376,13 +377,11 @@ def get_vertical_position(self, locator): See also `Get Horizontal Position`. """ - element = self.element_find(locator, True, False) + element = self.element_find(locator, required=False) if element is None: raise AssertionError("Could not determine position for '%s'" % (locator)) return element.location['y'] - # Public, mouse input/events - @keyword def click_element(self, locator): """Click element identified by `locator`. @@ -391,7 +390,7 @@ def click_element(self, locator): `introduction` for details about locating elements. """ self.info("Clicking element '%s'." % locator) - self.element_find(locator, True, True).click() + self.element_find(locator).click() @keyword def click_element_at_coordinates(self, locator, xoffset, yoffset): @@ -403,7 +402,7 @@ def click_element_at_coordinates(self, locator, xoffset, yoffset): `introduction` for details about locating elements. """ self.info("Click clicking element '%s' in coordinates '%s', '%s'." % (locator, xoffset, yoffset)) - element = self.element_find(locator, True, True) + element = self.element_find(locator) action = ActionChains(self.ctx.browser) action.move_to_element(element) action.move_by_offset(xoffset, yoffset) @@ -418,14 +417,14 @@ def double_click_element(self, locator): `introduction` for details about locating elements. """ self.info("Double clicking element '%s'." % locator) - element = self.element_find(locator, True, True) + element = self.element_find(locator) action = ActionChains(self.ctx.browser) action.double_click(element).perform() @keyword def focus(self, locator): """Sets focus to element identified by `locator`.""" - element = self.element_find(locator, True, True) + element = self.element_find(locator) self.ctx.browser.execute_script( "arguments[0].focus();", element ) @@ -443,8 +442,8 @@ def drag_and_drop(self, source, target): Examples: | Drag And Drop | elem1 | elem2 | # Move elem1 over elem2. | """ - src_elem = self.element_find(source, True, True) - trg_elem = self.element_find(target, True, True) + src_elem = self.element_find(source) + trg_elem = self.element_find(target) action = ActionChains(self.ctx.browser) action.drag_and_drop(src_elem, trg_elem).perform() @@ -458,7 +457,7 @@ def drag_and_drop_by_offset(self, source, xoffset, yoffset): Examples: | Drag And Drop By Offset | myElem | 50 | -35 | # Move myElem 50px right and 35px down. | """ - src_elem = self.element_find(source, True, True) + src_elem = self.element_find(source) action = ActionChains(self.ctx.browser) action.drag_and_drop_by_offset(src_elem, xoffset, yoffset) action.perform() @@ -476,7 +475,7 @@ def mouse_down(self, locator): `Mouse Down On Link`. """ self.info("Simulating Mouse Down on element '%s'" % locator) - element = self.element_find(locator, True, False) + element = self.element_find(locator, required=False) if element is None: raise AssertionError("ERROR: Element %s not found." % (locator)) action = ActionChains(self.ctx.browser) @@ -490,7 +489,7 @@ def mouse_out(self, locator): `introduction` for details about locating elements. """ self.info("Simulating Mouse Out on element '%s'" % locator) - element = self.element_find(locator, True, False) + element = self.element_find(locator, required=False) if element is None: raise AssertionError("ERROR: Element %s not found." % (locator)) size = element.size @@ -508,7 +507,7 @@ def mouse_over(self, locator): `introduction` for details about locating elements. """ self.info("Simulating Mouse Over on element '%s'" % locator) - element = self.element_find(locator, True, False) + element = self.element_find(locator, required=False) if element is None: raise AssertionError("ERROR: Element %s not found." % (locator)) action = ActionChains(self.ctx.browser) @@ -522,7 +521,7 @@ def mouse_up(self, locator): `introduction` for details about locating elements. """ self.info("Simulating Mouse Up on element '%s'" % locator) - element = self.element_find(locator, True, False) + element = self.element_find(locator, required=False) if element is None: raise AssertionError("ERROR: Element %s not found." % (locator)) ActionChains(self.ctx.browser).release(element).perform() @@ -530,7 +529,7 @@ def mouse_up(self, locator): @keyword def open_context_menu(self, locator): """Opens context menu on element identified by `locator`.""" - element = self.element_find(locator, True, True) + element = self.element_find(locator) action = ActionChains(self.ctx.browser) action.context_click(element).perform() @@ -543,7 +542,7 @@ def simulate(self, locator, event): See `introduction` for details about locating elements. """ - element = self.element_find(locator, True, True) + element = self.element_find(locator) script = """ element = arguments[0]; eventName = arguments[1]; @@ -568,8 +567,7 @@ def press_key(self, locator, key): """ if key.startswith('\\') and len(key) > 1: key = self._map_ascii_key_code_to_key(int(key[1:])) - element = self.element_find(locator, True, True) - #select it + element = self.element_find(locator) element.send_keys(key) @keyword @@ -590,7 +588,10 @@ def get_all_links(self): If a link has no id, an empty string will be in the list instead. """ links = [] - for anchor in self.element_find("tag=a", False, False, 'a'): + elements = self.element_find( + "tag=a", first_only=False, required=False, tag='a' + ) + for anchor in elements: links.append(anchor.get_attribute('id')) return links @@ -601,7 +602,7 @@ def mouse_down_on_link(self, locator): Key attributes for links are `id`, `name`, `href` and link text. See `introduction` for details about locating elements. """ - element = self.element_find(locator, True, True, 'link') + element = self.element_find(locator, tag='link') action = ActionChains(self.ctx.browser) action.click_and_hold(element).perform() @@ -639,10 +640,12 @@ def click_image(self, locator): `introduction` for details about locating elements. """ self.info("Clicking image '%s'." % locator) - element = self.element_find(locator, True, False, 'image') + element = self.element_find( + locator, required=False, tag='image' + ) if element is None: # A form may have an image as it's submit trigger. - element = self.element_find(locator, True, True, 'input') + element = self.element_find(locator, tag='input') element.click() @keyword @@ -652,7 +655,7 @@ def mouse_down_on_image(self, locator): Key attributes for images are `id`, `src` and `alt`. See `introduction` for details about locating elements. """ - element = self.element_find(locator, True, True, 'image') + element = self.element_find(locator, tag='image') action = ActionChains(self.ctx.browser) action.click_and_hold(element).perform() @@ -698,7 +701,9 @@ def get_matching_xpath_count(self, xpath, return_str=True): If you wish to assert the number of matching elements, use `Xpath Should Match X Times`. """ - count = len(self.element_find("xpath=" + xpath, False, False)) + count = len(self.element_find( + "xpath=" + xpath, first_only=False, required=False) + ) return str(count) if return_str else count @keyword @@ -715,7 +720,9 @@ def xpath_should_match_x_times(self, xpath, expected_xpath_count, message='', lo See `Page Should Contain Element` for explanation about `message` and `loglevel` arguments. """ - actual_xpath_count = len(self.element_find("xpath=" + xpath, False, False)) + actual_xpath_count = len(self.element_find( + "xpath=" + xpath, first_only=False, required=False) + ) if int(actual_xpath_count) != int(expected_xpath_count): if not message: message = "Xpath %s should have matched %s times but matched %s times"\ @@ -758,24 +765,26 @@ def remove_location_strategy(self, strategy_name): """ self.element_finder.unregister(strategy_name) - def element_find(self, locator, first_only, required, tag=None): - browser = self.ctx.browser + def element_find(self, locator, first_only=True, required=True, tag=None): if isinstance(locator, basestring): - elements = self.element_finder.find(browser, locator, tag) + elements = self.element_finder.find(self.ctx.browser, locator, tag) if required and len(elements) == 0: - raise ValueError("Element locator '" + locator + "' did not match any elements.") + raise ValueError( + "Element locator '{}' did not match any elements.".format( + locator + ) + ) if first_only: - if len(elements) == 0: return None + if not elements: + return None return elements[0] elif isinstance(locator, WebElement): elements = locator - # do some other stuff here like deal with list of webelements - # ... or raise locator/element specific error if required return elements def _frame_contains(self, locator, text): browser = self.ctx.browser - element = self.element_find(locator, True, True) + element = self.element_find(locator) browser.switch_to_frame(element) self.info("Searching for text from frame '%s'." % locator) found = self.is_text_present(text) @@ -783,17 +792,19 @@ def _frame_contains(self, locator, text): return found def _get_text(self, locator): - element = self.element_find(locator, True, True) + element = self.element_find(locator) if element is not None: return element.text return None def _get_value(self, locator, tag=None): - element = self.element_find(locator, True, False, tag=tag) + element = self.element_find( + locator, required=False, tag=tag + ) return element.get_attribute('value') if element is not None else None def _is_enabled(self, locator): - element = self.element_find(locator, True, True) + element = self.element_find(locator) if not self._is_form_element(element): raise AssertionError("ERROR: Element %s is not an input." % (locator)) if not element.is_enabled(): @@ -808,7 +819,7 @@ def is_text_present(self, text): return self.is_element_present(locator) def is_visible(self, locator): - element = self.element_find(locator, True, False) + element = self.element_find(locator, required=False) if element is not None: return element.is_displayed() return None @@ -855,7 +866,7 @@ def _parse_attribute_locator(self, attribute_locator): return parts[0], parts[2] def is_element_present(self, locator, tag=None): - return self.element_find(locator, True, False, tag=tag) is not None + return True if self.element_find(locator, required=False, tag=tag) else None def _page_contains(self, text): browser = self.ctx.browser @@ -864,7 +875,9 @@ def _page_contains(self, text): if self.is_text_present(text): return True - subframes = self.element_find("xpath=//frame|//iframe", False, False) + subframes = self.element_find( + "xpath=//frame|//iframe", first_only=False, required=False + ) self.debug('Current frame has %d subframes' % len(subframes)) for frame in subframes: browser.switch_to_frame(frame) diff --git a/src/Selenium2Library/keywords/formelement.py b/src/Selenium2Library/keywords/formelement.py index c64473a97..56984c78e 100644 --- a/src/Selenium2Library/keywords/formelement.py +++ b/src/Selenium2Library/keywords/formelement.py @@ -10,7 +10,7 @@ class FormElementKeywords(Base): def __init__(self, ctx): Base.__init__(self) self.ctx = ctx - self.element_find = ElementKeywords(ctx).element_find + self.element_find = ElementKeywords(ctx) @keyword def submit_form(self, locator=None): @@ -23,7 +23,7 @@ def submit_form(self, locator=None): self.info("Submitting form '%s'." % locator) if not locator: locator = 'xpath=//form' - element = self.element_find(locator, True, True, 'form') + element = self.element_find.element_find(locator, tag='form') element.submit() @keyword @@ -62,7 +62,9 @@ def page_should_contain_checkbox(self, locator, message='', loglevel='INFO'): Key attributes for checkboxes are `id` and `name`. See `introduction` for details about locating elements. """ - self._page_should_contain_element(locator, 'checkbox', message, loglevel) + self.element_find._page_should_contain_element( + locator, 'checkbox', message, loglevel + ) @keyword def page_should_not_contain_checkbox(self, locator, message='', loglevel='INFO'): @@ -74,7 +76,9 @@ def page_should_not_contain_checkbox(self, locator, message='', loglevel='INFO') Key attributes for checkboxes are `id` and `name`. See `introduction` for details about locating elements. """ - self._page_should_not_contain_element(locator, 'checkbox', message, loglevel) + self.element_find._page_should_not_contain_element( + locator, 'checkbox', message, loglevel + ) @keyword def select_checkbox(self, locator): @@ -112,7 +116,9 @@ def page_should_contain_radio_button(self, locator, message='', loglevel='INFO') Key attributes for radio buttons are `id`, `name` and `value`. See `introduction` for details about locating elements. """ - self._page_should_contain_element(locator, 'radio button', message, loglevel) + self.element_find._page_should_contain_element( + locator, 'radio button', message, loglevel + ) @keyword def page_should_not_contain_radio_button(self, locator, message='', loglevel='INFO'): @@ -124,7 +130,9 @@ def page_should_not_contain_radio_button(self, locator, message='', loglevel='IN Key attributes for radio buttons are `id`, `name` and `value`. See `introduction` for details about locating elements. """ - self._page_should_not_contain_element(locator, 'radio button', message, loglevel) + self.element_find._page_should_not_contain_element( + locator, 'radio button', message, loglevel + ) @keyword def radio_button_should_be_set_to(self, group_name, value): @@ -191,7 +199,7 @@ def choose_file(self, locator, file_path): if not os.path.isfile(file_path): raise AssertionError("File '%s' does not exist on the local file system" % file_path) - self.element_find(locator, True, True).send_keys(file_path) + self.element_find.element_find(locator).send_keys(file_path) @keyword def input_password(self, locator, text): @@ -223,7 +231,9 @@ def page_should_contain_textfield(self, locator, message='', loglevel='INFO'): Key attributes for text fields are `id` and `name`. See `introduction` for details about locating elements. """ - self._page_should_contain_element(locator, 'text field', message, loglevel) + self.element_find._page_should_contain_element( + locator, 'text field', message, loglevel + ) @keyword def page_should_not_contain_textfield(self, locator, message='', loglevel='INFO'): @@ -235,7 +245,9 @@ def page_should_not_contain_textfield(self, locator, message='', loglevel='INFO' Key attributes for text fields are `id` and `name`. See `introduction` for details about locating elements. """ - self._page_should_not_contain_element(locator, 'text field', message, loglevel) + self.element_find._page_should_not_contain_element( + locator, 'text field', message, loglevel + ) @keyword def textfield_should_contain(self, locator, expected, message=''): @@ -246,7 +258,7 @@ def textfield_should_contain(self, locator, expected, message=''): Key attributes for text fields are `id` and `name`. See `introduction` for details about locating elements. """ - actual = self._get_value(locator, 'text field') + actual = self.element_find._get_value(locator, 'text field') if expected not in actual: if not message: message = "Text field '%s' should have contained text '%s' "\ @@ -263,8 +275,13 @@ def textfield_value_should_be(self, locator, expected, message=''): Key attributes for text fields are `id` and `name`. See `introduction` for details about locating elements. """ - element = self.element_find(locator, True, False, 'text field') - if element is None: element = self.element_find(locator, True, False, 'file upload') + element = self.element_find.element_find( + locator, required=False, tag='text field' + ) + if not element: + element = self.element_find.element_find( + locator, required=False, tag='file upload' + ) actual = element.get_attribute('value') if element is not None else None if actual != expected: if not message: @@ -282,9 +299,9 @@ def textarea_should_contain(self, locator, expected, message=''): Key attributes for text areas are `id` and `name`. See `introduction` for details about locating elements. """ - actual = self._get_value(locator, 'text area') + actual = self.element_find._get_value(locator, 'text area') if actual is not None: - if not expected in actual: + if expected not in actual: if not message: message = "Text field '%s' should have contained text '%s' "\ "but it contained '%s'" % (locator, expected, actual) @@ -302,7 +319,7 @@ def textarea_value_should_be(self, locator, expected, message=''): Key attributes for text areas are `id` and `name`. See `introduction` for details about locating elements. """ - actual = self._get_value(locator, 'text area') + actual = self.element_find._get_value(locator, 'text area') if actual is not None: if expected!=actual: if not message: @@ -321,9 +338,11 @@ def click_button(self, locator): `introduction` for details about locating elements. """ self.info("Clicking button '%s'." % locator) - element = self.element_find(locator, True, False, 'input') + element = self.element_find.element_find( + locator, required=False, tag='input' + ) if element is None: - element = self.element_find(locator, True, True, 'button') + element = self.element_find.element_find(locator, tag='button') element.click() @keyword @@ -339,9 +358,13 @@ def page_should_contain_button(self, locator, message='', loglevel='INFO'): `introduction` for details about locating elements. """ try: - self._page_should_contain_element(locator, 'input', message, loglevel) + self.element_find._page_should_contain_element( + locator, 'input', message, loglevel + ) except AssertionError: - self._page_should_contain_element(locator, 'button', message, loglevel) + self.element_find._page_should_contain_element( + locator, 'button', message, loglevel + ) @keyword def page_should_not_contain_button(self, locator, message='', loglevel='INFO'): @@ -355,22 +378,26 @@ def page_should_not_contain_button(self, locator, message='', loglevel='INFO'): Key attributes for buttons are `id`, `name` and `value`. See `introduction` for details about locating elements. """ - self._page_should_not_contain_element(locator, 'button', message, loglevel) - self._page_should_not_contain_element(locator, 'input', message, loglevel) + self.element_find._page_should_not_contain_element( + locator, 'button', message, loglevel + ) + self.element_find._page_should_not_contain_element( + locator, 'input', message, loglevel + ) def _get_checkbox(self, locator): - return self.element_find(locator, True, True, tag='input') + return self.element_find.element_find(locator, tag='input') def _get_radio_buttons(self, group_name): xpath = "xpath=//input[@type='radio' and @name='%s']" % group_name self.debug('Radio group locator: ' + xpath) - return self.element_find(xpath, False, True) + return self.element_find.element_find(xpath, first_only=False) def _get_radio_button_with_value(self, group_name, value): xpath = "xpath=//input[@type='radio' and @name='%s' and (@value='%s' or @id='%s')]" \ % (group_name, value, value) self.debug('Radio group locator: ' + xpath) - return self.element_find(xpath, True, True) + return self.element_find.element_find(xpath) def _get_value_from_radio_buttons(self, elements): for element in elements: @@ -379,7 +406,7 @@ def _get_value_from_radio_buttons(self, elements): return None def _input_text_into_text_field(self, locator, text): - element = self.element_find(locator, True, True) + element = self.element_find.element_find(locator) element.clear() element.send_keys(text) diff --git a/src/Selenium2Library/keywords/selectelement.py b/src/Selenium2Library/keywords/selectelement.py index 56b0575d0..fb555f544 100644 --- a/src/Selenium2Library/keywords/selectelement.py +++ b/src/Selenium2Library/keywords/selectelement.py @@ -11,7 +11,7 @@ class SelectElementKeywords(Base): def __init__(self, ctx): Base.__init__(self) self.ctx = ctx - self.element_find = ElementKeywords(ctx).element_find + self.element_find = ElementKeywords(ctx) @keyword def get_list_items(self, locator, value=False): @@ -141,7 +141,9 @@ def page_should_contain_list(self, locator, message='', loglevel='INFO'): Key attributes for lists are `id` and `name`. See `introduction` for details about locating elements. """ - self._page_should_contain_element(locator, 'list', message, loglevel) + self.element_find._page_should_contain_element( + locator, 'list', message, loglevel + ) @keyword def page_should_not_contain_list(self, locator, message='', loglevel='INFO'): @@ -153,7 +155,9 @@ def page_should_not_contain_list(self, locator, message='', loglevel='INFO'): Key attributes for lists are `id` and `name`. See `introduction` for details about locating elements. """ - self._page_should_not_contain_element(locator, 'list', message, loglevel) + self.element_find._page_should_not_contain_element( + locator, 'list', message, loglevel + ) @keyword def select_all_from_list(self, locator): @@ -382,7 +386,7 @@ def _get_labels_for_options(self, options): return labels def _get_select_list(self, locator): - el = self.element_find(locator, True, True, 'select') + el = self.element_find.element_find(locator, tag='select') return Select(el) def _get_select_list_options(self, select_list_or_locator): @@ -400,7 +404,7 @@ def _get_select_list_options_selected(self, locator): def _get_values_for_options(self, options): values = [] for option in options: - values.append(option.get_attribute('value')) + values.append(option.get_attribute('value')) return values def _is_multiselect_list(self, select): diff --git a/test/acceptance/open_and_close.robot b/test/acceptance/open_and_close.robot index b46af0b21..d7d09b19d 100644 --- a/test/acceptance/open_and_close.robot +++ b/test/acceptance/open_and_close.robot @@ -30,3 +30,36 @@ Browser Open With Not Well-Formed URL Should Close ... LOG 2:3 DEBUG Finished Request Run Keyword And Expect Error * Open Browser bad.url.bad ${BROWSER} Close All Browsers + +Switch to closed browser is possible + Open Browser ${ROOT}/forms/prefilled_email_form.html ${BROWSER} Browser 1 + ... remote_url=${REMOTE_URL} desired_capabilities=${DESIRED_CAPABILITIES} + Open Browser ${ROOT}/forms/prefilled_email_form.html ${BROWSER} Browser 2 + ... remote_url=${REMOTE_URL} desired_capabilities=${DESIRED_CAPABILITIES} + Open Browser ${ROOT}/forms/prefilled_email_form.html ${BROWSER} Browser 3 + ... remote_url=${REMOTE_URL} desired_capabilities=${DESIRED_CAPABILITIES} + Switch Browser Browser 1 + Switch Browser Browser 2 + Close Browser + Switch Browser Browser 3 + Page Should Contain Name: + Switch Browser Browser 2 + Run Keyword And Expect Error + ... error: [Errno 111] Connection refused + ... Page Should Contain Name: + Close All Browsers + +Closing all browsers clears cache + Open Browser ${ROOT}/forms/prefilled_email_form.html ${BROWSER} Browser 1 + ... remote_url=${REMOTE_URL} desired_capabilities=${DESIRED_CAPABILITIES} + Open Browser ${ROOT}/forms/prefilled_email_form.html ${BROWSER} Browser 2 + ... remote_url=${REMOTE_URL} desired_capabilities=${DESIRED_CAPABILITIES} + Switch Browser Browser 1 + Switch Browser Browser 2 + Close All Browsers + Run Keyword And Expect Error + ... No browser with index or alias 'Browser 1' found. + ... Switch Browser Browser 1 + Run Keyword And Expect Error + ... No browser with index or alias 'Browser 2' found. + ... Switch Browser Browser 2 From 791d6502efe96617c6ee88edba990245ed3c50b8 Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Mon, 13 Mar 2017 00:31:32 +0200 Subject: [PATCH 07/16] Refactoring common methods to the Base class ElementKeywords did contains methods which where used in many other keyword classes. Moved those methods to the Base class. --- src/Selenium2Library/__init__.py | 11 ++- src/Selenium2Library/base.py | 66 +++++++++++++- .../keywords/browsermanagement.py | 5 +- src/Selenium2Library/keywords/element.py | 90 ++++++------------- src/Selenium2Library/keywords/formelement.py | 48 +++++----- src/Selenium2Library/keywords/javascript.py | 8 +- src/Selenium2Library/keywords/screenshot.py | 13 +++ .../keywords/selectelement.py | 10 +-- src/Selenium2Library/keywords/tableelement.py | 18 ++-- src/Selenium2Library/keywords/waiting.py | 12 +-- test/acceptance/create_webdriver.robot | 2 +- .../testlibs/CustomSeleniumKeywords.py | 3 +- 12 files changed, 164 insertions(+), 122 deletions(-) diff --git a/src/Selenium2Library/__init__.py b/src/Selenium2Library/__init__.py index 7a8d6ac8a..e273a9aac 100644 --- a/src/Selenium2Library/__init__.py +++ b/src/Selenium2Library/__init__.py @@ -202,9 +202,9 @@ def __init__(self, self.cache = BrowserCache() 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.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() def run_keyword(self, name, args, kwargs): @@ -221,6 +221,11 @@ def browser(self): raise RuntimeError('No browser is open') return self.cache.current + @property + def _cache(self): + logger.warn('Deprecated, please use self.cache') + return self.cache + def _current_browser(self): logger.warn('Deprecated, please use self.browser') return self.browser diff --git a/src/Selenium2Library/base.py b/src/Selenium2Library/base.py index 74edc4a99..f13e4b66d 100644 --- a/src/Selenium2Library/base.py +++ b/src/Selenium2Library/base.py @@ -1,10 +1,18 @@ from robot.api import logger +from selenium.webdriver.remote.webelement import WebElement -ROBOT_LOG_LEVELS = ['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'] +from .locators.elementfinder import ElementFinder class Base(object): + def __init__(self): + self.element_finder = ElementFinder() + self._speed_in_secs = 0.0 + self._timeout_in_secs = 5.0 + self._implicit_wait_in_secs = 5.0 + self.robot_log_levels = ['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'] + def info(self, msg, html=False): logger.info(msg, html) @@ -12,8 +20,62 @@ def debug(self, msg, html=False): logger.debug(msg, html) def log(self, msg, level='INFO', html=False): - if level.upper() in ROBOT_LOG_LEVELS: + if level.upper() in self.robot_log_levels: logger.write(msg, level, html) def warn(self, msg, html=False): logger.warn(msg, html) + + def element_find(self, locator, first_only=True, required=True, tag=None): + if isinstance(locator, basestring): + elements = self.element_finder.find(self.ctx.browser, locator, tag) + if required and len(elements) == 0: + raise ValueError( + "Element locator '{}' did not match any elements.".format( + locator + ) + ) + if first_only: + if not elements: + return None + return elements[0] + elif isinstance(locator, WebElement): + elements = locator + return elements + + def get_value(self, locator, tag=None): + element = self.element_find( + locator, required=False, tag=tag + ) + return element.get_attribute('value') if element is not None else None + + def page_contains_element(self, locator, tag=None, + message=None, loglevel='INFO'): + element_name = tag if tag else 'element' + if not self.element_find(locator, required=False, tag=tag): + if not message: + message = ( + "Page should have contained %s " + "'%s' but did not" % (element_name, locator) + ) + self.ctx.log_source(loglevel) + raise AssertionError(message) + self.info( + "Current page contains %s '%s'." % (element_name, locator) + ) + + def page_not_contains_element(self, locator, tag=None, + message=None, loglevel='INFO'): + element_name = tag if tag else 'element' + if self.element_find(locator, required=False, tag=tag): + if not message: + message = ( + "Page should not have contained %s '%s'" + % (element_name, locator) + ) + self.ctx.log_source(loglevel) + raise AssertionError(message) + self.info( + "Current page does not contain %s '%s'." + % (element_name, locator) + ) diff --git a/src/Selenium2Library/keywords/browsermanagement.py b/src/Selenium2Library/keywords/browsermanagement.py index ab7c90157..3684bdf90 100644 --- a/src/Selenium2Library/keywords/browsermanagement.py +++ b/src/Selenium2Library/keywords/browsermanagement.py @@ -40,9 +40,6 @@ def __init__(self, ctx): Base.__init__(self) self.ctx = ctx self._window_manager = WindowManager() - self._speed_in_secs = 0.0 - self._timeout_in_secs = float(5) - self._implicit_wait_in_secs = float(0) @keyword def close_all_browsers(self): @@ -319,7 +316,7 @@ def select_frame(self, locator): details about locating elements. """ self.info("Selecting frame '%s'." % locator) - element = self._element_find(locator, True, True) + element = self.element_find(locator) self.ctx.browser.switch_to_frame(element) @keyword diff --git a/src/Selenium2Library/keywords/element.py b/src/Selenium2Library/keywords/element.py index 193fbed17..a2d699f68 100644 --- a/src/Selenium2Library/keywords/element.py +++ b/src/Selenium2Library/keywords/element.py @@ -1,12 +1,11 @@ from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.action_chains import ActionChains -from selenium.webdriver.remote.webelement import WebElement from Selenium2Library import utils from Selenium2Library.base import Base +from Selenium2Library.keywords.formelement import FormElementKeywords from Selenium2Library.locators import ElementFinder from Selenium2Library.locators import CustomLocator -from Selenium2Library.keywords.browsermanagement import BrowserManagementKeywords from Selenium2Library.robotlibcore import keyword @@ -16,7 +15,7 @@ def __init__(self, ctx): Base.__init__(self) self.ctx = ctx self.element_finder = ElementFinder() - self.log_source = BrowserManagementKeywords(self.ctx).log_source + self.form_element = FormElementKeywords(self.ctx) @keyword def get_webelement(self, locator): @@ -41,7 +40,7 @@ def current_frame_contains(self, text, loglevel='INFO'): See `Page Should Contain ` for explanation about `loglevel` argument. """ if not self.is_text_present(text): - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Page should have contained text '%s' " "but did not" % text) self.info("Current page contains text '%s'." % text) @@ -53,7 +52,7 @@ def current_frame_should_not_contain(self, text, loglevel='INFO'): See `Page Should Contain ` for explanation about `loglevel` argument. """ if self.is_text_present(text): - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Page should not have contained text '%s' " "but it did" % text) self.info("Current page should not contain text '%s'." % text) @@ -109,7 +108,7 @@ def frame_should_contain(self, locator, text, loglevel='INFO'): details about locating elements. """ if not self._frame_contains(locator, text): - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Page should have contained text '%s' " "but did not" % text) self.info("Current page contains text '%s'." % text) @@ -125,7 +124,7 @@ def page_should_contain(self, text, loglevel='INFO'): will not be logged. """ if not self._page_contains(text): - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Page should have contained text '%s' " "but did not" % text) self.info("Current page contains text '%s'." % text) @@ -141,7 +140,9 @@ def page_should_contain_element(self, locator, message='', loglevel='INFO'): Key attributes for arbitrary elements are `id` and `name`. See `introduction` for details about locating elements. """ - self._page_should_contain_element(locator, None, message, loglevel) + self.page_contains_element( + locator, message=message, loglevel=loglevel + ) @keyword def locator_should_match_x_times(self, locator, expected_locator_count, message='', loglevel='INFO'): @@ -159,7 +160,7 @@ def locator_should_match_x_times(self, locator, expected_locator_count, message= if not message: message = "Locator %s should have matched %s times but matched %s times"\ %(locator, expected_locator_count, actual_locator_count) - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError(message) self.info("Current page contains %s elements matching '%s'." % (actual_locator_count, locator)) @@ -171,7 +172,7 @@ def page_should_not_contain(self, text, loglevel='INFO'): See `Page Should Contain ` for explanation about `loglevel` argument. """ if self._page_contains(text): - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Page should not have contained text '%s'" % text) self.info("Current page does not contain text '%s'." % text) @@ -186,9 +187,9 @@ def page_should_not_contain_element(self, locator, message='', loglevel='INFO'): Key attributes for arbitrary elements are `id` and `name`. See `introduction` for details about locating elements. """ - self._page_should_not_contain_element(locator, None, message, loglevel) - - # Public, attributes + self.page_not_contains_element( + locator, message=message, loglevel=loglevel + ) @keyword def assign_id_to_element(self, locator, id): @@ -202,8 +203,8 @@ def assign_id_to_element(self, locator, id): | Page Should Contain Element | my id | """ self.info("Assigning temporary id '%s' to element '%s'" % (id, locator)) - element = self.self.element_find(locator) - self.ctx( + element = self.element_find(locator) + self.ctx.browser.execute_script( "arguments[0].id = '%s';" % id, element ) @@ -616,7 +617,9 @@ def page_should_contain_link(self, locator, message='', loglevel='INFO'): Key attributes for links are `id`, `name`, `href` and link text. See `introduction` for details about locating elements. """ - self._page_should_contain_element(locator, 'link', message, loglevel) + self.page_contains_element( + locator, tag='link', message=message, loglevel=loglevel + ) @keyword def page_should_not_contain_link(self, locator, message='', loglevel='INFO'): @@ -628,7 +631,7 @@ def page_should_not_contain_link(self, locator, message='', loglevel='INFO'): Key attributes for images are `id`, `src` and `alt`. See `introduction` for details about locating elements. """ - self._page_should_not_contain_element( + self.page_not_contains_element( locator, 'link', message, loglevel ) @@ -668,7 +671,9 @@ def page_should_contain_image(self, locator, message='', loglevel='INFO'): Key attributes for images are `id`, `src` and `alt`. See `introduction` for details about locating elements. """ - self._page_should_contain_element(locator, 'image', message, loglevel) + self.page_contains_element( + locator, tag='image', message=message, loglevel=loglevel + ) @keyword def page_should_not_contain_image(self, locator, message='', loglevel='INFO'): @@ -680,7 +685,7 @@ def page_should_not_contain_image(self, locator, message='', loglevel='INFO'): Key attributes for images are `id`, `src` and `alt`. See `introduction` for details about locating elements. """ - self._page_should_not_contain_element( + self.page_not_contains_element( locator, 'image', message, loglevel ) @@ -727,7 +732,7 @@ def xpath_should_match_x_times(self, xpath, expected_xpath_count, message='', lo if not message: message = "Xpath %s should have matched %s times but matched %s times"\ %(xpath, expected_xpath_count, actual_xpath_count) - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError(message) self.info("Current page contains %s elements matching '%s'." % (actual_xpath_count, xpath)) @@ -765,23 +770,6 @@ def remove_location_strategy(self, strategy_name): """ self.element_finder.unregister(strategy_name) - def element_find(self, locator, first_only=True, required=True, tag=None): - if isinstance(locator, basestring): - elements = self.element_finder.find(self.ctx.browser, locator, tag) - if required and len(elements) == 0: - raise ValueError( - "Element locator '{}' did not match any elements.".format( - locator - ) - ) - if first_only: - if not elements: - return None - return elements[0] - elif isinstance(locator, WebElement): - elements = locator - return elements - def _frame_contains(self, locator, text): browser = self.ctx.browser element = self.element_find(locator) @@ -805,7 +793,7 @@ def _get_value(self, locator, tag=None): def _is_enabled(self, locator): element = self.element_find(locator) - if not self._is_form_element(element): + if not self.form_element._is_form_element(element): raise AssertionError("ERROR: Element %s is not an input." % (locator)) if not element.is_enabled(): return False @@ -816,7 +804,7 @@ def _is_enabled(self, locator): def is_text_present(self, text): locator = "xpath=//*[contains(., %s)]" % utils.escape_xpath_value(text) - return self.is_element_present(locator) + return self.element_find(locator, required=False) def is_visible(self, locator): element = self.element_find(locator, required=False) @@ -865,9 +853,6 @@ def _parse_attribute_locator(self, attribute_locator): raise ValueError("Attribute locator '%s' does not contain an attribute name." % (attribute_locator)) return parts[0], parts[2] - def is_element_present(self, locator, tag=None): - return True if self.element_find(locator, required=False, tag=tag) else None - def _page_contains(self, text): browser = self.ctx.browser browser.switch_to_default_content() @@ -886,24 +871,3 @@ def _page_contains(self, text): if found_text: return True return False - - def _page_should_contain_element(self, locator, tag, message, loglevel): - element_name = tag if tag is not None else 'element' - if not self.is_element_present(locator, tag): - if not message: - message = "Page should have contained %s '%s' but did not"\ - % (element_name, locator) - self.log_source(loglevel) - raise AssertionError(message) - self.info("Current page contains %s '%s'." % (element_name, locator)) - - def _page_should_not_contain_element(self, locator, tag, message, loglevel): - element_name = tag if tag is not None else 'element' - if self.is_element_present(locator, tag): - if not message: - message = "Page should not have contained %s '%s'"\ - % (element_name, locator) - self.log_source(loglevel) - raise AssertionError(message) - self.info("Current page does not contain %s '%s'." - % (element_name, locator)) diff --git a/src/Selenium2Library/keywords/formelement.py b/src/Selenium2Library/keywords/formelement.py index 56984c78e..6cc77b855 100644 --- a/src/Selenium2Library/keywords/formelement.py +++ b/src/Selenium2Library/keywords/formelement.py @@ -2,7 +2,6 @@ from Selenium2Library.base import Base from Selenium2Library.robotlibcore import keyword -from Selenium2Library.keywords.element import ElementKeywords class FormElementKeywords(Base): @@ -10,7 +9,6 @@ class FormElementKeywords(Base): def __init__(self, ctx): Base.__init__(self) self.ctx = ctx - self.element_find = ElementKeywords(ctx) @keyword def submit_form(self, locator=None): @@ -23,7 +21,7 @@ def submit_form(self, locator=None): self.info("Submitting form '%s'." % locator) if not locator: locator = 'xpath=//form' - element = self.element_find.element_find(locator, tag='form') + element = self.element_find(locator, tag='form') element.submit() @keyword @@ -62,7 +60,7 @@ def page_should_contain_checkbox(self, locator, message='', loglevel='INFO'): Key attributes for checkboxes are `id` and `name`. See `introduction` for details about locating elements. """ - self.element_find._page_should_contain_element( + self.page_contains_element( locator, 'checkbox', message, loglevel ) @@ -76,7 +74,7 @@ def page_should_not_contain_checkbox(self, locator, message='', loglevel='INFO') Key attributes for checkboxes are `id` and `name`. See `introduction` for details about locating elements. """ - self.element_find._page_should_not_contain_element( + self.page_not_contains_element( locator, 'checkbox', message, loglevel ) @@ -116,7 +114,7 @@ def page_should_contain_radio_button(self, locator, message='', loglevel='INFO') Key attributes for radio buttons are `id`, `name` and `value`. See `introduction` for details about locating elements. """ - self.element_find._page_should_contain_element( + self.page_contains_element( locator, 'radio button', message, loglevel ) @@ -130,7 +128,7 @@ def page_should_not_contain_radio_button(self, locator, message='', loglevel='IN Key attributes for radio buttons are `id`, `name` and `value`. See `introduction` for details about locating elements. """ - self.element_find._page_should_not_contain_element( + self.page_not_contains_element( locator, 'radio button', message, loglevel ) @@ -199,7 +197,7 @@ def choose_file(self, locator, file_path): if not os.path.isfile(file_path): raise AssertionError("File '%s' does not exist on the local file system" % file_path) - self.element_find.element_find(locator).send_keys(file_path) + self.element_find(locator).send_keys(file_path) @keyword def input_password(self, locator, text): @@ -231,7 +229,7 @@ def page_should_contain_textfield(self, locator, message='', loglevel='INFO'): Key attributes for text fields are `id` and `name`. See `introduction` for details about locating elements. """ - self.element_find._page_should_contain_element( + self.page_contains_element( locator, 'text field', message, loglevel ) @@ -245,7 +243,7 @@ def page_should_not_contain_textfield(self, locator, message='', loglevel='INFO' Key attributes for text fields are `id` and `name`. See `introduction` for details about locating elements. """ - self.element_find._page_should_not_contain_element( + self.page_not_contains_element( locator, 'text field', message, loglevel ) @@ -258,7 +256,7 @@ def textfield_should_contain(self, locator, expected, message=''): Key attributes for text fields are `id` and `name`. See `introduction` for details about locating elements. """ - actual = self.element_find._get_value(locator, 'text field') + actual = self.get_value(locator, 'text field') if expected not in actual: if not message: message = "Text field '%s' should have contained text '%s' "\ @@ -275,11 +273,11 @@ def textfield_value_should_be(self, locator, expected, message=''): Key attributes for text fields are `id` and `name`. See `introduction` for details about locating elements. """ - element = self.element_find.element_find( + element = self.element_find( locator, required=False, tag='text field' ) if not element: - element = self.element_find.element_find( + element = self.element_find( locator, required=False, tag='file upload' ) actual = element.get_attribute('value') if element is not None else None @@ -299,7 +297,7 @@ def textarea_should_contain(self, locator, expected, message=''): Key attributes for text areas are `id` and `name`. See `introduction` for details about locating elements. """ - actual = self.element_find._get_value(locator, 'text area') + actual = self.get_value(locator, 'text area') if actual is not None: if expected not in actual: if not message: @@ -319,7 +317,7 @@ def textarea_value_should_be(self, locator, expected, message=''): Key attributes for text areas are `id` and `name`. See `introduction` for details about locating elements. """ - actual = self.element_find._get_value(locator, 'text area') + actual = self.get_value(locator, 'text area') if actual is not None: if expected!=actual: if not message: @@ -338,11 +336,11 @@ def click_button(self, locator): `introduction` for details about locating elements. """ self.info("Clicking button '%s'." % locator) - element = self.element_find.element_find( + element = self.element_find( locator, required=False, tag='input' ) if element is None: - element = self.element_find.element_find(locator, tag='button') + element = self.element_find(locator, tag='button') element.click() @keyword @@ -358,11 +356,11 @@ def page_should_contain_button(self, locator, message='', loglevel='INFO'): `introduction` for details about locating elements. """ try: - self.element_find._page_should_contain_element( + self.page_contains_element( locator, 'input', message, loglevel ) except AssertionError: - self.element_find._page_should_contain_element( + self.page_contains_element( locator, 'button', message, loglevel ) @@ -378,26 +376,26 @@ def page_should_not_contain_button(self, locator, message='', loglevel='INFO'): Key attributes for buttons are `id`, `name` and `value`. See `introduction` for details about locating elements. """ - self.element_find._page_should_not_contain_element( + self.page_not_contains_element( locator, 'button', message, loglevel ) - self.element_find._page_should_not_contain_element( + self.page_not_contains_element( locator, 'input', message, loglevel ) def _get_checkbox(self, locator): - return self.element_find.element_find(locator, tag='input') + return self.element_find(locator, tag='input') def _get_radio_buttons(self, group_name): xpath = "xpath=//input[@type='radio' and @name='%s']" % group_name self.debug('Radio group locator: ' + xpath) - return self.element_find.element_find(xpath, first_only=False) + return self.element_find(xpath, first_only=False) def _get_radio_button_with_value(self, group_name, value): xpath = "xpath=//input[@type='radio' and @name='%s' and (@value='%s' or @id='%s')]" \ % (group_name, value, value) self.debug('Radio group locator: ' + xpath) - return self.element_find.element_find(xpath) + return self.element_find(xpath) def _get_value_from_radio_buttons(self, elements): for element in elements: @@ -406,7 +404,7 @@ def _get_value_from_radio_buttons(self, elements): return None def _input_text_into_text_field(self, locator, text): - element = self.element_find.element_find(locator) + element = self.element_find(locator) element.clear() element.send_keys(text) diff --git a/src/Selenium2Library/keywords/javascript.py b/src/Selenium2Library/keywords/javascript.py index 4861d1edb..a1154e528 100644 --- a/src/Selenium2Library/keywords/javascript.py +++ b/src/Selenium2Library/keywords/javascript.py @@ -71,8 +71,12 @@ def _get_javascript_to_execute(self, code): codepath = code.replace('/', os.sep) if not (os.path.isabs(codepath) and os.path.isfile(codepath)): return code - self._html('Reading JavaScript from file %s.' - % (codepath.replace(os.sep, '/'), codepath)) + self.info( + 'Reading JavaScript from file %s.'.format( + codepath.replace(os.sep, '/'), codepath + ), + html=True + ) codefile = open(codepath) try: return codefile.read().strip() diff --git a/src/Selenium2Library/keywords/screenshot.py b/src/Selenium2Library/keywords/screenshot.py index c7c22a238..b75883164 100644 --- a/src/Selenium2Library/keywords/screenshot.py +++ b/src/Selenium2Library/keywords/screenshot.py @@ -1,6 +1,8 @@ import errno import os +from robot.libraries.BuiltIn import BuiltIn +from robot.libraries.BuiltIn import RobotNotRunningError from robot.utils import get_link_path from Selenium2Library.base import Base @@ -144,3 +146,14 @@ def _get_screenshot_index(self, filename): self._screenshot_index[filename] = 0 self._screenshot_index[filename] += 1 return self._screenshot_index[filename] + + def _get_log_dir(self): + try: + logfile = BuiltIn().get_variable_value('${LOG FILE}') + except RobotNotRunningError: + logfile = os.getcwd() + if logfile != 'NONE': + logdir = os.path.dirname(logfile) + else: + logdir = BuiltIn().get_variable_value('${OUTPUTDIR}') + return logdir diff --git a/src/Selenium2Library/keywords/selectelement.py b/src/Selenium2Library/keywords/selectelement.py index fb555f544..2029f5587 100644 --- a/src/Selenium2Library/keywords/selectelement.py +++ b/src/Selenium2Library/keywords/selectelement.py @@ -2,7 +2,6 @@ from selenium.webdriver.support.ui import Select from Selenium2Library.base import Base -from Selenium2Library.keywords.element import ElementKeywords from Selenium2Library.robotlibcore import keyword @@ -11,7 +10,6 @@ class SelectElementKeywords(Base): def __init__(self, ctx): Base.__init__(self) self.ctx = ctx - self.element_find = ElementKeywords(ctx) @keyword def get_list_items(self, locator, value=False): @@ -141,7 +139,7 @@ def page_should_contain_list(self, locator, message='', loglevel='INFO'): Key attributes for lists are `id` and `name`. See `introduction` for details about locating elements. """ - self.element_find._page_should_contain_element( + self.page_contains_element( locator, 'list', message, loglevel ) @@ -155,7 +153,7 @@ def page_should_not_contain_list(self, locator, message='', loglevel='INFO'): Key attributes for lists are `id` and `name`. See `introduction` for details about locating elements. """ - self.element_find._page_should_not_contain_element( + self.page_not_contains_element( locator, 'list', message, loglevel ) @@ -224,7 +222,7 @@ def select_from_list(self, locator, *items): else: if any (non_existing_items[:-1]): items_str = non_existing_items[:-1] and "Option(s) '%s'" % ", ".join(non_existing_items[:-1]) - self._warn("%s not found within list '%s'." % (items_str, locator)) + self.warn("%s not found within list '%s'." % (items_str, locator)) if items and items[-1] in non_existing_items: raise ValueError("Option '%s' not in list '%s'." % (items[-1], locator)) @@ -386,7 +384,7 @@ def _get_labels_for_options(self, options): return labels def _get_select_list(self, locator): - el = self.element_find.element_find(locator, tag='select') + el = self.element_find(locator, tag='select') return Select(el) def _get_select_list_options(self, select_list_or_locator): diff --git a/src/Selenium2Library/keywords/tableelement.py b/src/Selenium2Library/keywords/tableelement.py index 528eca129..c4b78cfe1 100644 --- a/src/Selenium2Library/keywords/tableelement.py +++ b/src/Selenium2Library/keywords/tableelement.py @@ -1,6 +1,5 @@ from Selenium2Library.base import Base from Selenium2Library.locators import TableElementFinder -from Selenium2Library.keywords.browsermanagement import BrowserManagementKeywords from Selenium2Library.robotlibcore import keyword @@ -10,7 +9,6 @@ def __init__(self, ctx): Base.__init__(self) self.ctx = ctx self._table_element_finder = TableElementFinder() - self.log_source = BrowserManagementKeywords(self.ctx).log_source @keyword def get_table_cell(self, table_locator, row, column, loglevel='INFO'): @@ -47,7 +45,7 @@ def get_table_cell(self, table_locator, row, column, loglevel='INFO'): columns.extend(rows[row_index].find_elements_by_tag_name('td')) if column_index < len(columns): return columns[column_index].text - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Cell in table %s in row #%s and column #%s could not be found." % (table_locator, str(row), str(column))) @@ -74,11 +72,11 @@ def table_cell_should_contain(self, table_locator, row, column, expected, loglev content = self.get_table_cell(table_locator, row, column, loglevel='NONE') except AssertionError as err: self.info(err) - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError(message) self.info("Cell contains %s." % (content)) if expected not in content: - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError(message) @keyword @@ -104,7 +102,7 @@ def table_column_should_contain(self, table_locator, col, expected, loglevel='IN """ element = self._table_element_finder.find_by_col(self.ctx.browser, table_locator, col, expected) if element is None: - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Column #%s in table identified by '%s' " "should have contained text '%s'." % (col, table_locator, expected)) @@ -122,7 +120,7 @@ def table_footer_should_contain(self, table_locator, expected, loglevel='INFO'): """ element = self._table_element_finder.find_by_footer(self.ctx.browser, table_locator, expected) if element is None: - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Footer in table identified by '%s' should have contained " "text '%s'." % (table_locator, expected)) @@ -138,7 +136,7 @@ def table_header_should_contain(self, table_locator, expected, loglevel='INFO'): """ element = self._table_element_finder.find_by_header(self.ctx.browser, table_locator, expected) if element is None: - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Header in table identified by '%s' should have contained " "text '%s'." % (table_locator, expected)) @@ -162,7 +160,7 @@ def table_row_should_contain(self, table_locator, row, expected, loglevel='INFO' """ element = self._table_element_finder.find_by_row(self.ctx.browser, table_locator, row, expected) if element is None: - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Row #%s in table identified by '%s' should have contained " "text '%s'." % (row, table_locator, expected)) @@ -178,6 +176,6 @@ def table_should_contain(self, table_locator, expected, loglevel='INFO'): """ element = self._table_element_finder.find_by_content(self.ctx.browser, table_locator, expected) if element is None: - self.log_source(loglevel) + self.ctx.log_source(loglevel) raise AssertionError("Table identified by '%s' should have contained text '%s'." \ % (table_locator, expected)) diff --git a/src/Selenium2Library/keywords/waiting.py b/src/Selenium2Library/keywords/waiting.py index ecb37c0e8..b9889f759 100644 --- a/src/Selenium2Library/keywords/waiting.py +++ b/src/Selenium2Library/keywords/waiting.py @@ -94,7 +94,9 @@ def wait_until_page_contains_element(self, locator, timeout=None, error=None): """ if not error: error = "Element '%s' did not appear in " % locator - self._wait_until(timeout, error, self._is_element_present, locator) + self._wait_until( + timeout, error, self.element_find, locator + ) @keyword def wait_until_page_does_not_contain_element(self, locator, timeout=None, error=None): @@ -111,7 +113,7 @@ def wait_until_page_does_not_contain_element(self, locator, timeout=None, error= Keyword Succeeds`. """ def check_present(): - present = self.element.is_element_present(locator) + present = self.element_find(locator) if not present: return else: @@ -181,7 +183,7 @@ def wait_until_element_is_enabled(self, locator, timeout=None, error=None): Succeeds`. """ def check_enabled(): - element = self.element.element_find(locator, True, False) + element = self.element_find(locator) if not element: return error or "Element locator '%s' did not match any elements after %s" % (locator, self._format_timeout(timeout)) @@ -207,7 +209,7 @@ def wait_until_element_contains(self, locator, text, timeout=None, error=None): `Wait Until Element Is Visible` and BuiltIn keyword `Wait Until Keyword Succeeds`. """ - element = self.element.element_find(locator, True, True) + element = self.element_find(locator) def check_text(): actual = element.text if text in actual: @@ -231,7 +233,7 @@ def wait_until_element_does_not_contain(self, locator, text, timeout=None, error `Wait Until Element Is Visible` and BuiltIn keyword `Wait Until Keyword Succeeds`. """ - element = self.element.element_find(locator, True, True) + element = self.element_find(locator) def check_text(): actual = element.text if text not in actual: diff --git a/test/acceptance/create_webdriver.robot b/test/acceptance/create_webdriver.robot index 6f902827d..25f9a0a24 100644 --- a/test/acceptance/create_webdriver.robot +++ b/test/acceptance/create_webdriver.robot @@ -7,7 +7,7 @@ Library Collections Create Webdriver Creates Functioning WebDriver [Documentation] LOG 2:1 INFO REGEXP: Creating an instance of the \\w+ WebDriver ... LOG 2:4 DEBUG REGEXP: Created \\w+ WebDriver instance with session id (\\w|-)+ - [Tags] Known Issue Chrome Known Issue Internet Explorer Known Issue Safari + [Tags] Known Issue Internet Explorer Known Issue Safari [Setup] Set Driver Variables Create Webdriver ${DRIVER_NAME} kwargs=${KWARGS} Go To ${FRONT_PAGE} diff --git a/test/resources/testlibs/CustomSeleniumKeywords.py b/test/resources/testlibs/CustomSeleniumKeywords.py index 8c6a79c6f..9c25cc405 100755 --- a/test/resources/testlibs/CustomSeleniumKeywords.py +++ b/test/resources/testlibs/CustomSeleniumKeywords.py @@ -9,7 +9,8 @@ def __init__(self, *args, **kwargs): we don't have to open a separate browser instance for the `Run On Failure Keyword Only Called Once` test.""" ret = super(CustomSeleniumKeywords, self).__init__(*args, **kwargs) - self._cache = BuiltIn().get_library_instance("Selenium2Library")._cache + self.s2l = BuiltIn().get_library_instance("Selenium2Library") + self._cache = self.s2l.cache def custom_selenium_keyword(self): self.custom_selenium_keyword_inner() From 98ee06a62a5bbb0229b16fedf2107cfee27d70df Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Fri, 24 Mar 2017 20:49:41 +0200 Subject: [PATCH 08/16] Refactored Base to contain getter for browser and browsers --- src/Selenium2Library/__init__.py | 30 ++++---- src/Selenium2Library/base.py | 19 ++++-- src/Selenium2Library/keywords/alert.py | 5 +- .../keywords/browsermanagement.py | 68 +++++++++---------- src/Selenium2Library/keywords/cookie.py | 13 ++-- src/Selenium2Library/keywords/element.py | 49 ++++++------- src/Selenium2Library/keywords/formelement.py | 4 -- src/Selenium2Library/keywords/javascript.py | 8 +-- src/Selenium2Library/keywords/runonfailure.py | 3 +- src/Selenium2Library/keywords/screenshot.py | 9 ++- .../keywords/selectelement.py | 6 +- src/Selenium2Library/keywords/tableelement.py | 15 ++-- src/Selenium2Library/keywords/waiting.py | 7 +- src/Selenium2Library/robotlibcore.py | 21 ++++-- test/unit/keywords/test_browsermanagement.py | 8 +-- test/unit/locators/test_windowmanager.py | 1 - 16 files changed, 129 insertions(+), 137 deletions(-) diff --git a/src/Selenium2Library/__init__.py b/src/Selenium2Library/__init__.py index e273a9aac..868ca6c6c 100644 --- a/src/Selenium2Library/__init__.py +++ b/src/Selenium2Library/__init__.py @@ -1,4 +1,4 @@ -from robot.api import logger +import warnings from .utils import BrowserCache from .keywords.alert import AlertKeywords @@ -183,7 +183,6 @@ def __init__(self, | Library `|` Selenium2Library `|` implicit_wait=5 `|` run_on_failure=Log Source | # Sets default implicit_wait to 5 seconds and runs `Log Source` on failure | | Library `|` Selenium2Library `|` timeout=10 `|` run_on_failure=Nothing | # Sets default timeout to 10 seconds and does nothing on failure | """ - self.logger = logger libraries = [ AlertKeywords(self), BrowserManagementKeywords(self), @@ -197,10 +196,8 @@ def __init__(self, ScreenshotKeywords(self), WaitingKeywords(self) ] - + self._browsers = BrowserCache() DynamicCore.__init__(self, libraries) - self.cache = BrowserCache() - self.screenshot_root_directory = screenshot_root_directory self.set_selenium_timeout(timeout) self.set_selenium_implicit_wait(implicit_wait) @@ -214,21 +211,24 @@ def run_keyword(self, name, args, kwargs): print 'run failure here' raise + def register_browser(self, browser, alias): + return self._browsers.register(browser, alias) + @property - def browser(self): - """returns the current active browser""" - if not self.cache.current: + def _browser(self): + """Current active browser""" + if not self._browsers.current: raise RuntimeError('No browser is open') - return self.cache.current + return self._browsers.current @property def _cache(self): - logger.warn('Deprecated, please use self.cache') - return self.cache + warnings.warn('"Selenium2Library._cache" is deprecated, ' + 'use public API instead.', DeprecationWarning) + return self._browsers def _current_browser(self): - logger.warn('Deprecated, please use self.browser') + warnings.warn('"Selenium2Library._current_browser" is deprecated, ' + 'use "Selenium2Library.browser" instead.', + DeprecationWarning) return self.browser - - def register_browser(self, browser, alias): - return self.cache.register(browser, alias) diff --git a/src/Selenium2Library/base.py b/src/Selenium2Library/base.py index f13e4b66d..69aa0de51 100644 --- a/src/Selenium2Library/base.py +++ b/src/Selenium2Library/base.py @@ -4,14 +4,17 @@ from .locators.elementfinder import ElementFinder +LOG_LEVELS = ['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'] + + class Base(object): - def __init__(self): + def __init__(self, ctx): + self.ctx = ctx self.element_finder = ElementFinder() self._speed_in_secs = 0.0 self._timeout_in_secs = 5.0 self._implicit_wait_in_secs = 5.0 - self.robot_log_levels = ['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'] def info(self, msg, html=False): logger.info(msg, html) @@ -20,15 +23,23 @@ def debug(self, msg, html=False): logger.debug(msg, html) def log(self, msg, level='INFO', html=False): - if level.upper() in self.robot_log_levels: + if level.upper() in LOG_LEVELS: logger.write(msg, level, html) def warn(self, msg, html=False): logger.warn(msg, html) + @property + def browser(self): + return self.ctx._browser + + @property + def browsers(self): + return self.ctx._browsers + def element_find(self, locator, first_only=True, required=True, tag=None): if isinstance(locator, basestring): - elements = self.element_finder.find(self.ctx.browser, locator, tag) + elements = self.element_finder.find(self.browser, locator, tag) if required and len(elements) == 0: raise ValueError( "Element locator '{}' did not match any elements.".format( diff --git a/src/Selenium2Library/keywords/alert.py b/src/Selenium2Library/keywords/alert.py index 97c79f212..db9a66749 100644 --- a/src/Selenium2Library/keywords/alert.py +++ b/src/Selenium2Library/keywords/alert.py @@ -14,7 +14,7 @@ class AlertKeywords(Base): DISMISS_ALERT = 'dismiss' def __init__(self, ctx): - self.ctx = ctx + Base.__init__(self, ctx) self._next_alert_dismiss_type = self.ACCEPT_ALERT @keyword @@ -145,5 +145,4 @@ def _alert_worker(self, dismiss_type=None): return text def _wait_alert(self): - return WebDriverWait(self.ctx.browser, 1).until( - EC.alert_is_present()) + return WebDriverWait(self.browser, 1).until(EC.alert_is_present()) diff --git a/src/Selenium2Library/keywords/browsermanagement.py b/src/Selenium2Library/keywords/browsermanagement.py index 3684bdf90..dbea371b4 100644 --- a/src/Selenium2Library/keywords/browsermanagement.py +++ b/src/Selenium2Library/keywords/browsermanagement.py @@ -37,8 +37,7 @@ class BrowserManagementKeywords(Base): def __init__(self, ctx): - Base.__init__(self) - self.ctx = ctx + Base.__init__(self, ctx) self._window_manager = WindowManager() @keyword @@ -52,18 +51,18 @@ def close_all_browsers(self): all browsers are closed. """ self.debug('Closing all browsers') - self.ctx.cache.close_all() + self.browsers.close_all() @keyword def close_browser(self): """Closes the current browser.""" - if self.ctx.cache.current: + if self.browsers.current: self.debug( 'Closing browser with session id {}'.format( - self.ctx.cache.current.session_id + self.browsers.current.session_id ) ) - self.ctx.cache.close() + self.browsers.close() @keyword def open_browser( @@ -217,10 +216,10 @@ def switch_browser(self, index_or_alias): | Switch Browser | ${id} | | | """ try: - self.ctx.cache.switch(index_or_alias) + self.browsers.switch(index_or_alias) self.debug( 'Switched to browser with Selenium session id %s' - % self.ctx.browser.session_id + % self.browser.session_id ) except (RuntimeError, DataError): # RF 2.6 uses RE, earlier DE raise RuntimeError("No browser with index or alias '%s' found." @@ -229,22 +228,19 @@ def switch_browser(self, index_or_alias): @keyword def close_window(self): """Closes currently opened pop-up window.""" - self.ctx.browser.close() + self.browser.close() @keyword def get_window_identifiers(self): """Returns and logs id attributes of all windows known to the browser.""" return self._log_list( - self._window_manager.get_window_ids(self.ctx.browser) + self._window_manager.get_window_ids(self.browser) ) @keyword def get_window_names(self): """Returns and logs names of all windows known to the browser.""" - values = self._window_manager.get_window_names( - self.ctx.browser - ) - + values = self._window_manager.get_window_names(self.browser) # for backward compatibility, since Selenium 1 would always # return this constant value for the main window if len(values) and values[0] == 'undefined': @@ -256,13 +252,13 @@ def get_window_names(self): def get_window_titles(self): """Returns and logs titles of all windows known to the browser.""" return self._log_list( - self._window_manager.get_window_titles(self.ctx.browser) + self._window_manager.get_window_titles(self.browser) ) @keyword def maximize_browser_window(self): """Maximizes current browser window.""" - self.ctx.browser.maximize_window() + self.browser.maximize_window() @keyword def get_window_size(self): @@ -271,7 +267,7 @@ def get_window_size(self): Example: | ${width} | ${height}= | Get Window Size | """ - size = self.ctx.browser.get_window_size() + size = self.browser.get_window_size() return size['width'], size['height'] @keyword @@ -284,7 +280,7 @@ def set_window_size(self, width, height): | Should Be Equal | ${width} | ${800} | | Should Be Equal | ${height} | ${600} | """ - return self.ctx.browser.set_window_size(width, height) + return self.browser.set_window_size(width, height) @keyword def get_window_position(self): @@ -293,7 +289,7 @@ def get_window_position(self): Example: | ${x} | ${y}= | Get Window Position | """ - position = self.ctx.browser.get_window_position() + position = self.browser.get_window_position() return position['x'], position['y'] @keyword @@ -306,7 +302,7 @@ def set_window_position(self, x, y): | Should Be Equal | ${x} | ${8} | | Should Be Equal | ${y} | ${10} | """ - return self.ctx.browser.set_window_position(x, y) + return self.browser.set_window_position(x, y) @keyword def select_frame(self, locator): @@ -317,7 +313,7 @@ def select_frame(self, locator): """ self.info("Selecting frame '%s'." % locator) element = self.element_find(locator) - self.ctx.browser.switch_to_frame(element) + self.browser.switch_to_frame(element) @keyword def select_window(self, locator=None): @@ -355,44 +351,44 @@ def select_window(self, locator=None): | Select Window | | | # Chooses the main window again | """ try: - return self.ctx.browser.current_window_handle + return self.browser.current_window_handle except NoSuchWindowException: pass finally: - self._window_manager.select(self.ctx.browser, locator) + self._window_manager.select(self.browser, locator) @keyword def list_windows(self): """Return all current window handles as a list""" - return self.ctx.browser.window_handles + return self.browser.window_handles @keyword def unselect_frame(self): """Sets the top frame as the current frame.""" - self.ctx.browser.switch_to_default_content() + self.browser.switch_to_default_content() @keyword def get_location(self): """Returns the current location.""" - return self.ctx.browser.current_url + return self.browser.current_url @keyword def get_locations(self): """Returns and logs current locations of all windows known to the browser.""" return self._log_list( [window_info[4] for window_info in - self._window_manager._get_window_infos(self.ctx.browser)] + self._window_manager._get_window_infos(self.browser)] ) @keyword def get_source(self): """Returns the entire html source of the current page or frame.""" - return self.ctx.browser.page_source + return self.browser.page_source @keyword def get_title(self): """Returns title of current page.""" - return self.ctx.browser.title + return self.browser.title @keyword def location_should_be(self, url): @@ -449,18 +445,18 @@ def title_should_be(self, title): @keyword def go_back(self): """Simulates the user clicking the "back" button on their browser.""" - self.ctx.browser.back() + self.browser.back() @keyword def go_to(self, url): """Navigates the active browser instance to the provided URL.""" self.info("Opening url '%s'" % url) - self.ctx.browser.get(url) + self.browser.get(url) @keyword def reload_page(self): """Simulates user reloading page.""" - self.ctx.browser.refresh() + self.browser.refresh() @keyword def get_selenium_speed(self): @@ -503,7 +499,7 @@ def set_selenium_speed(self, seconds): """ old_speed = self._speed_in_secs self._speed_in_secs = timestr_to_secs(seconds) - for browser in self.ctx.cache.browsers: + for browser in self.browsers.browsers: browser._speed = self._speed_in_secs self._monkey_patch_speed(browser) return old_speed @@ -528,7 +524,7 @@ def set_selenium_timeout(self, seconds): """ old_timeout = self.get_selenium_timeout() self._timeout_in_secs = timestr_to_secs(seconds) - for browser in self.ctx.cache.get_open_browsers(): + for browser in self.browsers.get_open_browsers(): browser.set_script_timeout(self._timeout_in_secs) return old_timeout @@ -548,7 +544,7 @@ def set_selenium_implicit_wait(self, seconds): """ old_wait = self.get_selenium_implicit_wait() self._implicit_wait_in_secs = timestr_to_secs(seconds) - for browser in self.ctx.cache.get_open_browsers(): + for browser in self.browsers.get_open_browsers(): browser.implicitly_wait(self._implicit_wait_in_secs) return old_wait @@ -566,7 +562,7 @@ def set_browser_implicit_wait(self, seconds): See also `Set Selenium Implicit Wait`. """ implicit_wait_in_secs = timestr_to_secs(seconds) - self.ctx.browser.implicitly_wait(implicit_wait_in_secs) + self.browser.implicitly_wait(implicit_wait_in_secs) def _get_browser_creation_function(self, browser_name): func_name = BROWSER_NAMES.get(browser_name.lower().replace(' ', '')) diff --git a/src/Selenium2Library/keywords/cookie.py b/src/Selenium2Library/keywords/cookie.py index 27c71815a..c56ee9427 100644 --- a/src/Selenium2Library/keywords/cookie.py +++ b/src/Selenium2Library/keywords/cookie.py @@ -4,13 +4,10 @@ class CookieKeywords(Base): - def __init__(self, ctx): - self.ctx = ctx - @keyword def delete_all_cookies(self): """Deletes all cookies.""" - self.ctx.browser.delete_all_cookies() + self.browser.delete_all_cookies() @keyword def delete_cookie(self, name): @@ -18,13 +15,13 @@ def delete_cookie(self, name): If the cookie is not found, nothing happens. """ - self.ctx.browser.delete_cookie(name) + self.browser.delete_cookie(name) @keyword def get_cookies(self): """Returns all cookies of the current page.""" pairs = [] - for cookie in self.ctx.browser.get_cookies(): + for cookie in self.browser.get_cookies(): pairs.append(cookie['name'] + "=" + cookie['value']) return '; '.join(pairs) @@ -34,7 +31,7 @@ def get_cookie_value(self, name): If no cookie is found with `name`, this keyword fails. """ - cookie = self.ctx.browser.get_cookie(name) + cookie = self.browser.get_cookie(name) if cookie is not None: return cookie['value'] raise ValueError("Cookie with name %s not found." % name) @@ -56,4 +53,4 @@ def add_cookie(self, name, value, path=None, domain=None, secure=None, # secure should be True or False so check explicitly for None if secure is not None: new_cookie['secure'] = secure - self.ctx.browser.add_cookie(new_cookie) + self.browser.add_cookie(new_cookie) diff --git a/src/Selenium2Library/keywords/element.py b/src/Selenium2Library/keywords/element.py index a2d699f68..c5ed1a906 100644 --- a/src/Selenium2Library/keywords/element.py +++ b/src/Selenium2Library/keywords/element.py @@ -12,10 +12,9 @@ class ElementKeywords(Base): def __init__(self, ctx): - Base.__init__(self) - self.ctx = ctx + Base.__init__(self, ctx) self.element_finder = ElementFinder() - self.form_element = FormElementKeywords(self.ctx) + self.form_element = FormElementKeywords(ctx) @keyword def get_webelement(self, locator): @@ -204,9 +203,7 @@ def assign_id_to_element(self, locator, id): """ self.info("Assigning temporary id '%s' to element '%s'" % (id, locator)) element = self.element_find(locator) - self.ctx.browser.execute_script( - "arguments[0].id = '%s';" % id, element - ) + self.browser.execute_script("arguments[0].id = '%s';" % id, element) @keyword def element_should_be_disabled(self, locator): @@ -404,7 +401,7 @@ def click_element_at_coordinates(self, locator, xoffset, yoffset): """ self.info("Click clicking element '%s' in coordinates '%s', '%s'." % (locator, xoffset, yoffset)) element = self.element_find(locator) - action = ActionChains(self.ctx.browser) + action = ActionChains(self.browser) action.move_to_element(element) action.move_by_offset(xoffset, yoffset) action.click() @@ -419,16 +416,14 @@ def double_click_element(self, locator): """ self.info("Double clicking element '%s'." % locator) element = self.element_find(locator) - action = ActionChains(self.ctx.browser) + action = ActionChains(self.browser) action.double_click(element).perform() @keyword def focus(self, locator): """Sets focus to element identified by `locator`.""" element = self.element_find(locator) - self.ctx.browser.execute_script( - "arguments[0].focus();", element - ) + self.browser.execute_script("arguments[0].focus();", element) @keyword def drag_and_drop(self, source, target): @@ -445,7 +440,7 @@ def drag_and_drop(self, source, target): """ src_elem = self.element_find(source) trg_elem = self.element_find(target) - action = ActionChains(self.ctx.browser) + action = ActionChains(self.browser) action.drag_and_drop(src_elem, trg_elem).perform() @keyword @@ -459,7 +454,7 @@ def drag_and_drop_by_offset(self, source, xoffset, yoffset): | Drag And Drop By Offset | myElem | 50 | -35 | # Move myElem 50px right and 35px down. | """ src_elem = self.element_find(source) - action = ActionChains(self.ctx.browser) + action = ActionChains(self.browser) action.drag_and_drop_by_offset(src_elem, xoffset, yoffset) action.perform() @@ -479,7 +474,7 @@ def mouse_down(self, locator): element = self.element_find(locator, required=False) if element is None: raise AssertionError("ERROR: Element %s not found." % (locator)) - action = ActionChains(self.ctx.browser) + action = ActionChains(self.browser) action.click_and_hold(element).perform() @keyword @@ -496,7 +491,7 @@ def mouse_out(self, locator): size = element.size offsetx = (size['width'] / 2) + 1 offsety = (size['height'] / 2) + 1 - action = ActionChains(self.ctx.browser) + action = ActionChains(self.browser) action.move_to_element(element).move_by_offset(offsetx, offsety) action.perform() @@ -511,7 +506,7 @@ def mouse_over(self, locator): element = self.element_find(locator, required=False) if element is None: raise AssertionError("ERROR: Element %s not found." % (locator)) - action = ActionChains(self.ctx.browser) + action = ActionChains(self.browser) action.move_to_element(element).perform() @keyword @@ -525,13 +520,13 @@ def mouse_up(self, locator): element = self.element_find(locator, required=False) if element is None: raise AssertionError("ERROR: Element %s not found." % (locator)) - ActionChains(self.ctx.browser).release(element).perform() + ActionChains(self.browser).release(element).perform() @keyword def open_context_menu(self, locator): """Opens context menu on element identified by `locator`.""" element = self.element_find(locator) - action = ActionChains(self.ctx.browser) + action = ActionChains(self.browser) action.context_click(element).perform() @keyword @@ -554,7 +549,7 @@ def simulate(self, locator, event): evt.initEvent(eventName, true, true); return !element.dispatchEvent(evt); """ - self.ctx.browser.execute_script(script, element, event) + self.browser.execute_script(script, element, event) @keyword def press_key(self, locator, key): @@ -604,7 +599,7 @@ def mouse_down_on_link(self, locator): `introduction` for details about locating elements. """ element = self.element_find(locator, tag='link') - action = ActionChains(self.ctx.browser) + action = ActionChains(self.browser) action.click_and_hold(element).perform() @keyword @@ -659,7 +654,7 @@ def mouse_down_on_image(self, locator): `introduction` for details about locating elements. """ element = self.element_find(locator, tag='image') - action = ActionChains(self.ctx.browser) + action = ActionChains(self.browser) action.click_and_hold(element).perform() @keyword @@ -771,12 +766,11 @@ def remove_location_strategy(self, strategy_name): self.element_finder.unregister(strategy_name) def _frame_contains(self, locator, text): - browser = self.ctx.browser element = self.element_find(locator) - browser.switch_to_frame(element) + self.browser.switch_to_frame(element) self.info("Searching for text from frame '%s'." % locator) found = self.is_text_present(text) - browser.switch_to_default_content() + self.browser.switch_to_default_content() return found def _get_text(self, locator): @@ -854,8 +848,7 @@ def _parse_attribute_locator(self, attribute_locator): return parts[0], parts[2] def _page_contains(self, text): - browser = self.ctx.browser - browser.switch_to_default_content() + self.browser.switch_to_default_content() if self.is_text_present(text): return True @@ -865,9 +858,9 @@ def _page_contains(self, text): ) self.debug('Current frame has %d subframes' % len(subframes)) for frame in subframes: - browser.switch_to_frame(frame) + self.browser.switch_to_frame(frame) found_text = self.is_text_present(text) - browser.switch_to_default_content() + self.browser.switch_to_default_content() if found_text: return True return False diff --git a/src/Selenium2Library/keywords/formelement.py b/src/Selenium2Library/keywords/formelement.py index 6cc77b855..0bd987d4f 100644 --- a/src/Selenium2Library/keywords/formelement.py +++ b/src/Selenium2Library/keywords/formelement.py @@ -6,10 +6,6 @@ class FormElementKeywords(Base): - def __init__(self, ctx): - Base.__init__(self) - self.ctx = ctx - @keyword def submit_form(self, locator=None): """Submits a form identified by `locator`. diff --git a/src/Selenium2Library/keywords/javascript.py b/src/Selenium2Library/keywords/javascript.py index a1154e528..2aeff1af3 100644 --- a/src/Selenium2Library/keywords/javascript.py +++ b/src/Selenium2Library/keywords/javascript.py @@ -6,10 +6,6 @@ class JavaScriptKeywords(Base): - def __init__(self, ctx): - Base.__init__(self) - self.ctx = ctx - @keyword def execute_javascript(self, *code): """Executes the given JavaScript code. @@ -40,7 +36,7 @@ def execute_javascript(self, *code): """ js = self._get_javascript_to_execute(''.join(code)) self.info("Executing JavaScript:\n%s" % js) - return self.ctx.browser.execute_script(js) + return self.browser.execute_script(js) @keyword def execute_async_javascript(self, *code): @@ -65,7 +61,7 @@ def execute_async_javascript(self, *code): """ js = self._get_javascript_to_execute(''.join(code)) self.info("Executing Asynchronous JavaScript:\n%s" % js) - return self.ctx.browser.execute_async_script(js) + return self.browser.execute_async_script(js) def _get_javascript_to_execute(self, code): codepath = code.replace('/', os.sep) diff --git a/src/Selenium2Library/keywords/runonfailure.py b/src/Selenium2Library/keywords/runonfailure.py index 5c65b2711..d35596945 100644 --- a/src/Selenium2Library/keywords/runonfailure.py +++ b/src/Selenium2Library/keywords/runonfailure.py @@ -7,8 +7,7 @@ class RunOnFailureKeywords(Base): def __init__(self, ctx): - Base.__init__(self) - self.ctx = ctx + Base.__init__(self, ctx) self._run_on_failure_keyword = None self._running_on_failure_routine = False diff --git a/src/Selenium2Library/keywords/screenshot.py b/src/Selenium2Library/keywords/screenshot.py index b75883164..dc801a89e 100644 --- a/src/Selenium2Library/keywords/screenshot.py +++ b/src/Selenium2Library/keywords/screenshot.py @@ -13,8 +13,7 @@ class ScreenshotKeywords(Base): def __init__(self, ctx): - Base.__init__(self) - self.ctx = ctx + Base.__init__(self, ctx) self._screenshot_index = {} self._screenshot_path_stack = [] self.screenshot_root_directory = None @@ -93,11 +92,11 @@ def capture_page_screenshot(self, """ path, link = self._get_screenshot_paths(filename) self._create_directory(path) - if hasattr(self.ctx.browser, 'get_screenshot_as_file'): - if not self.ctx.browser.get_screenshot_as_file(path): + if hasattr(self.browser, 'get_screenshot_as_file'): + if not self.browser.get_screenshot_as_file(path): raise RuntimeError('Failed to save screenshot ' + link) else: - if not self.ctx.browser.save_screenshot(path): + if not self.browser.save_screenshot(path): raise RuntimeError('Failed to save screenshot ' + link) # Image is shown on its own row and thus prev row is closed on purpose msg = ( diff --git a/src/Selenium2Library/keywords/selectelement.py b/src/Selenium2Library/keywords/selectelement.py index 2029f5587..4bf0f9b16 100644 --- a/src/Selenium2Library/keywords/selectelement.py +++ b/src/Selenium2Library/keywords/selectelement.py @@ -7,10 +7,6 @@ class SelectElementKeywords(Base): - def __init__(self, ctx): - Base.__init__(self) - self.ctx = ctx - @keyword def get_list_items(self, locator, value=False): """Returns the labels or values in the select list identified by `locator`. @@ -412,7 +408,7 @@ def _is_multiselect_list(self, select): return False def _unselect_all_options_from_multi_select_list(self, select): - self.ctx.browser.execute_script( + self.browser.execute_script( "arguments[0].selectedIndex = -1;", select ) diff --git a/src/Selenium2Library/keywords/tableelement.py b/src/Selenium2Library/keywords/tableelement.py index c4b78cfe1..ad4d8aade 100644 --- a/src/Selenium2Library/keywords/tableelement.py +++ b/src/Selenium2Library/keywords/tableelement.py @@ -6,8 +6,7 @@ class TableElementKeywords(Base): def __init__(self, ctx): - Base.__init__(self) - self.ctx = ctx + Base.__init__(self, ctx) self._table_element_finder = TableElementFinder() @keyword @@ -31,7 +30,7 @@ def get_table_cell(self, table_locator, row, column, loglevel='INFO'): if column > 0: column_index = column - 1 table = self._table_element_finder.find( - self.ctx.browser, table_locator + self.browser, table_locator ) if table is not None: rows = table.find_elements_by_xpath("./thead/tr") @@ -100,7 +99,7 @@ def table_column_should_contain(self, table_locator, col, expected, loglevel='IN See `Page Should Contain Element` for explanation about `loglevel` argument. """ - element = self._table_element_finder.find_by_col(self.ctx.browser, table_locator, col, expected) + element = self._table_element_finder.find_by_col(self.browser, table_locator, col, expected) if element is None: self.ctx.log_source(loglevel) raise AssertionError("Column #%s in table identified by '%s' " @@ -118,7 +117,7 @@ def table_footer_should_contain(self, table_locator, expected, loglevel='INFO'): See `Page Should Contain Element` for explanation about `loglevel` argument. """ - element = self._table_element_finder.find_by_footer(self.ctx.browser, table_locator, expected) + element = self._table_element_finder.find_by_footer(self.browser, table_locator, expected) if element is None: self.ctx.log_source(loglevel) raise AssertionError("Footer in table identified by '%s' should have contained " @@ -134,7 +133,7 @@ def table_header_should_contain(self, table_locator, expected, loglevel='INFO'): See `Page Should Contain Element` for explanation about `loglevel` argument. """ - element = self._table_element_finder.find_by_header(self.ctx.browser, table_locator, expected) + element = self._table_element_finder.find_by_header(self.browser, table_locator, expected) if element is None: self.ctx.log_source(loglevel) raise AssertionError("Header in table identified by '%s' should have contained " @@ -158,7 +157,7 @@ def table_row_should_contain(self, table_locator, row, expected, loglevel='INFO' See `Page Should Contain Element` for explanation about `loglevel` argument. """ - element = self._table_element_finder.find_by_row(self.ctx.browser, table_locator, row, expected) + element = self._table_element_finder.find_by_row(self.browser, table_locator, row, expected) if element is None: self.ctx.log_source(loglevel) raise AssertionError("Row #%s in table identified by '%s' should have contained " @@ -174,7 +173,7 @@ def table_should_contain(self, table_locator, expected, loglevel='INFO'): See `Page Should Contain Element` for explanation about `loglevel` argument. """ - element = self._table_element_finder.find_by_content(self.ctx.browser, table_locator, expected) + element = self._table_element_finder.find_by_content(self.browser, table_locator, expected) if element is None: self.ctx.log_source(loglevel) raise AssertionError("Table identified by '%s' should have contained text '%s'." \ diff --git a/src/Selenium2Library/keywords/waiting.py b/src/Selenium2Library/keywords/waiting.py index b9889f759..c2ac2d5e7 100644 --- a/src/Selenium2Library/keywords/waiting.py +++ b/src/Selenium2Library/keywords/waiting.py @@ -10,9 +10,8 @@ class WaitingKeywords(Base): def __init__(self, ctx): - Base.__init__(self) - self.ctx = ctx - self.element = ElementKeywords(self.ctx) + Base.__init__(self, ctx) + self.element = ElementKeywords(ctx) @keyword def wait_for_condition(self, condition, timeout=None, error=None): @@ -36,7 +35,7 @@ def wait_for_condition(self, condition, timeout=None, error=None): error = "Condition '%s' did not become true in " % condition self._wait_until( timeout, error, - lambda: self.ctx.browser.execute_script(condition) is True) + lambda: self.browser.execute_script(condition) is True) @keyword def wait_until_page_contains(self, text, timeout=None, error=None): diff --git a/src/Selenium2Library/robotlibcore.py b/src/Selenium2Library/robotlibcore.py index e0b8ee6af..ec40e0b7e 100644 --- a/src/Selenium2Library/robotlibcore.py +++ b/src/Selenium2Library/robotlibcore.py @@ -31,10 +31,23 @@ def _find_keywords(self, *libraries): yield kw_name, getattr(library, name) def _get_members(self, library): - # Avoid calling properties by getting members from class, not instance. + if inspect.ismodule(library): + return inspect.getmembers(library) if inspect.isclass(library): - library = type(library) - return inspect.getmembers(library) + raise TypeError('Libraries must be modules or instances, got ' + 'class {!r} instead.'.format(library.__name__)) + if type(library) != library.__class__: + raise TypeError('Libraries must be modules or new-style class ' + 'instances, got old-style class {!r} instead.' + .format(library.__class__.__name__)) + return self._get_members_from_instannce(library) + + def _get_members_from_instannce(self, instance): + # Avoid calling properties by getting members from class, not instance. + cls = type(instance) + for name in dir(instance): + owner = cls if hasattr(cls, name) else instance + yield name, getattr(owner, name) def __getattr__(self, name): if name in self.keywords: @@ -98,4 +111,4 @@ def get_keyword_documentation(self, name): class StaticCore(HybridCore): def __init__(self): - HybridCore.__init__(self, []) + HybridCore.__init__(self, []) \ No newline at end of file diff --git a/test/unit/keywords/test_browsermanagement.py b/test/unit/keywords/test_browsermanagement.py index adb6729f1..462e75791 100644 --- a/test/unit/keywords/test_browsermanagement.py +++ b/test/unit/keywords/test_browsermanagement.py @@ -96,17 +96,17 @@ def test_capabilities_attribute_not_modified(self): def test_set_selenium_timeout_only_affects_open_browsers(self): ctx = mock() - cache = mock() - ctx.cache = cache + _browsers = mock() + ctx._browsers = _browsers first_browser, second_browser = mock(), mock() - when(cache).get_open_browsers().thenReturn( + when(_browsers).get_open_browsers().thenReturn( [first_browser, second_browser] ) bm = BrowserManagementKeywords(ctx) bm.set_selenium_timeout("10 seconds") verify(first_browser).set_script_timeout(10.0) verify(second_browser).set_script_timeout(10.0) - when(cache).get_open_browsers().thenReturn( + when(_browsers).get_open_browsers().thenReturn( [] ) bm.set_selenium_timeout("20 seconds") diff --git a/test/unit/locators/test_windowmanager.py b/test/unit/locators/test_windowmanager.py index 3ee392efa..4faabab4f 100644 --- a/test/unit/locators/test_windowmanager.py +++ b/test/unit/locators/test_windowmanager.py @@ -312,7 +312,6 @@ def _make_mock_browser(self, *window_specs): browser = mock() current_window = mock() browser.window_handles = [] - browser.window_handles = [] window_infos = {} for window_spec in window_specs: handle = uuid.uuid4().hex From 257ba6baa2340ce62aa80f24990601ba6d04004f Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Mon, 27 Mar 2017 23:08:29 +0300 Subject: [PATCH 09/16] Fixes run on failure --- src/Selenium2Library/__init__.py | 4 ++- src/Selenium2Library/keywords/runonfailure.py | 30 ++++++++----------- test/acceptance/keywords/run_on_failure.robot | 8 +++++ .../testlibs/CustomSeleniumKeywords.py | 14 ++++++--- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/Selenium2Library/__init__.py b/src/Selenium2Library/__init__.py index 868ca6c6c..d8363ed50 100644 --- a/src/Selenium2Library/__init__.py +++ b/src/Selenium2Library/__init__.py @@ -183,6 +183,8 @@ def __init__(self, | Library `|` Selenium2Library `|` implicit_wait=5 `|` run_on_failure=Log Source | # Sets default implicit_wait to 5 seconds and runs `Log Source` on failure | | Library `|` Selenium2Library `|` 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 libraries = [ AlertKeywords(self), BrowserManagementKeywords(self), @@ -208,7 +210,7 @@ def run_keyword(self, name, args, kwargs): try: return DynamicCore.run_keyword(self, name, args, kwargs) except Exception: - print 'run failure here' + RunOnFailureKeywords(self)._run_on_failure() raise def register_browser(self, browser, alias): diff --git a/src/Selenium2Library/keywords/runonfailure.py b/src/Selenium2Library/keywords/runonfailure.py index d35596945..b6d82838c 100644 --- a/src/Selenium2Library/keywords/runonfailure.py +++ b/src/Selenium2Library/keywords/runonfailure.py @@ -6,11 +6,6 @@ class RunOnFailureKeywords(Base): - def __init__(self, ctx): - Base.__init__(self, ctx) - self._run_on_failure_keyword = None - self._running_on_failure_routine = False - @keyword def register_keyword_to_run_on_failure(self, keyword): """Sets the keyword to execute when a Selenium2Library keyword fails. @@ -34,34 +29,33 @@ def register_keyword_to_run_on_failure(self, keyword): | ${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._run_on_failure_keyword - old_keyword_text = old_keyword if old_keyword is not None else "No 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 is not None else "No keyword" + new_keyword_text = new_keyword if new_keyword else "No keyword" - self._run_on_failure_keyword = new_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): - self._run_on_failure_keyword = True - if self._run_on_failure_keyword is None: + if not self.ctx._run_on_failure_keyword: return - if self._running_on_failure_routine: + if self.ctx._running_on_failure_routine: return - self._running_on_failure_routine = True + self.ctx._running_on_failure_routine = True try: - BuiltIn().run_keyword(self._run_on_failure_keyword) + BuiltIn().run_keyword(self.ctx._run_on_failure_keyword) except Exception as err: self._run_on_failure_error(err) finally: - self._running_on_failure_routine = False + self.ctx._running_on_failure_routine = False def _run_on_failure_error(self, err): - err = "Keyword '%s' could not be run on failure: %s" % (self._run_on_failure_keyword, err) - if hasattr(self, '_warn'): - self._warn(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) diff --git a/test/acceptance/keywords/run_on_failure.robot b/test/acceptance/keywords/run_on_failure.robot index 630367b0b..27e91a7e5 100644 --- a/test/acceptance/keywords/run_on_failure.robot +++ b/test/acceptance/keywords/run_on_failure.robot @@ -43,6 +43,11 @@ Run on Failure Returns Previous Value ${old}= Register Keyword to Run on Failure ${old} Should Be Equal ${old} Log Source +Run On Failure also fails + Register Keyword to Run on Failure Failure During Run On failure + Run Keyword And Expect Error ${FAILURE MESSAGE} Page Should Not Contain + ... needle loglevel=None + *** Keywords *** On Fail [Documentation] On Fail @@ -61,3 +66,6 @@ Restore Old Search Order Open Browser To Front Page [Documentation] Open Browser To Front Page Open Browser ${FRONT PAGE} + +Failure During Run On failure + Page Should Not Contain needle diff --git a/test/resources/testlibs/CustomSeleniumKeywords.py b/test/resources/testlibs/CustomSeleniumKeywords.py index 9c25cc405..f5a241791 100755 --- a/test/resources/testlibs/CustomSeleniumKeywords.py +++ b/test/resources/testlibs/CustomSeleniumKeywords.py @@ -1,19 +1,25 @@ -from Selenium2Library import Selenium2Library from robot.utils import asserts from robot.libraries.BuiltIn import BuiltIn + +from Selenium2Library import Selenium2Library +from Selenium2Library.robotlibcore import keyword + + class CustomSeleniumKeywords(Selenium2Library): def __init__(self, *args, **kwargs): """Share `Selenium2Library`'s cache of browsers, so that we don't have to open a separate browser instance for the `Run On Failure Keyword Only Called Once` test.""" - ret = super(CustomSeleniumKeywords, self).__init__(*args, **kwargs) + super(CustomSeleniumKeywords, self).__init__(*args, **kwargs) self.s2l = BuiltIn().get_library_instance("Selenium2Library") - self._cache = self.s2l.cache + self._browsers = self.s2l._browsers + @keyword def custom_selenium_keyword(self): self.custom_selenium_keyword_inner() + @keyword def custom_selenium_keyword_inner(self): - asserts.assert_false(True) \ No newline at end of file + asserts.assert_false(True) From 18eb9b3f767fa3368187807e431586f40ad0ae39 Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Thu, 30 Mar 2017 23:06:34 +0300 Subject: [PATCH 10/16] Fixes waiting keywords --- src/Selenium2Library/keywords/waiting.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Selenium2Library/keywords/waiting.py b/src/Selenium2Library/keywords/waiting.py index c2ac2d5e7..8eba38800 100644 --- a/src/Selenium2Library/keywords/waiting.py +++ b/src/Selenium2Library/keywords/waiting.py @@ -91,11 +91,11 @@ def wait_until_page_contains_element(self, locator, timeout=None, error=None): `Wait Until Element Is Visible` and BuiltIn keyword `Wait Until Keyword Succeeds`. """ + def is_element_present(locator): + return self.element_find(locator, required=False) is not None if not error: error = "Element '%s' did not appear in " % locator - self._wait_until( - timeout, error, self.element_find, locator - ) + self._wait_until(timeout, error, is_element_present, locator) @keyword def wait_until_page_does_not_contain_element(self, locator, timeout=None, error=None): @@ -112,7 +112,7 @@ def wait_until_page_does_not_contain_element(self, locator, timeout=None, error= Keyword Succeeds`. """ def check_present(): - present = self.element_find(locator) + present = self.element_find(locator, required=False) if not present: return else: @@ -182,7 +182,7 @@ def wait_until_element_is_enabled(self, locator, timeout=None, error=None): Succeeds`. """ def check_enabled(): - element = self.element_find(locator) + element = self.element_find(locator, required=False) if not element: return error or "Element locator '%s' did not match any elements after %s" % (locator, self._format_timeout(timeout)) From 71cfe9441a21d75a31c24a45abe7105bfde0d128 Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Sat, 1 Apr 2017 21:32:01 +0300 Subject: [PATCH 11/16] Added TODO's to mark fixing in next PR --- src/Selenium2Library/base.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Selenium2Library/base.py b/src/Selenium2Library/base.py index 69aa0de51..91726e7d8 100644 --- a/src/Selenium2Library/base.py +++ b/src/Selenium2Library/base.py @@ -37,6 +37,8 @@ def browser(self): def browsers(self): return self.ctx._browsers + # TODO: Move logic in elementfinder.ElementFinder but keep method as proxy + # in Base class def element_find(self, locator, first_only=True, required=True, tag=None): if isinstance(locator, basestring): elements = self.element_finder.find(self.browser, locator, tag) @@ -54,12 +56,16 @@ def element_find(self, locator, first_only=True, required=True, tag=None): elements = locator return elements + # TODO: Move logic in elementfinder.ElementFinder but keep method as proxy + # in Base class def get_value(self, locator, tag=None): element = self.element_find( locator, required=False, tag=tag ) return element.get_attribute('value') if element is not None else None + # TODO: Move logic in elementfinder.ElementFinder but keep method as proxy + # in Base class def page_contains_element(self, locator, tag=None, message=None, loglevel='INFO'): element_name = tag if tag else 'element' @@ -75,6 +81,8 @@ def page_contains_element(self, locator, tag=None, "Current page contains %s '%s'." % (element_name, locator) ) + # TODO: Move logic in elementfinder.ElementFinder but keep method as proxy + # in Base class def page_not_contains_element(self, locator, tag=None, message=None, loglevel='INFO'): element_name = tag if tag else 'element' From 1b9d7175e99d5c9ad9b2751483c84f401d42613b Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Tue, 25 Apr 2017 22:24:58 +0300 Subject: [PATCH 12/16] Fixes imports to follow PEP8 Also changes run_on_failure to public method --- src/Selenium2Library/__init__.py | 6 +++--- src/Selenium2Library/keywords/alert.py | 2 +- src/Selenium2Library/keywords/element.py | 4 ++-- src/Selenium2Library/keywords/runonfailure.py | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Selenium2Library/__init__.py b/src/Selenium2Library/__init__.py index d8363ed50..7b86519ed 100644 --- a/src/Selenium2Library/__init__.py +++ b/src/Selenium2Library/__init__.py @@ -1,6 +1,5 @@ import warnings -from .utils import BrowserCache from .keywords.alert import AlertKeywords from .keywords.browsermanagement import BrowserManagementKeywords from .keywords.cookie import CookieKeywords @@ -12,9 +11,10 @@ from .keywords.selectelement import SelectElementKeywords from .keywords.tableelement import TableElementKeywords from .keywords.waiting import WaitingKeywords +from .robotlibcore import DynamicCore +from .utils import BrowserCache from .utils import LibraryListener from .version import VERSION -from .robotlibcore import DynamicCore __version__ = VERSION @@ -210,7 +210,7 @@ def run_keyword(self, name, args, kwargs): try: return DynamicCore.run_keyword(self, name, args, kwargs) except Exception: - RunOnFailureKeywords(self)._run_on_failure() + RunOnFailureKeywords(self).run_on_failure() raise def register_browser(self, browser, alias): diff --git a/src/Selenium2Library/keywords/alert.py b/src/Selenium2Library/keywords/alert.py index db9a66749..1727ed93c 100644 --- a/src/Selenium2Library/keywords/alert.py +++ b/src/Selenium2Library/keywords/alert.py @@ -1,8 +1,8 @@ import time from selenium.common.exceptions import WebDriverException -from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.ui import WebDriverWait from Selenium2Library.base import Base from Selenium2Library.robotlibcore import keyword diff --git a/src/Selenium2Library/keywords/element.py b/src/Selenium2Library/keywords/element.py index c5ed1a906..09c519ab5 100644 --- a/src/Selenium2Library/keywords/element.py +++ b/src/Selenium2Library/keywords/element.py @@ -1,11 +1,11 @@ -from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.action_chains import ActionChains +from selenium.webdriver.common.keys import Keys from Selenium2Library import utils from Selenium2Library.base import Base from Selenium2Library.keywords.formelement import FormElementKeywords -from Selenium2Library.locators import ElementFinder from Selenium2Library.locators import CustomLocator +from Selenium2Library.locators import ElementFinder from Selenium2Library.robotlibcore import keyword diff --git a/src/Selenium2Library/keywords/runonfailure.py b/src/Selenium2Library/keywords/runonfailure.py index b6d82838c..28da71839 100644 --- a/src/Selenium2Library/keywords/runonfailure.py +++ b/src/Selenium2Library/keywords/runonfailure.py @@ -40,7 +40,7 @@ def register_keyword_to_run_on_failure(self, keyword): return old_keyword_text - def _run_on_failure(self): + def run_on_failure(self): if not self.ctx._run_on_failure_keyword: return if self.ctx._running_on_failure_routine: @@ -49,11 +49,11 @@ def _run_on_failure(self): try: BuiltIn().run_keyword(self.ctx._run_on_failure_keyword) except Exception as err: - self._run_on_failure_error(err) + self.run_on_failure_error(err) finally: self.ctx._running_on_failure_routine = False - def _run_on_failure_error(self, err): + 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) From 0e0a08e68470e013631a8691ebecf8f5dbaef3db Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Tue, 25 Apr 2017 23:05:40 +0300 Subject: [PATCH 13/16] Changed timer not to depend on the instance --- src/Selenium2Library/base.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Selenium2Library/base.py b/src/Selenium2Library/base.py index 91726e7d8..143b59c09 100644 --- a/src/Selenium2Library/base.py +++ b/src/Selenium2Library/base.py @@ -9,12 +9,13 @@ class Base(object): + _speed_in_secs = 0.0 + _timeout_in_secs = 5.0 + _implicit_wait_in_secs = 5.0 + def __init__(self, ctx): self.ctx = ctx self.element_finder = ElementFinder() - self._speed_in_secs = 0.0 - self._timeout_in_secs = 5.0 - self._implicit_wait_in_secs = 5.0 def info(self, msg, html=False): logger.info(msg, html) From 5a3521efd96614babb23597676f127db1131a475 Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Wed, 26 Apr 2017 22:36:07 +0300 Subject: [PATCH 14/16] Moved timeouts to library __init__ --- src/Selenium2Library/__init__.py | 3 +++ src/Selenium2Library/base.py | 4 ---- .../keywords/browsermanagement.py | 24 +++++++++---------- src/Selenium2Library/keywords/waiting.py | 4 ++-- test/unit/keywords/test_browsermanagement.py | 1 + 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Selenium2Library/__init__.py b/src/Selenium2Library/__init__.py index 7b86519ed..c4fa5b349 100644 --- a/src/Selenium2Library/__init__.py +++ b/src/Selenium2Library/__init__.py @@ -185,6 +185,9 @@ def __init__(self, """ 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 libraries = [ AlertKeywords(self), BrowserManagementKeywords(self), diff --git a/src/Selenium2Library/base.py b/src/Selenium2Library/base.py index 143b59c09..3cacd6b12 100644 --- a/src/Selenium2Library/base.py +++ b/src/Selenium2Library/base.py @@ -9,10 +9,6 @@ class Base(object): - _speed_in_secs = 0.0 - _timeout_in_secs = 5.0 - _implicit_wait_in_secs = 5.0 - def __init__(self, ctx): self.ctx = ctx self.element_finder = ElementFinder() diff --git a/src/Selenium2Library/keywords/browsermanagement.py b/src/Selenium2Library/keywords/browsermanagement.py index dbea371b4..661e14a69 100644 --- a/src/Selenium2Library/keywords/browsermanagement.py +++ b/src/Selenium2Library/keywords/browsermanagement.py @@ -463,21 +463,21 @@ 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._speed_in_secs) + return secs_to_timestr(self.ctx._speed_in_secs) @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._timeout_in_secs) + return secs_to_timestr(self.ctx._timeout_in_secs) @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._implicit_wait_in_secs) + return secs_to_timestr(self.ctx._implicit_wait_in_secs) @keyword def set_selenium_speed(self, seconds): @@ -497,10 +497,10 @@ def set_selenium_speed(self, seconds): Example: | Set Selenium Speed | .5 seconds | """ - old_speed = self._speed_in_secs - self._speed_in_secs = timestr_to_secs(seconds) + old_speed = self.ctx._speed_in_secs + self.ctx._speed_in_secs = timestr_to_secs(seconds) for browser in self.browsers.browsers: - browser._speed = self._speed_in_secs + browser._speed = self.ctx._speed_in_secs self._monkey_patch_speed(browser) return old_speed @@ -523,9 +523,9 @@ def set_selenium_timeout(self, seconds): | Set Selenium Timeout | ${orig timeout} | """ old_timeout = self.get_selenium_timeout() - self._timeout_in_secs = timestr_to_secs(seconds) + self.ctx._timeout_in_secs = timestr_to_secs(seconds) for browser in self.browsers.get_open_browsers(): - browser.set_script_timeout(self._timeout_in_secs) + browser.set_script_timeout(self.ctx._timeout_in_secs) return old_timeout @keyword @@ -543,9 +543,9 @@ def set_selenium_implicit_wait(self, seconds): | Set Selenium Implicit Wait | ${orig wait} | """ old_wait = self.get_selenium_implicit_wait() - self._implicit_wait_in_secs = timestr_to_secs(seconds) + self.ctx._implicit_wait_in_secs = timestr_to_secs(seconds) for browser in self.browsers.get_open_browsers(): - browser.implicitly_wait(self._implicit_wait_in_secs) + browser.implicitly_wait(self.ctx._implicit_wait_in_secs) return old_wait @keyword @@ -576,8 +576,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._timeout_in_secs) - browser.implicitly_wait(self._implicit_wait_in_secs) + browser.set_script_timeout(self.ctx._timeout_in_secs) + browser.implicitly_wait(self.ctx._implicit_wait_in_secs) return browser diff --git a/src/Selenium2Library/keywords/waiting.py b/src/Selenium2Library/keywords/waiting.py index 8eba38800..e48f7535b 100644 --- a/src/Selenium2Library/keywords/waiting.py +++ b/src/Selenium2Library/keywords/waiting.py @@ -248,7 +248,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 timeout is not None else self._timeout_in_secs + timeout = timestr_to_secs(timeout) if timeout else self.ctx._timeout_in_secs maxtime = time.time() + timeout while True: timeout_error = wait_func(*args) @@ -258,5 +258,5 @@ 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 timeout is not None else self._timeout_in_secs + timeout = timestr_to_secs(timeout) if timeout else self.ctx._timeout_in_secs return secs_to_timestr(timeout) diff --git a/test/unit/keywords/test_browsermanagement.py b/test/unit/keywords/test_browsermanagement.py index 462e75791..50712f3b1 100644 --- a/test/unit/keywords/test_browsermanagement.py +++ b/test/unit/keywords/test_browsermanagement.py @@ -96,6 +96,7 @@ def test_capabilities_attribute_not_modified(self): def test_set_selenium_timeout_only_affects_open_browsers(self): ctx = mock() + ctx._timeout_in_secs = 5.0 _browsers = mock() ctx._browsers = _browsers first_browser, second_browser = mock(), mock() From 3ec0ccec97002dca429c076c601252eb8a658d48 Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Wed, 26 Apr 2017 23:20:15 +0300 Subject: [PATCH 15/16] Fixes too verbose coding style --- src/Selenium2Library/keywords/alert.py | 2 +- .../keywords/browsermanagement.py | 25 ++----- src/Selenium2Library/keywords/element.py | 75 ++++++++----------- src/Selenium2Library/keywords/formelement.py | 44 ++++------- src/Selenium2Library/keywords/runonfailure.py | 3 +- .../keywords/selectelement.py | 11 +-- 6 files changed, 64 insertions(+), 96 deletions(-) diff --git a/src/Selenium2Library/keywords/alert.py b/src/Selenium2Library/keywords/alert.py index 1727ed93c..cbff4905e 100644 --- a/src/Selenium2Library/keywords/alert.py +++ b/src/Selenium2Library/keywords/alert.py @@ -123,7 +123,7 @@ def _handle_alert(self, dismiss_type=None): alert.text alert.dismiss - This function creates a re-try funtionality to better support + This function creates a re-try functionality to better support alerts in Chrome. """ retry = 0 diff --git a/src/Selenium2Library/keywords/browsermanagement.py b/src/Selenium2Library/keywords/browsermanagement.py index 661e14a69..d64361baf 100644 --- a/src/Selenium2Library/keywords/browsermanagement.py +++ b/src/Selenium2Library/keywords/browsermanagement.py @@ -57,11 +57,8 @@ def close_all_browsers(self): def close_browser(self): """Closes the current browser.""" if self.browsers.current: - self.debug( - 'Closing browser with session id {}'.format( - self.browsers.current.session_id - ) - ) + self.debug('Closing browser with session ' + 'id {}'.format(self.browsers.current.session_id)) self.browsers.close() @keyword @@ -217,10 +214,8 @@ def switch_browser(self, index_or_alias): """ try: self.browsers.switch(index_or_alias) - self.debug( - 'Switched to browser with Selenium session id %s' - % self.browser.session_id - ) + self.debug('Switched to browser with Selenium session id %s' + % self.browser.session_id) except (RuntimeError, DataError): # RF 2.6 uses RE, earlier DE raise RuntimeError("No browser with index or alias '%s' found." % index_or_alias) @@ -233,9 +228,7 @@ def close_window(self): @keyword def get_window_identifiers(self): """Returns and logs id attributes of all windows known to the browser.""" - return self._log_list( - self._window_manager.get_window_ids(self.browser) - ) + return self._log_list(self._window_manager.get_window_ids(self.browser)) @keyword def get_window_names(self): @@ -377,8 +370,7 @@ def get_locations(self): """Returns and logs current locations of all windows known to the browser.""" return self._log_list( [window_info[4] for window_info in - self._window_manager._get_window_infos(self.browser)] - ) + self._window_manager._get_window_infos(self.browser)]) @keyword def get_source(self): @@ -439,7 +431,7 @@ def title_should_be(self, title): actual = self.get_title() if actual != title: raise AssertionError("Title should have been '%s' but was '%s'" - % (title, actual)) + % (title, actual)) self.info("Page title is '%s'." % title) @keyword @@ -690,8 +682,7 @@ def execute(self, driver_command, params=None): def _log_list(self, items, what='item'): msg = [ 'Altogether {} {}.'.format( - len(items), what if len(items) == 1 else '{}s'.format(what) - ) + len(items), what if len(items) == 1 else '{}s'.format(what)) ] for index, item in enumerate(items): msg.append('{}: {}'.format(index + 1, item)) diff --git a/src/Selenium2Library/keywords/element.py b/src/Selenium2Library/keywords/element.py index 09c519ab5..76f30ee72 100644 --- a/src/Selenium2Library/keywords/element.py +++ b/src/Selenium2Library/keywords/element.py @@ -68,9 +68,8 @@ def element_should_contain(self, locator, expected, message=''): Key attributes for arbitrary elements are `id` and `name`. See `introduction` for details about locating elements. """ - self.info( - "Verifying element '%s' contains text '%s'." % (locator, expected) - ) + self.info("Verifying element '%s' contains " + "text '%s'." % (locator, expected)) actual = self._get_text(locator) if expected not in actual: if not message: @@ -87,9 +86,8 @@ def element_should_not_contain(self, locator, expected, message=''): Key attributes for arbitrary elements are `id` and `name`. See `Element Should Contain` for more details. """ - self.info( - "Verifying element '%s' does not contain text '%s'." - % (locator, expected)) + self.info("Verifying element '%s' does not contain text '%s'." + % (locator, expected)) actual = self._get_text(locator) if expected in actual: if not message: @@ -162,7 +160,7 @@ def locator_should_match_x_times(self, locator, expected_locator_count, message= self.ctx.log_source(loglevel) raise AssertionError(message) self.info("Current page contains %s elements matching '%s'." - % (actual_locator_count, locator)) + % (actual_locator_count, locator)) @keyword def page_should_not_contain(self, text, loglevel='INFO'): @@ -186,9 +184,8 @@ def page_should_not_contain_element(self, locator, message='', loglevel='INFO'): Key attributes for arbitrary elements are `id` and `name`. See `introduction` for details about locating elements. """ - self.page_not_contains_element( - locator, message=message, loglevel=loglevel - ) + self.page_not_contains_element(locator, message=message, + loglevel=loglevel) @keyword def assign_id_to_element(self, locator, id): @@ -201,7 +198,8 @@ def assign_id_to_element(self, locator, id): | Assign ID to Element | xpath=//div[@id="first_div"] | my id | | Page Should Contain Element | my id | """ - self.info("Assigning temporary id '%s' to element '%s'" % (id, locator)) + self.info("Assigning temporary id '%s' " + "to element '%s'" % (id, locator)) element = self.element_find(locator) self.browser.execute_script("arguments[0].id = '%s';" % id, element) @@ -243,8 +241,8 @@ def element_should_be_visible(self, locator, message=''): visible = self.is_visible(locator) if not visible: if not message: - message = "The element '%s' should be visible, but it "\ - "is not." % locator + message = ("The element '%s' should be visible, but it " + "is not." % locator) raise AssertionError(message) @keyword @@ -262,8 +260,8 @@ def element_should_not_be_visible(self, locator, message=''): visible = self.is_visible(locator) if visible: if not message: - message = "The element '%s' should not be visible, "\ - "but it is." % locator + message = ("The element '%s' should not be visible, " + "but it is." % locator) raise AssertionError(message) @keyword @@ -279,13 +277,14 @@ def element_text_should_be(self, locator, expected, message=''): `introduction` for details about locating elements. """ self.info("Verifying element '%s' contains exactly text '%s'." - % (locator, expected)) + % (locator, expected)) element = self.element_find(locator) actual = element.text if expected != actual: if not message: - message = "The text of element '%s' should have been '%s' but "\ - "in fact it was '%s'." % (locator, expected, actual) + message = ("The text of element '%s' should have been '%s' " + "but in fact it was '%s'." + % (locator, expected, actual)) raise AssertionError(message) @keyword @@ -399,7 +398,8 @@ def click_element_at_coordinates(self, locator, xoffset, yoffset): Key attributes for arbitrary elements are `id` and `name`. See `introduction` for details about locating elements. """ - self.info("Click clicking element '%s' in coordinates '%s', '%s'." % (locator, xoffset, yoffset)) + self.info("Click clicking element '%s' in coordinates " + "'%s', '%s'." % (locator, xoffset, yoffset)) element = self.element_find(locator) action = ActionChains(self.browser) action.move_to_element(element) @@ -612,9 +612,8 @@ def page_should_contain_link(self, locator, message='', loglevel='INFO'): Key attributes for links are `id`, `name`, `href` and link text. See `introduction` for details about locating elements. """ - self.page_contains_element( - locator, tag='link', message=message, loglevel=loglevel - ) + self.page_contains_element(locator, tag='link', message=message, + loglevel=loglevel) @keyword def page_should_not_contain_link(self, locator, message='', loglevel='INFO'): @@ -626,9 +625,7 @@ def page_should_not_contain_link(self, locator, message='', loglevel='INFO'): Key attributes for images are `id`, `src` and `alt`. See `introduction` for details about locating elements. """ - self.page_not_contains_element( - locator, 'link', message, loglevel - ) + self.page_not_contains_element(locator, 'link', message, loglevel) @keyword def click_image(self, locator): @@ -638,9 +635,7 @@ def click_image(self, locator): `introduction` for details about locating elements. """ self.info("Clicking image '%s'." % locator) - element = self.element_find( - locator, required=False, tag='image' - ) + element = self.element_find(locator, required=False, tag='image') if element is None: # A form may have an image as it's submit trigger. element = self.element_find(locator, tag='input') @@ -666,9 +661,8 @@ def page_should_contain_image(self, locator, message='', loglevel='INFO'): Key attributes for images are `id`, `src` and `alt`. See `introduction` for details about locating elements. """ - self.page_contains_element( - locator, tag='image', message=message, loglevel=loglevel - ) + self.page_contains_element(locator, tag='image', message=message, + loglevel=loglevel) @keyword def page_should_not_contain_image(self, locator, message='', loglevel='INFO'): @@ -680,9 +674,7 @@ def page_should_not_contain_image(self, locator, message='', loglevel='INFO'): Key attributes for images are `id`, `src` and `alt`. See `introduction` for details about locating elements. """ - self.page_not_contains_element( - locator, 'image', message, loglevel - ) + self.page_not_contains_element(locator, 'image', message, loglevel) @keyword def get_matching_xpath_count(self, xpath, return_str=True): @@ -701,9 +693,8 @@ def get_matching_xpath_count(self, xpath, return_str=True): If you wish to assert the number of matching elements, use `Xpath Should Match X Times`. """ - count = len(self.element_find( - "xpath=" + xpath, first_only=False, required=False) - ) + count = len(self.element_find("xpath=" + xpath, first_only=False, + required=False)) return str(count) if return_str else count @keyword @@ -721,16 +712,16 @@ def xpath_should_match_x_times(self, xpath, expected_xpath_count, message='', lo `loglevel` arguments. """ actual_xpath_count = len(self.element_find( - "xpath=" + xpath, first_only=False, required=False) - ) + "xpath=" + xpath, first_only=False, required=False)) if int(actual_xpath_count) != int(expected_xpath_count): if not message: - message = "Xpath %s should have matched %s times but matched %s times"\ - %(xpath, expected_xpath_count, actual_xpath_count) + message = ("Xpath %s should have matched %s times but " + "matched %s times" + % (xpath, expected_xpath_count, actual_xpath_count)) self.ctx.log_source(loglevel) raise AssertionError(message) self.info("Current page contains %s elements matching '%s'." - % (actual_xpath_count, xpath)) + % (actual_xpath_count, xpath)) @keyword def add_location_strategy(self, strategy_name, strategy_keyword, persist=False): diff --git a/src/Selenium2Library/keywords/formelement.py b/src/Selenium2Library/keywords/formelement.py index 0bd987d4f..b1bf07d38 100644 --- a/src/Selenium2Library/keywords/formelement.py +++ b/src/Selenium2Library/keywords/formelement.py @@ -44,7 +44,7 @@ def checkbox_should_not_be_selected(self, locator): element = self._get_checkbox(locator) if element.is_selected(): raise AssertionError("Checkbox '%s' should not have been selected" - % locator) + % locator) @keyword def page_should_contain_checkbox(self, locator, message='', loglevel='INFO'): @@ -70,9 +70,7 @@ def page_should_not_contain_checkbox(self, locator, message='', loglevel='INFO') Key attributes for checkboxes are `id` and `name`. See `introduction` for details about locating elements. """ - self.page_not_contains_element( - locator, 'checkbox', message, loglevel - ) + self.page_not_contains_element(locator, 'checkbox', message, loglevel) @keyword def select_checkbox(self, locator): @@ -110,9 +108,7 @@ def page_should_contain_radio_button(self, locator, message='', loglevel='INFO') Key attributes for radio buttons are `id`, `name` and `value`. See `introduction` for details about locating elements. """ - self.page_contains_element( - locator, 'radio button', message, loglevel - ) + self.page_contains_element(locator, 'radio button', message, loglevel) @keyword def page_should_not_contain_radio_button(self, locator, message='', loglevel='INFO'): @@ -124,9 +120,8 @@ def page_should_not_contain_radio_button(self, locator, message='', loglevel='IN Key attributes for radio buttons are `id`, `name` and `value`. See `introduction` for details about locating elements. """ - self.page_not_contains_element( - locator, 'radio button', message, loglevel - ) + self.page_not_contains_element(locator, 'radio button', message, + loglevel) @keyword def radio_button_should_be_set_to(self, group_name, value): @@ -135,14 +130,14 @@ def radio_button_should_be_set_to(self, group_name, value): See `Select Radio Button` for information about how radio buttons are located. """ - self.info("Verifying radio button '%s' has selection '%s'." \ - % (group_name, value)) + self.info("Verifying radio button '%s' has selection '%s'." + % (group_name, value)) elements = self._get_radio_buttons(group_name) actual_value = self._get_value_from_radio_buttons(elements) if actual_value is None or actual_value != value: raise AssertionError("Selection of radio button '%s' should have " "been '%s' but was '%s'" - % (group_name, value, actual_value)) + % (group_name, value, actual_value)) @keyword def radio_button_should_not_be_selected(self, group_name): @@ -157,7 +152,7 @@ def radio_button_should_not_be_selected(self, group_name): if actual_value is not None: raise AssertionError("Radio button group '%s' should not have had " "selection, but '%s' was selected" - % (group_name, actual_value)) + % (group_name, actual_value)) @keyword def select_radio_button(self, group_name, value): @@ -174,7 +169,8 @@ def select_radio_button(self, group_name, value): | Select Radio Button | size | XL | # Matches HTML like XL | | Select Radio Button | size | sizeXL | # Matches HTML like XL | """ - self.info("Selecting '%s' from radio button '%s'." % (value, group_name)) + self.info("Selecting '%s' from radio button '%s'." + % (value, group_name)) element = self._get_radio_button_with_value(group_name, value) if not element.is_selected(): element.click() @@ -192,7 +188,7 @@ def choose_file(self, locator, file_path): """ if not os.path.isfile(file_path): raise AssertionError("File '%s' does not exist on the local file system" - % file_path) + % file_path) self.element_find(locator).send_keys(file_path) @keyword @@ -225,9 +221,7 @@ def page_should_contain_textfield(self, locator, message='', loglevel='INFO'): Key attributes for text fields are `id` and `name`. See `introduction` for details about locating elements. """ - self.page_contains_element( - locator, 'text field', message, loglevel - ) + self.page_contains_element(locator, 'text field', message, loglevel) @keyword def page_should_not_contain_textfield(self, locator, message='', loglevel='INFO'): @@ -239,9 +233,7 @@ def page_should_not_contain_textfield(self, locator, message='', loglevel='INFO' Key attributes for text fields are `id` and `name`. See `introduction` for details about locating elements. """ - self.page_not_contains_element( - locator, 'text field', message, loglevel - ) + self.page_not_contains_element(locator, 'text field', message, loglevel) @keyword def textfield_should_contain(self, locator, expected, message=''): @@ -372,12 +364,8 @@ def page_should_not_contain_button(self, locator, message='', loglevel='INFO'): Key attributes for buttons are `id`, `name` and `value`. See `introduction` for details about locating elements. """ - self.page_not_contains_element( - locator, 'button', message, loglevel - ) - self.page_not_contains_element( - locator, 'input', message, loglevel - ) + self.page_not_contains_element(locator, 'button', message, loglevel) + self.page_not_contains_element(locator, 'input', message, loglevel) def _get_checkbox(self, locator): return self.element_find(locator, tag='input') diff --git a/src/Selenium2Library/keywords/runonfailure.py b/src/Selenium2Library/keywords/runonfailure.py index 28da71839..e16d2fb01 100644 --- a/src/Selenium2Library/keywords/runonfailure.py +++ b/src/Selenium2Library/keywords/runonfailure.py @@ -54,7 +54,8 @@ def run_on_failure(self): self.ctx._running_on_failure_routine = False def run_on_failure_error(self, err): - err = "Keyword '%s' could not be run on failure: %s" % (self.ctx._run_on_failure_keyword, 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 diff --git a/src/Selenium2Library/keywords/selectelement.py b/src/Selenium2Library/keywords/selectelement.py index 4bf0f9b16..d46290e7d 100644 --- a/src/Selenium2Library/keywords/selectelement.py +++ b/src/Selenium2Library/keywords/selectelement.py @@ -135,9 +135,7 @@ def page_should_contain_list(self, locator, message='', loglevel='INFO'): Key attributes for lists are `id` and `name`. See `introduction` for details about locating elements. """ - self.page_contains_element( - locator, 'list', message, loglevel - ) + self.page_contains_element(locator, 'list', message, loglevel) @keyword def page_should_not_contain_list(self, locator, message='', loglevel='INFO'): @@ -149,9 +147,7 @@ def page_should_not_contain_list(self, locator, message='', loglevel='INFO'): Key attributes for lists are `id` and `name`. See `introduction` for details about locating elements. """ - self.page_not_contains_element( - locator, 'list', message, loglevel - ) + self.page_not_contains_element(locator, 'list', message, loglevel) @keyword def select_all_from_list(self, locator): @@ -164,7 +160,8 @@ def select_all_from_list(self, locator): select = self._get_select_list(locator) if not select.is_multiple: - raise RuntimeError("Keyword 'Select all from list' works only for multiselect lists.") + raise RuntimeError("Keyword 'Select all from list' works only for " + "multiselect lists.") for i in range(len(select.options)): select.select_by_index(i) From 55c61379394efc950aac24d6f7aa104600d6e378 Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Wed, 26 Apr 2017 23:59:56 +0300 Subject: [PATCH 16/16] More code formatting to less verbose format --- .../keywords/browsermanagement.py | 4 +--- src/Selenium2Library/keywords/element.py | 14 ++++------- src/Selenium2Library/keywords/formelement.py | 23 ++++++------------- src/Selenium2Library/keywords/javascript.py | 6 ++--- .../keywords/selectelement.py | 4 +--- 5 files changed, 16 insertions(+), 35 deletions(-) diff --git a/src/Selenium2Library/keywords/browsermanagement.py b/src/Selenium2Library/keywords/browsermanagement.py index d64361baf..90befaa95 100644 --- a/src/Selenium2Library/keywords/browsermanagement.py +++ b/src/Selenium2Library/keywords/browsermanagement.py @@ -244,9 +244,7 @@ def get_window_names(self): @keyword def get_window_titles(self): """Returns and logs titles of all windows known to the browser.""" - return self._log_list( - self._window_manager.get_window_titles(self.browser) - ) + return self._log_list(self._window_manager.get_window_titles(self.browser)) @keyword def maximize_browser_window(self): diff --git a/src/Selenium2Library/keywords/element.py b/src/Selenium2Library/keywords/element.py index 76f30ee72..5279c3b8b 100644 --- a/src/Selenium2Library/keywords/element.py +++ b/src/Selenium2Library/keywords/element.py @@ -137,9 +137,7 @@ def page_should_contain_element(self, locator, message='', loglevel='INFO'): Key attributes for arbitrary elements are `id` and `name`. See `introduction` for details about locating elements. """ - self.page_contains_element( - locator, message=message, loglevel=loglevel - ) + self.page_contains_element(locator, message=message, loglevel=loglevel) @keyword def locator_should_match_x_times(self, locator, expected_locator_count, message='', loglevel='INFO'): @@ -584,9 +582,8 @@ def get_all_links(self): If a link has no id, an empty string will be in the list instead. """ links = [] - elements = self.element_find( - "tag=a", first_only=False, required=False, tag='a' - ) + elements = self.element_find("tag=a", first_only=False, + required=False, tag='a') for anchor in elements: links.append(anchor.get_attribute('id')) return links @@ -844,9 +841,8 @@ def _page_contains(self, text): if self.is_text_present(text): return True - subframes = self.element_find( - "xpath=//frame|//iframe", first_only=False, required=False - ) + subframes = self.element_find("xpath=//frame|//iframe", + first_only=False, required=False) self.debug('Current frame has %d subframes' % len(subframes)) for frame in subframes: self.browser.switch_to_frame(frame) diff --git a/src/Selenium2Library/keywords/formelement.py b/src/Selenium2Library/keywords/formelement.py index b1bf07d38..b52d7787b 100644 --- a/src/Selenium2Library/keywords/formelement.py +++ b/src/Selenium2Library/keywords/formelement.py @@ -56,9 +56,7 @@ def page_should_contain_checkbox(self, locator, message='', loglevel='INFO'): Key attributes for checkboxes are `id` and `name`. See `introduction` for details about locating elements. """ - self.page_contains_element( - locator, 'checkbox', message, loglevel - ) + self.page_contains_element(locator, 'checkbox', message, loglevel) @keyword def page_should_not_contain_checkbox(self, locator, message='', loglevel='INFO'): @@ -265,10 +263,9 @@ def textfield_value_should_be(self, locator, expected, message=''): locator, required=False, tag='text field' ) if not element: - element = self.element_find( - locator, required=False, tag='file upload' - ) - actual = element.get_attribute('value') if element is not None else None + element = self.element_find(locator, required=False, + tag='file upload') + actual = element.get_attribute('value') if element else None if actual != expected: if not message: message = "Value of text field '%s' should have been '%s' "\ @@ -324,9 +321,7 @@ def click_button(self, locator): `introduction` for details about locating elements. """ self.info("Clicking button '%s'." % locator) - element = self.element_find( - locator, required=False, tag='input' - ) + element = self.element_find(locator, required=False, tag='input') if element is None: element = self.element_find(locator, tag='button') element.click() @@ -344,13 +339,9 @@ def page_should_contain_button(self, locator, message='', loglevel='INFO'): `introduction` for details about locating elements. """ try: - self.page_contains_element( - locator, 'input', message, loglevel - ) + self.page_contains_element(locator, 'input', message, loglevel) except AssertionError: - self.page_contains_element( - locator, 'button', message, loglevel - ) + self.page_contains_element(locator, 'button', message, loglevel) @keyword def page_should_not_contain_button(self, locator, message='', loglevel='INFO'): diff --git a/src/Selenium2Library/keywords/javascript.py b/src/Selenium2Library/keywords/javascript.py index 2aeff1af3..9089cb23c 100644 --- a/src/Selenium2Library/keywords/javascript.py +++ b/src/Selenium2Library/keywords/javascript.py @@ -69,10 +69,8 @@ def _get_javascript_to_execute(self, code): return code self.info( 'Reading JavaScript from file %s.'.format( - codepath.replace(os.sep, '/'), codepath - ), - html=True - ) + codepath.replace(os.sep, '/'), codepath), + html=True) codefile = open(codepath) try: return codefile.read().strip() diff --git a/src/Selenium2Library/keywords/selectelement.py b/src/Selenium2Library/keywords/selectelement.py index d46290e7d..9063f99df 100644 --- a/src/Selenium2Library/keywords/selectelement.py +++ b/src/Selenium2Library/keywords/selectelement.py @@ -405,9 +405,7 @@ def _is_multiselect_list(self, select): return False def _unselect_all_options_from_multi_select_list(self, select): - self.browser.execute_script( - "arguments[0].selectedIndex = -1;", select - ) + self.browser.execute_script("arguments[0].selectedIndex = -1;", select) def _unselect_option_from_multi_select_list(self, select, options, index): if options[index].is_selected():