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/src/Selenium2Library/__init__.py b/src/Selenium2Library/__init__.py index c237449e9..c4fa5b349 100644 --- a/src/Selenium2Library/__init__.py +++ b/src/Selenium2Library/__init__.py @@ -1,25 +1,25 @@ -from .keywords import * +import warnings + +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 .robotlibcore import DynamicCore +from .utils import BrowserCache from .utils import LibraryListener from .version import VERSION - __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,57 @@ 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._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), + RunOnFailureKeywords(self), + ElementKeywords(self), + TableElementKeywords(self), + FormElementKeywords(self), + SelectElementKeywords(self), + JavaScriptKeywords(self), + CookieKeywords(self), + ScreenshotKeywords(self), + WaitingKeywords(self) + ] + self._browsers = BrowserCache() + DynamicCore.__init__(self, libraries) self.screenshot_root_directory = screenshot_root_directory self.set_selenium_timeout(timeout) self.set_selenium_implicit_wait(implicit_wait) self.register_keyword_to_run_on_failure(run_on_failure) self.ROBOT_LIBRARY_LISTENER = LibraryListener() + + def run_keyword(self, name, args, kwargs): + try: + return DynamicCore.run_keyword(self, name, args, kwargs) + except Exception: + RunOnFailureKeywords(self).run_on_failure() + raise + + def register_browser(self, browser, alias): + return self._browsers.register(browser, alias) + + @property + def _browser(self): + """Current active browser""" + if not self._browsers.current: + raise RuntimeError('No browser is open') + return self._browsers.current + + @property + def _cache(self): + warnings.warn('"Selenium2Library._cache" is deprecated, ' + 'use public API instead.', DeprecationWarning) + return self._browsers + + def _current_browser(self): + warnings.warn('"Selenium2Library._current_browser" is deprecated, ' + 'use "Selenium2Library.browser" instead.', + DeprecationWarning) + return self.browser diff --git a/src/Selenium2Library/base.py b/src/Selenium2Library/base.py new file mode 100644 index 000000000..3cacd6b12 --- /dev/null +++ b/src/Selenium2Library/base.py @@ -0,0 +1,97 @@ +from robot.api import logger +from selenium.webdriver.remote.webelement import WebElement + +from .locators.elementfinder import ElementFinder + + +LOG_LEVELS = ['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'] + + +class Base(object): + + def __init__(self, ctx): + self.ctx = ctx + self.element_finder = ElementFinder() + + 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): + 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 + + # 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) + 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 + + # 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' + 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) + ) + + # 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' + 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/__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..cbff4905e 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 selenium.webdriver.support.ui import WebDriverWait -from .keywordgroup import KeywordGroup - +from Selenium2Library.base import Base +from Selenium2Library.robotlibcore import keyword -class AlertKeywords(KeywordGroup): - __ACCEPT_ALERT = 'accept' - __DISMISS_ALERT = 'dismiss' +class AlertKeywords(Base): - def __init__(self): - self._next_alert_dismiss_type = self.__ACCEPT_ALERT + ACCEPT_ALERT = 'accept' + DISMISS_ALERT = 'dismiss' - # Public + def __init__(self, ctx): + Base.__init__(self, ctx) + self._next_alert_dismiss_type = self.ACCEPT_ALERT + @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. @@ -34,16 +37,18 @@ 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'" % (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 + 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 @@ -59,8 +64,9 @@ 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): """Dismisses currently shown confirmation dialog and returns it's message. @@ -79,9 +85,10 @@ 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 def get_alert_message(self, dismiss=True): """Returns the text of current JavaScript alert. @@ -91,10 +98,11 @@ 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() + @keyword def dismiss_alert(self, accept=True): """ Returns true if alert was confirmed, false if it was dismissed @@ -103,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() @@ -115,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 @@ -130,12 +138,11 @@ 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( - 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 edf969248..90befaa95 100644 --- a/src/Selenium2Library/keywords/browsermanagement.py +++ b/src/Selenium2Library/keywords/browsermanagement.py @@ -6,46 +6,41 @@ 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, 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 +50,21 @@ 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.browsers.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.browsers.current: + self.debug('Closing browser with session ' + 'id {}'.format(self.browsers.current.session_id)) + self.browsers.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 +114,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 +181,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,27 +213,27 @@ 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._cache.current.session_id) + self.browsers.switch(index_or_alias) + 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) - # Public, window management - + @keyword def close_window(self): """Closes currently opened pop-up window.""" - self._current_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._current_browser())) + return self._log_list(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._current_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': @@ -241,23 +241,27 @@ 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.browser)) + @keyword def maximize_browser_window(self): """Maximizes current browser window.""" - self._current_browser().maximize_window() + self.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.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 +271,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.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.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 +293,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.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) - element = self._element_find(locator, True, True) - self._current_browser().switch_to_frame(element) + self.info("Selecting frame '%s'." % locator) + element = self.element_find(locator) + self.browser.switch_to_frame(element) + @keyword def select_window(self, locator=None): """Selects the window matching locator and return previous window handle. @@ -334,122 +342,134 @@ def select_window(self, locator=None): | Select Window | | | # Chooses the main window again | """ try: - return self._current_browser().current_window_handle + return self.browser.current_window_handle except NoSuchWindowException: pass finally: - self._window_manager.select(self._current_browser(), locator) + self._window_manager.select(self.browser, locator) + @keyword def list_windows(self): """Return all current window handles as a list""" - return self._current_browser().window_handles + return self.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.browser.switch_to_default_content() + @keyword def get_location(self): """Returns the current location.""" - return self._current_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._current_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._current_browser().page_source + return self.browser.page_source + @keyword def get_title(self): """Returns title of current page.""" - return self._current_browser().title + return self.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 + % (title, actual)) + 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.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.browser.get(url) + @keyword def reload_page(self): """Simulates user reloading page.""" - self._current_browser().refresh() - - # Public, execution properties + self.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) + 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): """Sets the delay in seconds that is waited after each Selenium command. @@ -467,13 +487,14 @@ 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) - for browser in self._cache.browsers: - browser._speed = self._speed_in_secs + 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.ctx._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. @@ -492,11 +513,12 @@ 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) - for browser in self._cache.get_open_browsers(): - browser.set_script_timeout(self._timeout_in_secs) + self.ctx._timeout_in_secs = timestr_to_secs(seconds) + for browser in self.browsers.get_open_browsers(): + browser.set_script_timeout(self.ctx._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. @@ -511,12 +533,12 @@ 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) - for browser in self._cache.get_open_browsers(): - browser.implicitly_wait(self._implicit_wait_in_secs) + self.ctx._implicit_wait_in_secs = timestr_to_secs(seconds) + for browser in self.browsers.get_open_browsers(): + browser.implicitly_wait(self.ctx._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 +552,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.browser.implicitly_wait(implicit_wait_in_secs) def _get_browser_creation_function(self, browser_name): func_name = BROWSER_NAMES.get(browser_name.lower().replace(' ', '')) @@ -551,13 +566,12 @@ 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 - - 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 +582,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 +634,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 +676,13 @@ 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..c56ee9427 100644 --- a/src/Selenium2Library/keywords/cookie.py +++ b/src/Selenium2Library/keywords/cookie.py @@ -1,46 +1,56 @@ -from .keywordgroup import KeywordGroup +from Selenium2Library.base import Base +from Selenium2Library.robotlibcore import keyword -class CookieKeywords(KeywordGroup): +class CookieKeywords(Base): + @keyword def delete_all_cookies(self): """Deletes all cookies.""" - self._current_browser().delete_all_cookies() + self.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.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.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.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.browser.add_cookie(new_cookie) diff --git a/src/Selenium2Library/keywords/element.py b/src/Selenium2Library/keywords/element.py index 3c6bf57f3..5279c3b8b 100644 --- a/src/Selenium2Library/keywords/element.py +++ b/src/Selenium2Library/keywords/element.py @@ -1,60 +1,62 @@ -from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.action_chains import ActionChains -from selenium.webdriver.remote.webelement import WebElement +from selenium.webdriver.common.keys import Keys from Selenium2Library import utils -from Selenium2Library.locators import ElementFinder +from Selenium2Library.base import Base +from Selenium2Library.keywords.formelement import FormElementKeywords from Selenium2Library.locators import CustomLocator +from Selenium2Library.locators import ElementFinder +from Selenium2Library.robotlibcore import keyword -from .keywordgroup import KeywordGroup - - -class ElementKeywords(KeywordGroup): - def __init__(self): - self._element_finder = ElementFinder() +class ElementKeywords(Base): - # Public, get element(s) + def __init__(self, ctx): + Base.__init__(self, ctx) + self.element_finder = ElementFinder() + self.form_element = FormElementKeywords(ctx) + @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) + @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, first_only=False) + @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) + if not self.is_text_present(text): + 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) + if self.is_text_present(text): + 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,16 @@ 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,8 +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: @@ -92,6 +95,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 +105,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 +121,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. @@ -131,8 +137,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'): """Verifies that the page contains the given number of elements located by the given `locator`. @@ -141,26 +148,30 @@ 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"\ %(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)) + 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. @@ -171,10 +182,10 @@ 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): """Assigns a temporary identifier to element specified by `locator`. @@ -185,10 +196,12 @@ 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) + self.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,14 +235,15 @@ 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) - visible = self._is_visible(locator) + self.info("Verifying element '%s' is visible." % locator) + 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 def element_should_not_be_visible(self, locator, message=''): """Verifies that the element identified by `locator` is NOT visible. @@ -238,14 +254,15 @@ 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) - visible = self._is_visible(locator) + self.info("Verifying element '%s' is not visible." % locator) + 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 def element_text_should_be(self, locator, expected, message=''): """Verifies element identified by `locator` exactly contains text `expected`. @@ -257,16 +274,18 @@ 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'." - % (locator, expected)) - element = self._element_find(locator, True, True) + self.info("Verifying element '%s' contains exactly text '%s'." + % (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 def get_element_attribute(self, locator, attribute_name=None): """Returns value of the element attribute. @@ -290,11 +309,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, required=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 +323,22 @@ 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'] + @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) 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) element.clear() + @keyword def get_vertical_position(self, locator): """Returns vertical position of element identified by `locator`. @@ -348,22 +372,22 @@ 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`. 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).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 +396,34 @@ 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) + action = ActionChains(self.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) + 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, True, True) - self._current_browser().execute_script("arguments[0].focus();", element) + element = self.element_find(locator) + self.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 +436,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) + trg_elem = self.element_find(target) + action = ActionChains(self.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 +451,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) + action = ActionChains(self.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 +468,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, required=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.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, required=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.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, required=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.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, required=False) if element is None: raise AssertionError("ERROR: Element %s not found." % (locator)) - ActionChains(self._current_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, True, True) - ActionChains(self._current_browser()).context_click(element).perform() + element = self.element_find(locator) + action = ActionChains(self.browser) + action.context_click(element).perform() + @keyword def simulate(self, locator, event): """Simulates `event` on element identified by `locator`. @@ -494,7 +536,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]; @@ -505,8 +547,9 @@ def simulate(self, locator, event): evt.initEvent(eventName, true, true); return !element.dispatchEvent(evt); """ - self._current_browser().execute_script(script, element, event) + self.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 +561,45 @@ 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) - # 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'): + 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 + @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, tag='link') + action = ActionChains(self.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. @@ -562,8 +609,10 @@ 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'): """Verifies image identified by `locator` is not found from current page. @@ -573,32 +622,34 @@ 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_not_contains_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, 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 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, tag='image') + action = ActionChains(self.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 @@ -607,8 +658,10 @@ 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'): """Verifies image identified by `locator` is found from current page. @@ -618,10 +671,9 @@ 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_not_contains_element(locator, 'image', message, loglevel) + @keyword def get_matching_xpath_count(self, xpath, return_str=True): """Returns number of elements matching `xpath` @@ -638,9 +690,11 @@ 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 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 +708,19 @@ 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"\ - %(xpath, expected_xpath_count, actual_xpath_count) - self.log_source(loglevel) + 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)) + 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,55 +742,40 @@ 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 - - def _element_find(self, locator, first_only, required, tag=None): - browser = self._current_browser() - if isinstance(locator, basestring): - 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: - if len(elements) == 0: 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 + self.element_finder.unregister(strategy_name) def _frame_contains(self, locator, text): - browser = self._current_browser() - 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) - browser.switch_to_default_content() + element = self.element_find(locator) + self.browser.switch_to_frame(element) + self.info("Searching for text from frame '%s'." % locator) + found = self.is_text_present(text) + self.browser.switch_to_default_content() 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) - if not self._is_form_element(element): + element = self.element_find(locator) + 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 @@ -743,12 +784,12 @@ 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.element_find(locator, required=False) - def _is_visible(self, locator): - element = self._element_find(locator, True, False) + def is_visible(self, locator): + element = self.element_find(locator, required=False) if element is not None: return element.is_displayed() return None @@ -783,7 +824,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.debug(message) raise ValueError(message) def _parse_attribute_locator(self, attribute_locator): @@ -794,43 +835,19 @@ 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 self._element_find(locator, True, False, tag=tag) is not None - def _page_contains(self, text): - browser = self._current_browser() - browser.switch_to_default_content() + self.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)) + 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) - found_text = self._is_text_present(text) - browser.switch_to_default_content() + self.browser.switch_to_frame(frame) + found_text = self.is_text_present(text) + self.browser.switch_to_default_content() 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 9371f34a2..b52d7787b 100644 --- a/src/Selenium2Library/keywords/formelement.py +++ b/src/Selenium2Library/keywords/formelement.py @@ -1,12 +1,12 @@ import os -from .keywordgroup import KeywordGroup +from Selenium2Library.base import Base +from Selenium2Library.robotlibcore import keyword -class FormElementKeywords(KeywordGroup): - - # Public, form +class FormElementKeywords(Base): + @keyword def submit_form(self, locator=None): """Submits a form identified by `locator`. @@ -14,38 +14,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, tag='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) + % locator) + @keyword def page_should_contain_checkbox(self, locator, message='', loglevel='INFO'): """Verifies checkbox identified by `locator` is found from current page. @@ -55,8 +56,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.page_contains_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. @@ -66,8 +68,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.page_not_contains_element(locator, 'checkbox', message, loglevel) + @keyword def select_checkbox(self, locator): """Selects checkbox identified by `locator`. @@ -75,11 +78,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 +91,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. @@ -103,8 +106,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.page_contains_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. @@ -114,37 +118,41 @@ 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.page_not_contains_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'." \ - % (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): """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: 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): """Sets selection of radio button group identified by `group_name` to `value`. @@ -159,13 +167,13 @@ 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`. @@ -178,9 +186,10 @@ 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) + % file_path) + self.element_find(locator).send_keys(file_path) + @keyword def input_password(self, locator, text): """Types the given password into text field identified by `locator`. @@ -188,17 +197,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. @@ -208,8 +219,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.page_contains_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. @@ -219,8 +231,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.page_not_contains_element(locator, 'text field', message, loglevel) + @keyword def textfield_should_contain(self, locator, expected, message=''): """Verifies text field identified by `locator` contains text `expected`. @@ -229,14 +242,15 @@ 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') - if not expected in actual: + actual = self.get_value(locator, 'text field') + 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 +259,21 @@ 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') - actual = element.get_attribute('value') if element is not None else None + element = self.element_find( + 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 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`. @@ -263,17 +282,18 @@ 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.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) 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`. @@ -282,7 +302,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.get_value(locator, 'text area') if actual is not None: if expected!=actual: if not message: @@ -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, required=False, tag='input') if element is None: - element = self._element_find(locator, True, True, 'button') + element = self.element_find(locator, tag='button') element.click() + @keyword def page_should_contain_button(self, locator, message='', loglevel='INFO'): """Verifies button identified by `locator` is found from current page. @@ -319,10 +339,11 @@ 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.page_contains_element(locator, 'input', message, loglevel) except AssertionError: - self._page_should_contain_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'): """Verifies button identified by `locator` is not found from current page. @@ -334,24 +355,22 @@ 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) - - # Private + 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, True, True, 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(xpath, False, True) + self.debug('Radio group locator: ' + xpath) + 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(xpath, True, True) + self.debug('Radio group locator: ' + xpath) + return self.element_find(xpath) 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) element.clear() element.send_keys(text) diff --git a/src/Selenium2Library/keywords/javascript.py b/src/Selenium2Library/keywords/javascript.py index 203d58686..9089cb23c 100644 --- a/src/Selenium2Library/keywords/javascript.py +++ b/src/Selenium2Library/keywords/javascript.py @@ -1,12 +1,12 @@ import os -from .keywordgroup import KeywordGroup +from Selenium2Library.base import Base +from Selenium2Library.robotlibcore import keyword -class JavaScriptKeywords(KeywordGroup): - - # Public +class JavaScriptKeywords(Base): + @keyword def execute_javascript(self, *code): """Executes the given JavaScript code. @@ -35,9 +35,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.browser.execute_script(js) + @keyword def execute_async_javascript(self, *code): """Executes asynchronous JavaScript code. @@ -59,17 +60,17 @@ 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.browser.execute_async_script(js) 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/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..e16d2fb01 100644 --- a/src/Selenium2Library/keywords/runonfailure.py +++ b/src/Selenium2Library/keywords/runonfailure.py @@ -1,16 +1,12 @@ from robot.libraries.BuiltIn import BuiltIn -from .keywordgroup import KeywordGroup +from Selenium2Library.base import Base +from Selenium2Library.robotlibcore import keyword -class RunOnFailureKeywords(KeywordGroup): - - def __init__(self): - self._run_on_failure_keyword = None - self._running_on_failure_routine = False - - # Public +class RunOnFailureKeywords(Base): + @keyword def register_keyword_to_run_on_failure(self, keyword): """Sets the keyword to execute when a Selenium2Library keyword fails. @@ -33,35 +29,34 @@ 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._info('%s will be run on failure.' % new_keyword_text) + self.ctx._run_on_failure_keyword = new_keyword + self.info('%s will be run on failure.' % new_keyword_text) return old_keyword_text - # Private - - def _run_on_failure(self): - if self._run_on_failure_keyword is None: + def run_on_failure(self): + 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) + 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) + def run_on_failure_error(self, err): + err = ("Keyword '%s' could not be run on failure: %s" + % (self.ctx._run_on_failure_keyword, err)) + if hasattr(self, 'warn'): + self.warn(err) return raise Exception(err) diff --git a/src/Selenium2Library/keywords/screenshot.py b/src/Selenium2Library/keywords/screenshot.py index e0d573070..dc801a89e 100644 --- a/src/Selenium2Library/keywords/screenshot.py +++ b/src/Selenium2Library/keywords/screenshot.py @@ -1,22 +1,24 @@ 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 +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, 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 +37,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 +92,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.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._current_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 - 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): @@ -140,3 +145,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 bc39f98ea..9063f99df 100644 --- a/src/Selenium2Library/keywords/selectelement.py +++ b/src/Selenium2Library/keywords/selectelement.py @@ -1,13 +1,13 @@ from selenium.common.exceptions import NoSuchElementException from selenium.webdriver.support.ui import Select -from .keywordgroup import KeywordGroup +from Selenium2Library.base import Base +from Selenium2Library.robotlibcore import keyword -class SelectElementKeywords(KeywordGroup): - - # Public +class SelectElementKeywords(Base): + @keyword def get_list_items(self, locator, value=False): """Returns the labels or values in the select list identified by `locator`. @@ -27,6 +27,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 +38,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 +53,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 +66,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 +81,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 +92,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 +109,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 +117,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 +125,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. @@ -128,8 +135,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.page_contains_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. @@ -139,23 +147,26 @@ 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.page_not_contains_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: - 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) + @keyword def select_from_list(self, locator, *items): """Selects `*items` from list identified by `locator` @@ -179,7 +190,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) @@ -204,10 +215,11 @@ 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)) + @keyword def select_from_list_by_index(self, locator, *indexes): """Selects `*indexes` from list identified by `locator` @@ -218,12 +230,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 +247,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 +264,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 +286,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 +309,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 +321,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 +330,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 +341,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 +350,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 +361,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 +370,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 +377,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, tag='select') return Select(el) def _get_select_list_options(self, select_list_or_locator): @@ -379,7 +395,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): @@ -389,7 +405,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.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 6408344e5..ad4d8aade 100644 --- a/src/Selenium2Library/keywords/tableelement.py +++ b/src/Selenium2Library/keywords/tableelement.py @@ -1,15 +1,15 @@ +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, 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 +23,15 @@ 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.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 +44,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 +70,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 +99,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.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 +117,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.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 +133,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.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 +157,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.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 +173,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.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..e48f7535b 100644 --- a/src/Selenium2Library/keywords/waiting.py +++ b/src/Selenium2Library/keywords/waiting.py @@ -2,13 +2,18 @@ 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, ctx) + self.element = ElementKeywords(ctx) + @keyword def wait_for_condition(self, condition, timeout=None, error=None): """Waits until the given `condition` is true or `timeout` expires. @@ -28,9 +33,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.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 +53,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 +70,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. @@ -82,10 +91,13 @@ 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._is_element_present, 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): """Waits until element specified with `locator` disappears from current page. @@ -100,13 +112,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_find(locator, required=False) 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 +134,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 +143,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 +158,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 +167,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 +182,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_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)) @@ -179,6 +194,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 +208,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_find(locator) def check_text(): actual = element.text if text in actual: @@ -202,7 +218,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 +232,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_find(locator) 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(): @@ -234,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) @@ -244,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/src/Selenium2Library/robotlibcore.py b/src/Selenium2Library/robotlibcore.py new file mode 100644 index 000000000..ec40e0b7e --- /dev/null +++ b/src/Selenium2Library/robotlibcore.py @@ -0,0 +1,114 @@ +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): + if inspect.ismodule(library): + return inspect.getmembers(library) + if inspect.isclass(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: + 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, []) \ No newline at end of file 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/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/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/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 diff --git a/test/resources/testlibs/CustomSeleniumKeywords.py b/test/resources/testlibs/CustomSeleniumKeywords.py index 8c6a79c6f..f5a241791 100755 --- a/test/resources/testlibs/CustomSeleniumKeywords.py +++ b/test/resources/testlibs/CustomSeleniumKeywords.py @@ -1,18 +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) - self._cache = BuiltIn().get_library_instance("Selenium2Library")._cache + super(CustomSeleniumKeywords, self).__init__(*args, **kwargs) + self.s2l = BuiltIn().get_library_instance("Selenium2Library") + 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) diff --git a/test/unit/keywords/test_browsermanagement.py b/test/unit/keywords/test_browsermanagement.py index 2e52ec1ff..50712f3b1 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 @@ -8,26 +8,30 @@ 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) - 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"), (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 +43,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,54 +54,70 @@ 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"]) self.assertTrue("val2", capabilities["key2"]) self.assertTrue(2, len(capabilities)) + unstub() 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"]) 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"} - 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() + ctx._timeout_in_secs = 5.0 + _browsers = mock() + ctx._browsers = _browsers first_browser, second_browser = mock(), mock() - bm._cache.register(first_browser) - bm._cache.close() - verify(first_browser).quit() - bm._cache.register(second_browser) + 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) - bm._cache.close_all() - verify(second_browser).quit() + when(_browsers).get_open_browsers().thenReturn( + [] + ) bm.set_selenium_timeout("20 seconds") verifyNoMoreInteractions(first_browser) verifyNoMoreInteractions(second_browser) + unstub() def test_bad_browser_name(self): - bm = BrowserManagementKeywords() + ctx = mock() + bm = BrowserManagementKeywords(ctx) try: bm._make_browser("fireox") self.fail("Exception not raised") @@ -103,26 +125,29 @@ 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) 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._current_browser(), driver) - self.assertEquals(bm._cache.get_connection(index), driver) - self.assertEquals(bm._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._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 - 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 +155,11 @@ 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) + unstub() 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) 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 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()