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('