def __init__(self, timeout=5.0, implicit_wait=0.0, run_on_failure='Capture Page Screenshot', screenshot_root_directory=None, plugins=None, event_firing_webdriver=None): """SeleniumLibrary can be imported with several optional arguments. - ``timeout``: Default value for `timeouts` used with ``Wait ...`` keywords. - ``implicit_wait``: Default value for `implicit wait` used when locating elements. - ``run_on_failure``: Default action for the `run-on-failure functionality`. - ``screenshot_root_directory``: Location where possible screenshots are created. If not given, the directory where the log file is written is used. - ``plugins``: Allows extending the SeleniumLibrary with external Python classes. - ``event_firing_webdriver``: Class for wrapping Selenium with [https://seleniumhq.github.io/selenium/docs/api/py/webdriver_support/selenium.webdriver.support.event_firing_webdriver.html#module-selenium.webdriver.support.event_firing_webdriver|EventFiringWebDriver] """ self.timeout = timestr_to_secs(timeout) self.implicit_wait = timestr_to_secs(implicit_wait) self.speed = 0.0 self.run_on_failure_keyword \ = RunOnFailureKeywords.resolve_keyword(run_on_failure) self._running_on_failure_keyword = False self.screenshot_root_directory = screenshot_root_directory self._element_finder = ElementFinder(self) self._plugin_keywords = [] libraries = [ AlertKeywords(self), BrowserManagementKeywords(self), CookieKeywords(self), ElementKeywords(self), FormElementKeywords(self), FrameKeywords(self), JavaScriptKeywords(self), RunOnFailureKeywords(self), ScreenshotKeywords(self), SelectElementKeywords(self), TableElementKeywords(self), WaitingKeywords(self), WindowKeywords(self) ] if is_truthy(plugins): plugin_libs = self._parse_plugins(plugins) libraries = libraries + plugin_libs self._drivers = WebDriverCache() DynamicCore.__init__(self, libraries) self.ROBOT_LIBRARY_LISTENER = LibraryListener() if is_truthy(event_firing_webdriver): self.event_firing_webdriver = self._parse_listener( event_firing_webdriver) else: self.event_firing_webdriver = None self._running_keyword = None
def __init__(self, timeout=5.0, implicit_wait=0.0, run_on_failure='Capture Page Screenshot', screenshot_root_directory=None): """SeleniumLibrary can be imported with several optional arguments. - ``timeout``: Default value for `timeouts` used with ``Wait ...`` keywords. - ``implicit_wait``: Default value for `implicit wait` used when locating elements. - ``run_on_failure``: Default action for the `run-on-failure functionality`. - ``screenshot_root_directory``: Location where possible screenshots are created. If not given, the directory where the log file is written is used. """ self.timeout = timestr_to_secs(timeout) self.implicit_wait = timestr_to_secs(implicit_wait) self.speed = 0.0 self.run_on_failure_keyword \ = RunOnFailureKeywords.resolve_keyword(run_on_failure) self._running_on_failure_keyword = False self.screenshot_root_directory = screenshot_root_directory libraries = [ GerenciamentoDeBrowser(self), ] self._drivers = WebDriverCache() DynamicCore.__init__(self, libraries) self.ROBOT_LIBRARY_LISTENER = LibraryListener() self._element_finder = ElementFinder(self)
def test_close_all_no_error(self): cache = WebDriverCache() driver0, driver1, driver2 = mock(), mock(), mock() when(driver0).quit().thenReturn(None) when(driver1).quit().thenReturn(None) when(driver2).quit().thenReturn(None) cache.close_all() self.verify_cache(cache)
def test_close_no_error(self): cache = WebDriverCache() driver = mock() when(driver).quit().thenReturn(None) cache.register(driver, 'bar') cache.close() self.assertTrue(isinstance(cache.current, NoConnection)) self.assertTrue(driver in cache._closed)
def test_close(self): cache = WebDriverCache() browser = mock() cache.register(browser) verify(browser, times=0).quit() # sanity check cache.close() verify(browser, times=1).quit()
def test_close_all_cache_first_quite_fails(self): cache = WebDriverCache() driver = mock() when(driver).quit().thenRaise(TimeoutException('timeout.')) cache.register(driver, 'bar') with self.assertRaises(TimeoutException): cache.close_all() self.verify_cache(cache)
def test_close_quite_fails(self): cache = WebDriverCache() driver = mock() when(driver).quit().thenRaise(TimeoutException('timeout.')) cache.register(driver, 'bar') with self.assertRaises(TimeoutException): cache.close() self.assertTrue(isinstance(cache.current, NoConnection)) self.assertTrue(driver in cache._closed)
def __init__(self, timeout=5.0, implicit_wait=0.0, run_on_failure='Capture Page Screenshot', screenshot_root_directory=None, demo=False): """SeleniumLibrary can be imported with several optional arguments. - ``timeout``: Default value for `timeouts` used with ``Wait ...`` keywords. - ``implicit_wait``: Default value for `implicit wait` used when locating elements. - ``run_on_failure``: Default action for the `run-on-failure functionality`. - ``screenshot_root_directory``: Location where possible screenshots are created. If not given, the directory where the log file is written is used. - ``Demo`` Flash the element before click, input and etc actions. Set value True if you want it. """ self.timeout = timestr_to_secs(timeout) self.implicit_wait = timestr_to_secs(implicit_wait) self.speed = 0.0 self.run_on_failure_keyword \ = RunOnFailureKeywords.resolve_keyword(run_on_failure) self._running_on_failure_keyword = False self.screenshot_root_directory = screenshot_root_directory self.demo = demo libraries = [ AlertKeywords(self), BrowserManagementKeywords(self), CookieKeywords(self), ElementKeywords(self), FormElementKeywords(self), FrameKeywords(self), JavaScriptKeywords(self), RunOnFailureKeywords(self), ScreenshotKeywords(self), SelectElementKeywords(self), TableElementKeywords(self), WaitingKeywords(self), WindowKeywords(self) ] self._drivers = WebDriverCache() DynamicCore.__init__(self, libraries) self.ROBOT_LIBRARY_LISTENER = LibraryListener() self._element_finder = ElementFinder(self)
def __init__(self, timeout=5.0, implicit_wait=0.0, run_on_failure='Capture Page Screenshot', screenshot_root_directory=None): self.timeout = timestr_to_secs(timeout) self.implicit_wait = timestr_to_secs(implicit_wait) self.speed = 0.0 self.run_on_failure_keyword \ = RunOnFailureKeywords.resolve_keyword(run_on_failure) self._running_on_failure_keyword = False self.screenshot_root_directory = screenshot_root_directory libraries = [ AlertKeywords(self), BrowserManagementKeywords(self), CookieKeywords(self), ElementKeywords(self), FormElementKeywords(self), FrameKeywords(self), JavaScriptKeywords(self), RunOnFailureKeywords(self), ScreenshotKeywords(self), SelectElementKeywords(self), TableElementKeywords(self), WaitingKeywords(self), WindowKeywords(self), KeyboardKeywords(self) ] self._drivers = WebDriverCache() DynamicCore.__init__(self, libraries) self.ROBOT_LIBRARY_LISTENER = LibraryListener() self._element_finder = ElementFinder(self)
def test_resolve_alias_or_index_with_none(self): cache = WebDriverCache() cache.register(mock(), "foo") cache.register(mock(), "None") index = cache.get_index("foo") self.assertEqual(index, 1) index = cache.get_index(1) self.assertEqual(index, 1) index = cache.get_index(None) self.assertEqual(index, None)
def test_resolve_alias_or_index_error(self): cache = WebDriverCache() cache.register(mock(), "foo") cache.register(mock()) index = cache.get_index("bar") self.assertEqual(index, None) index = cache.get_index(12) self.assertEqual(index, None) index = cache.get_index(-1) self.assertEqual(index, None)
def test_browsers_property(self): cache = WebDriverCache() driver1 = mock() driver2 = mock() driver3 = mock() index1 = cache.register(driver1) index2 = cache.register(driver2) index3 = cache.register(driver3) self.assertEqual(len(cache.drivers), 3) self.assertEqual(cache.drivers[0], driver1) self.assertEqual(cache.drivers[1], driver2) self.assertEqual(cache.drivers[2], driver3) self.assertEqual(index1, 1) self.assertEqual(index2, 2) self.assertEqual(index3, 3)
def test_close_only_called_once(self): cache = WebDriverCache() browser1 = mock() browser2 = mock() browser3 = mock() cache.register(browser1) cache.register(browser2) cache.register(browser3) cache.close() verify(browser3, times=1).quit() cache.close_all() verify(browser1, times=1).quit() verify(browser2, times=1).quit() verify(browser3, times=1).quit()
def test_close_and_same_alias(self): cache = WebDriverCache() cache.register(mock(), "foo") cache.register(mock(), "bar") cache.close() index = cache.get_index("bar") self.assertEqual(index, None)
def test_close_all_cache_middle_quite_fails(self): cache = WebDriverCache() driver0, driver1, driver2 = mock(), mock(), mock() when(driver0).quit().thenReturn(None) when(driver1).quit().thenRaise(TimeoutException("timeout.")) when(driver2).quit().thenReturn(None) cache.register(driver0, "bar0") cache.register(driver1, "bar1") cache.register(driver2, "bar2") with self.assertRaises(TimeoutException): cache.close_all() self.verify_cache(cache)
def test_close_all_cache_all_quite_fails(self): cache = WebDriverCache() driver0, driver1, driver2 = mock(), mock(), mock() when(driver0).quit().thenRaise(RemoteDriverServerException('stuff.')) when(driver1).quit().thenRaise(RemoteDriverServerException('stuff.')) when(driver2).quit().thenRaise(TimeoutException('timeout.')) cache.register(driver0, 'bar0') cache.register(driver1, 'bar1') cache.register(driver2, 'bar2') with self.assertRaises(TimeoutException): cache.close_all() self.verify_cache(cache)
def test_close_all_cache_not_selenium_error(self): cache = WebDriverCache() driver0, driver1, driver2 = mock(), mock(), mock() when(driver0).quit().thenRaise(RemoteDriverServerException("stuff.")) when(driver1).quit().thenRaise(ValueError("stuff.")) when(driver2).quit().thenRaise(TimeoutException("timeout.")) cache.register(driver0, "bar0") cache.register(driver1, "bar1") cache.register(driver2, "bar2") with self.assertRaises(TimeoutException): cache.close_all() self.verify_cache(cache)
def test_resolve_alias_or_index_with_none(self): cache = WebDriverCache() cache.register(mock(), 'foo') cache.register(mock(), 'None') index = cache.get_index('foo') self.assertEqual(index, 1) index = cache.get_index(1) self.assertEqual(index, 1) index = cache.get_index(None) self.assertEqual(index, None) index = cache.get_index('None') self.assertEqual(index, None) index = cache.get_index('NoNe') self.assertEqual(index, None)
def test_resolve_alias_or_index_error(self): cache = WebDriverCache() cache.register(mock(), 'foo') cache.register(mock()) index = cache.get_index('bar') self.assertEqual(index, None) index = cache.get_index(12) self.assertEqual(index, None) index = cache.get_index(-1) self.assertEqual(index, None)
def test_close_all_cache_first_quite_fails(self): cache = WebDriverCache() driver = mock() when(driver).quit().thenRaise(TimeoutException("timeout.")) cache.register(driver, "bar") with self.assertRaises(TimeoutException): cache.close_all() self.verify_cache(cache)
def test_close_and_same_alias(self): cache = WebDriverCache() cache.register(mock(), 'foo') cache.register(mock(), 'bar') cache.close() index = cache.get_index('bar') self.assertEqual(index, None)
def test_close_no_error(self): cache = WebDriverCache() driver = mock() when(driver).quit().thenReturn(None) cache.register(driver, "bar") cache.close() self.assertTrue(isinstance(cache.current, NoConnection)) self.assertTrue(driver in cache._closed)
def test_close_all_cache_middle_quite_fails(self): cache = WebDriverCache() driver0, driver1, driver2 = mock(), mock(), mock() when(driver0).quit().thenReturn(None) when(driver1).quit().thenRaise(TimeoutException('timeout.')) when(driver2).quit().thenReturn(None) cache.register(driver0, 'bar0') cache.register(driver1, 'bar1') cache.register(driver2, 'bar2') with self.assertRaises(TimeoutException): cache.close_all() self.verify_cache(cache)
def test_close_all_cache_not_selenium_error(self): cache = WebDriverCache() driver0, driver1, driver2 = mock(), mock(), mock() when(driver0).quit().thenRaise(RemoteDriverServerException('stuff.')) when(driver1).quit().thenRaise(ValueError('stuff.')) when(driver2).quit().thenRaise(TimeoutException('timeout.')) cache.register(driver0, 'bar0') cache.register(driver1, 'bar1') cache.register(driver2, 'bar2') with self.assertRaises(TimeoutException): cache.close_all() self.verify_cache(cache)
def test_close_quite_fails(self): cache = WebDriverCache() driver = mock() when(driver).quit().thenRaise(TimeoutException("timeout.")) cache.register(driver, "bar") with self.assertRaises(TimeoutException): cache.close() self.assertTrue(isinstance(cache.current, NoConnection)) self.assertTrue(driver in cache._closed)
def test_get_open_browsers(self): cache = WebDriverCache() driver1 = mock() driver2 = mock() driver3 = mock() cache.register(driver1) cache.register(driver2) cache.register(driver3) drivers = cache.active_drivers self.assertEqual(len(drivers), 3) self.assertEqual(drivers[0], driver1) self.assertEqual(drivers[1], driver2) self.assertEqual(drivers[2], driver3) cache.close() drivers = cache.active_drivers self.assertEqual(len(drivers), 2) self.assertEqual(drivers[0], driver1) self.assertEqual(drivers[1], driver2)
class SeleniumLibrary(DynamicCore): """SeleniumLibrary is a web testing library for Robot Framework. This document explains how to use keywords provided by SeleniumLibrary. For information about installation, support, and more, please visit the [https://github.com/robotframework/SeleniumLibrary|project pages]. For more information about Robot Framework, see http://robotframework.org. SeleniumLibrary uses the Selenium WebDriver modules internally to control a web browser. See http://seleniumhq.org for more information about Selenium in general and SeleniumLibrary README.rst [https://github.com/robotframework/SeleniumLibrary#browser-drivers|Browser drivers chapter] for more details about WebDriver binary installation. == Table of contents == - `Locating elements` - `Timeouts, waits and delays` - `Run-on-failure functionality` - `Boolean arguments` - `Plugins` - `EventFiringWebDriver` - `Thread support` - `Importing` - `Shortcuts` - `Keywords` = Locating elements = All keywords in SeleniumLibrary that need to interact with an element on a web page take an argument typically named ``locator`` that specifies how to find the element. Most often the locator is given as a string using the locator syntax described below, but `using WebElements` is possible too. == Locator syntax == SeleniumLibrary supports finding elements based on different strategies such as the element id, XPath expressions, or CSS selectors. The strategy can either be explicitly specified with a prefix or the strategy can be implicit. === Default locator strategy === By default locators are considered to use the keyword specific default locator strategy. All keywords support finding elements based on ``id`` and ``name`` attributes, but some keywords support additional attributes or other values that make sense in their context. For example, `Click Link` supports the ``href`` attribute and the link text and addition to the normal ``id`` and ``name``. Examples: | `Click Element` | example | # Match based on ``id`` or ``name``. | | `Click Link` | example | # Match also based on link text and ``href``. | | `Click Button` | example | # Match based on ``id``, ``name`` or ``value``. | If a locator accidentally starts with a prefix recognized as `explicit locator strategy` or `implicit XPath strategy`, it is possible to use the explicit ``default`` prefix to enable the default strategy. Examples: | `Click Element` | name:foo | # Find element with name ``foo``. | | `Click Element` | default:name:foo | # Use default strategy with value ``name:foo``. | | `Click Element` | //foo | # Find element using XPath ``//foo``. | | `Click Element` | default: //foo | # Use default strategy with value ``//foo``. | === Explicit locator strategy === The explicit locator strategy is specified with a prefix using either syntax ``strategy:value`` or ``strategy=value``. The former syntax is preferred, because the latter is identical to Robot Framework's [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#named-argument-syntax| named argument syntax] and that can cause problems. Spaces around the separator are ignored, so ``id:foo``, ``id: foo`` and ``id : foo`` are all equivalent. Locator strategies that are supported by default are listed in the table below. In addition to them, it is possible to register `custom locators`. | = Strategy = | = Match based on = | = Example = | | id | Element ``id``. | ``id:example`` | | name | ``name`` attribute. | ``name:example`` | | identifier | Either ``id`` or ``name``. | ``identifier:example`` | | class | Element ``class``. | ``class:example`` | | tag | Tag name. | ``tag:div`` | | xpath | XPath expression. | ``xpath://div[@id="example"]`` | | css | CSS selector. | ``css:div#example`` | | dom | DOM expression. | ``dom:document.images[5]`` | | link | Exact text a link has. | ``link:The example`` | | partial link | Partial link text. | ``partial link:he ex`` | | sizzle | Sizzle selector deprecated. | ``sizzle:div.example`` | | jquery | jQuery expression. | ``jquery:div.example`` | | default | Keyword specific default behavior. | ``default:example`` | See the `Default locator strategy` section below for more information about how the default strategy works. Using the explicit ``default`` prefix is only necessary if the locator value itself accidentally matches some of the explicit strategies. Different locator strategies have different pros and cons. Using ids, either explicitly like ``id:foo`` or by using the `default locator strategy` simply like ``foo``, is recommended when possible, because the syntax is simple and locating elements by an id is fast for browsers. If an element does not have an id or the id is not stable, other solutions need to be used. If an element has a unique tag name or class, using ``tag``, ``class`` or ``css`` strategy like ``tag:h1``, ``class:example`` or ``css:h1.example`` is often an easy solution. In more complex cases using XPath expressions is typically the best approach. They are very powerful but a downside is that they can also get complex. Examples: | `Click Element` | id:foo | # Element with id 'foo'. | | `Click Element` | css:div#foo h1 | # h1 element under div with id 'foo'. | | `Click Element` | xpath: //div[@id="foo"]//h1 | # Same as the above using XPath, not CSS. | | `Click Element` | xpath: //*[contains(text(), "example")] | # Element containing text 'example'. | *NOTE:* - The ``strategy:value`` syntax is only supported by SeleniumLibrary 3.0 and newer. - Using the ``sizzle`` strategy or its alias ``jquery`` requires that the system under test contains the jQuery library. - Prior to SeleniumLibrary 3.0, table related keywords only supported ``xpath``, ``css`` and ``sizzle/jquery`` strategies. === Implicit XPath strategy === If the locator starts with ``//`` or ``(//``, the locator is considered to be an XPath expression. In other words, using ``//div`` is equivalent to using explicit ``xpath://div``. Examples: | `Click Element` | //div[@id="foo"]//h1 | | `Click Element` | (//div)[2] | The support for the ``(//`` prefix is new in SeleniumLibrary 3.0. == Using WebElements == In addition to specifying a locator as a string, it is possible to use Selenium's WebElement objects. This requires first getting a WebElement, for example, by using the `Get WebElement` keyword. | ${elem} = | `Get WebElement` | id:example | | `Click Element` | ${elem} | | == Custom locators == If more complex lookups are required than what is provided through the default locators, custom lookup strategies can be created. Using custom locators is a two part process. First, create a keyword that returns a WebElement that should be acted on: | Custom Locator Strategy | [Arguments] | ${browser} | ${locator} | ${tag} | ${constraints} | | | ${element}= | Execute Javascript | return window.document.getElementById('${locator}'); | | | [Return] | ${element} | This keyword is a reimplementation of the basic functionality of the ``id`` locator where ``${browser}`` is a reference to a WebDriver instance and ``${locator}`` is name of the locator strategy. To use this locator it must first be registered by using the `Add Location Strategy` keyword: | `Add Location Strategy` | custom | Custom Locator Strategy | The first argument of `Add Location Strategy` specifies the name of the strategy and it must be unique. After registering the strategy, the usage is the same as with other locators: | `Click Element` | custom:example | See the `Add Location Strategy` keyword for more details. = Timeouts, waits and delays = This section discusses different ways how to wait for elements to appear on web pages and to slow down execution speed otherwise. It also explains the `time format` that can be used when setting various timeouts, waits and delays. == Timeout == SeleniumLibrary contains various keywords that have an optional ``timeout`` argument that specifies how long these keywords should wait for certain events or actions. These keywords include, for example, ``Wait ...`` keywords and keywords related to alerts. Additionally `Execute Async Javascript`. although it does not have ``timeout``, argument, uses timeout to define how long asynchronous JavaScript can run. The default timeout these keywords use can be set globally either by using the `Set Selenium Timeout` keyword or with the ``timeout`` argument when `importing` the library. See `time format` below for supported timeout syntax. == Implicit wait == Implicit wait specifies the maximum time how long Selenium waits when searching for elements. It can be set by using the `Set Selenium Implicit Wait` keyword or with the ``implicit_wait`` argument when `importing` the library. See [https://www.seleniumhq.org/docs/04_webdriver_advanced.jsp| Selenium documentation] for more information about this functionality. See `time format` below for supported syntax. == Selenium speed == Selenium execution speed can be slowed down globally by using `Set Selenium speed` keyword. This functionality is designed to be used for demonstrating or debugging purposes. Using it to make sure that elements appear on a page is not a good idea, and the above explained timeouts and waits should be used instead. See `time format` below for supported syntax. == Time format == All timeouts and waits can be given as numbers considered seconds (e.g. ``0.5`` or ``42``) or in Robot Framework's time syntax (e.g. ``1.5 seconds`` or ``1 min 30 s``). For more information about the time syntax see the [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#time-format|Robot Framework User Guide]. = Run-on-failure functionality = SeleniumLibrary has a handy feature that it can automatically execute a keyword if any of its own keywords fails. By default it uses the `Capture Page Screenshot` keyword, but this can be changed either by using the `Register Keyword To Run On Failure` keyword or with the ``run_on_failure`` argument when `importing` the library. It is possible to use any keyword from any imported library or resource file. The run-on-failure functionality can be disabled by using a special value ``NOTHING`` or anything considered false (see `Boolean arguments`) such as ``NONE``. = Boolean arguments = Some keywords accept arguments that are handled as Boolean values true or false. If such an argument is given as a string, it is considered false if it is either empty or case-insensitively equal to ``false``, ``no``, ``off``, ``0`` or ``none``. Other strings are considered true regardless their value, and other argument types are tested using same [https://docs.python.org/2/library/stdtypes.html#truth-value-testing|rules as in Python]. True examples: | `Set Screenshot Directory` | ${RESULTS} | persist=True | # Strings are generally true. | | `Set Screenshot Directory` | ${RESULTS} | persist=yes | # Same as the above. | | `Set Screenshot Directory` | ${RESULTS} | persist=${TRUE} | # Python True is true. | | `Set Screenshot Directory` | ${RESULTS} | persist=${42} | # Numbers other than 0 are true. | False examples: | `Set Screenshot Directory` | ${RESULTS} | persist=False | # String false is false. | | `Set Screenshot Directory` | ${RESULTS} | persist=no | # Also string no is false. | | `Set Screenshot Directory` | ${RESULTS} | persist=NONE | # String NONE is false. | | `Set Screenshot Directory` | ${RESULTS} | persist=${EMPTY} | # Empty string is false. | | `Set Screenshot Directory` | ${RESULTS} | persist=${FALSE} | # Python False is false. | | `Set Screenshot Directory` | ${RESULTS} | persist=${NONE} | # Python None is false. | Note that prior to SeleniumLibrary 3.0, all non-empty strings, including ``false``, ``no`` and ``none``, were considered true. Starting from SeleniumLibrary 4.0, strings ``0`` and ``off`` are considered as false. = Plugins = SeleniumLibrary offers plugins as a way to modify and add library keywords and modify some of the internal functionality without creating new library or hacking the source code. See [https://github.com/robotframework/SeleniumLibrary/blob/master/docs/extending/extending_seleniumlibrary.rst#Plugins|plugin API] documentation for further details. Plugin API is new SeleniumLibrary 4.0 = EventFiringWebDriver = The SeleniumLibrary offers support for [https://seleniumhq.github.io/selenium/docs/api/py/webdriver_support/selenium.webdriver.support.event_firing_webdriver.html#module-selenium.webdriver.support.event_firing_webdriver|EventFiringWebDriver]. See the Selenium and SeleniumLibrary [https://github.com/robotframework/SeleniumLibrary/blob/master/docs/extending/extending_seleniumlibrary.rst#EventFiringWebDriver|EventFiringWebDriver support] documentation for futher details. EventFiringWebDriver is new in SeleniumLibrary 4.0 = Thread support = SeleniumLibrary is not thread safe. This is mainly due because the underlying [https://github.com/SeleniumHQ/selenium/wiki/Frequently-Asked-Questions#q-is-webdriver-thread-safe| Selenium tool is not thread safe] within one browser/driver instance. Because of the limitation in the Selenium side, the keywords or the API provided by the SeleniumLibrary is not thread safe. """ ROBOT_LIBRARY_SCOPE = 'GLOBAL' ROBOT_LIBRARY_VERSION = __version__ def __init__(self, timeout=5.0, implicit_wait=0.0, run_on_failure='Capture Page Screenshot', screenshot_root_directory=None, plugins=None, event_firing_webdriver=None): """SeleniumLibrary can be imported with several optional arguments. - ``timeout``: Default value for `timeouts` used with ``Wait ...`` keywords. - ``implicit_wait``: Default value for `implicit wait` used when locating elements. - ``run_on_failure``: Default action for the `run-on-failure functionality`. - ``screenshot_root_directory``: Location where possible screenshots are created. If not given, the directory where the log file is written is used. - ``plugins``: Allows extending the SeleniumLibrary with external Python classes. - ``event_firing_webdriver``: Class for wrapping Selenium with [https://seleniumhq.github.io/selenium/docs/api/py/webdriver_support/selenium.webdriver.support.event_firing_webdriver.html#module-selenium.webdriver.support.event_firing_webdriver|EventFiringWebDriver] """ self.timeout = timestr_to_secs(timeout) self.implicit_wait = timestr_to_secs(implicit_wait) self.speed = 0.0 self.run_on_failure_keyword \ = RunOnFailureKeywords.resolve_keyword(run_on_failure) self._running_on_failure_keyword = False self.screenshot_root_directory = screenshot_root_directory self._element_finder = ElementFinder(self) self._plugin_keywords = [] libraries = [ AlertKeywords(self), BrowserManagementKeywords(self), CookieKeywords(self), ElementKeywords(self), FormElementKeywords(self), FrameKeywords(self), JavaScriptKeywords(self), RunOnFailureKeywords(self), ScreenshotKeywords(self), SelectElementKeywords(self), TableElementKeywords(self), WaitingKeywords(self), WindowKeywords(self) ] if is_truthy(plugins): plugin_libs = self._parse_plugins(plugins) libraries = libraries + plugin_libs self._drivers = WebDriverCache() DynamicCore.__init__(self, libraries) self.ROBOT_LIBRARY_LISTENER = LibraryListener() if is_truthy(event_firing_webdriver): self.event_firing_webdriver = self._parse_listener(event_firing_webdriver) else: self.event_firing_webdriver = None def run_keyword(self, name, args, kwargs): try: return DynamicCore.run_keyword(self, name, args, kwargs) except Exception: self.failure_occurred() raise def get_keyword_tags(self, name): tags = list(DynamicCore.get_keyword_tags(self, name)) if name in self._plugin_keywords: tags.append('plugin') return tags def register_driver(self, driver, alias): """Add's a `driver` to the library WebDriverCache. :param driver: Instance of the Selenium `WebDriver`. :type driver: selenium.webdriver.remote.webdriver.WebDriver :param alias: Alias given for this `WebDriver` instance. :type alias: str :return: The index of the `WebDriver` instance. :rtype: int """ return self._drivers.register(driver, alias) def failure_occurred(self): """Method that is executed when a SeleniumLibrary keyword fails. By default executes the registered run-on-failure keyword. Libraries extending SeleniumLibrary can overwrite this hook method if they want to provide custom functionality instead. """ if self._running_on_failure_keyword or not self.run_on_failure_keyword: return try: self._running_on_failure_keyword = True BuiltIn().run_keyword(self.run_on_failure_keyword) except Exception as err: logger.warn("Keyword '%s' could not be run on failure: %s" % (self.run_on_failure_keyword, err)) finally: self._running_on_failure_keyword = False @property def driver(self): """Current active driver. :rtype: selenium.webdriver.remote.webdriver.WebDriver :raises SeleniumLibrary.errors.NoOpenBrowser: If browser is not open. """ if not self._drivers.current: raise NoOpenBrowser('No browser is open.') return self._drivers.current def find_element(self, locator, parent=None): """Find element matching `locator`. :param locator: Locator to use when searching the element. See library documentation for the supported locator syntax. :type locator: str or selenium.webdriver.remote.webelement.WebElement :param parent: Optional parent `WebElememt` to search child elements from. By default search starts from the root using `WebDriver`. :type parent: selenium.webdriver.remote.webelement.WebElement :return: Found `WebElement`. :rtype: selenium.webdriver.remote.webelement.WebElement :raises SeleniumLibrary.errors.ElementNotFound: If element not found. """ return self._element_finder.find(locator, parent=parent) def find_elements(self, locator, parent=None): """Find all elements matching `locator`. :param locator: Locator to use when searching the element. See library documentation for the supported locator syntax. :type locator: str or selenium.webdriver.remote.webelement.WebElement :param parent: Optional parent `WebElememt` to search child elements from. By default search starts from the root using `WebDriver`. :type parent: selenium.webdriver.remote.webelement.WebElement :return: list of found `WebElement` or e,mpty if elements are not found. :rtype: list[selenium.webdriver.remote.webelement.WebElement] """ return self._element_finder.find(locator, first_only=False, required=False, parent=parent) def _parse_plugins(self, plugins): libraries = [] importer = Importer('test library') for parsed_plugin in self._string_to_modules(plugins): plugin = importer.import_class_or_module(parsed_plugin.module) if not isclass(plugin): message = "Importing test library: '%s' failed." % parsed_plugin.module raise DataError(message) plugin = plugin(self, *parsed_plugin.args, **parsed_plugin.kw_args) if not isinstance(plugin, LibraryComponent): message = 'Plugin does not inherit SeleniumLibrary.base.LibraryComponent' raise PluginError(message) self._store_plugin_keywords(plugin) libraries.append(plugin) return libraries def _parse_listener(self, event_firing_webdriver): listener_module = self._string_to_modules(event_firing_webdriver) listener_count = len(listener_module ) if listener_count > 1: message = 'Is is possible import only one listener but there was %s listeners.' % listener_count raise ValueError(message) listener_module = listener_module[0] importer = Importer('test library') listener = importer.import_class_or_module(listener_module.module) if not isclass(listener): message = "Importing test Selenium lister class '%s' failed." % listener_module.module raise DataError(message) return listener def _string_to_modules(self, modules): Module = namedtuple('Module', 'module, args, kw_args') parsed_modules = [] for module in modules.split(','): module = module.strip() module_and_args = module.split(';') module_name = module_and_args.pop(0) kw_args = {} args = [] for argument in module_and_args: if '=' in argument: key, value = argument.split('=') kw_args[key] = value else: args.append(argument) module = Module(module=module_name, args=args, kw_args=kw_args) parsed_modules.append(module) return parsed_modules def _store_plugin_keywords(self, plugin): dynamic_core = DynamicCore([plugin]) self._plugin_keywords.extend(dynamic_core.get_keyword_names())
class SeleniumLibraryTeste(DynamicCore): ROBOT_LIBRARY_SCOPE = 'GLOBAL' ROBOT_LIBRARY_VERSION = __version__ def __init__(self, timeout=5.0, implicit_wait=0.0, run_on_failure='Capture Page Screenshot', screenshot_root_directory=None): """SeleniumLibrary can be imported with several optional arguments. - ``timeout``: Default value for `timeouts` used with ``Wait ...`` keywords. - ``implicit_wait``: Default value for `implicit wait` used when locating elements. - ``run_on_failure``: Default action for the `run-on-failure functionality`. - ``screenshot_root_directory``: Location where possible screenshots are created. If not given, the directory where the log file is written is used. """ self.timeout = timestr_to_secs(timeout) self.implicit_wait = timestr_to_secs(implicit_wait) self.speed = 0.0 self.run_on_failure_keyword \ = RunOnFailureKeywords.resolve_keyword(run_on_failure) self._running_on_failure_keyword = False self.screenshot_root_directory = screenshot_root_directory libraries = [ GerenciamentoDeBrowser(self), ] self._drivers = WebDriverCache() DynamicCore.__init__(self, libraries) self.ROBOT_LIBRARY_LISTENER = LibraryListener() self._element_finder = ElementFinder(self) _speed_in_secs = Deprecated('_speed_in_secs', 'speed') _timeout_in_secs = Deprecated('_timeout_in_secs', 'timeout') _implicit_wait_in_secs = Deprecated('_implicit_wait_in_secs', 'implicit_wait') _run_on_failure_keyword = Deprecated('_run_on_failure_keyword', 'run_on_failure_keyword') def run_keyword(self, name, args, kwargs): try: return DynamicCore.run_keyword(self, name, args, kwargs) except Exception: self.failure_occurred() raise def register_driver(self, driver, alias): """Add's a `driver` to the library WebDriverCache. :param driver: Instance of the Selenium `WebDriver`. :type driver: selenium.webdriver.remote.webdriver.WebDriver :param alias: Alias given for this `WebDriver` instance. :type alias: str :return: The index of the `WebDriver` instance. :rtype: int """ return self._drivers.register(driver, alias) def failure_occurred(self): """Method that is executed when a SeleniumLibrary keyword fails. By default executes the registered run-on-failure keyword. Libraries extending SeleniumLibrary can overwrite this hook method if they want to provide custom functionality instead. """ if self._running_on_failure_keyword or not self.run_on_failure_keyword: return try: self._running_on_failure_keyword = True BuiltIn().run_keyword(self.run_on_failure_keyword) except Exception as err: logger.warn("Keyword '%s' could not be run on failure: %s" % (self.run_on_failure_keyword, err)) finally: self._running_on_failure_keyword = False @property def driver(self): """Current active driver. :rtype: selenium.webdriver.remote.webdriver.WebDriver :raises SeleniumLibrary.errors.NoOpenBrowser: If browser is not open. """ if not self._drivers.current: raise NoOpenBrowser('No browser is open.') return self._drivers.current def find_element(self, locator, parent=None): """Find element matching `locator`. :param locator: Locator to use when searching the element. See library documentation for the supported locator syntax. :type locator: str or selenium.webdriver.remote.webelement.WebElement :param parent: Optional parent `WebElememt` to search child elements from. By default search starts from the root using `WebDriver`. :type parent: selenium.webdriver.remote.webelement.WebElement :return: Found `WebElement`. :rtype: selenium.webdriver.remote.webelement.WebElement :raises SeleniumLibrary.errors.ElementNotFound: If element not found. """ return self._element_finder.find(locator, parent=parent) def find_elements(self, locator, parent=None): """Find all elements matching `locator`. :param locator: Locator to use when searching the element. See library documentation for the supported locator syntax. :type locator: str or selenium.webdriver.remote.webelement.WebElement :param parent: Optional parent `WebElememt` to search child elements from. By default search starts from the root using `WebDriver`. :type parent: selenium.webdriver.remote.webelement.WebElement :return: list of found `WebElement` or e,mpty if elements are not found. :rtype: list[selenium.webdriver.remote.webelement.WebElement] """ return self._element_finder.find(locator, first_only=False, required=False, parent=parent) @property def _cache(self): warnings.warn( '"SeleniumLibrary._cache" is deprecated, ' 'use public API instead.', DeprecationWarning) return self._drivers def _current_browser(self): warnings.warn( '"SeleniumLibrary._current_browser" is deprecated, ' 'use "SeleniumLibrary.driver" instead.', DeprecationWarning) return self.driver def _run_on_failure(self): warnings.warn( '"SeleniumLibrary._run_on_failure" is deprecated, ' 'use "SeleniumLibrary.failure_occurred" instead.', DeprecationWarning) self.failure_occurred()
def test_no_current_message(self): cache = WebDriverCache() try: self.assertRaises(RuntimeError, cache.current.anyMember()) except RuntimeError as e: self.assertEqual(str(e), "No current browser")
def test_same_alias_new_browser(self): cache = WebDriverCache() cache.close() index = cache.get_index('bar') self.assertEqual(index, None)
class SeleniumLibrary(DynamicCore): """SeleniumLibrary is a web testing library for Robot Framework. This document explains how to use keywords provided by SeleniumLibrary. For information about installation, support, and more, please visit the [https://github.com/robotframework/SeleniumLibrary|project pages]. For more information about Robot Framework, see http://robotframework.org. SeleniumLibrary uses the Selenium WebDriver modules internally to control a web browser. See http://seleniumhq.org for more information about Selenium in general and SeleniumLibrary README.rst [https://github.com/robotframework/SeleniumLibrary#browser-drivers|Browser drivers chapter] for more details about WebDriver binary installation. == Table of contents == - `Locating elements` - `Timeouts, waits and delays` - `Run-on-failure functionality` - `Boolean arguments` - `Plugins` - `EventFiringWebDriver` - `Thread support` - `Importing` - `Shortcuts` - `Keywords` = Locating elements = All keywords in SeleniumLibrary that need to interact with an element on a web page take an argument typically named ``locator`` that specifies how to find the element. Most often the locator is given as a string using the locator syntax described below, but `using WebElements` is possible too. == Locator syntax == SeleniumLibrary supports finding elements based on different strategies such as the element id, XPath expressions, or CSS selectors. The strategy can either be explicitly specified with a prefix or the strategy can be implicit. === Default locator strategy === By default locators are considered to use the keyword specific default locator strategy. All keywords support finding elements based on ``id`` and ``name`` attributes, but some keywords support additional attributes or other values that make sense in their context. For example, `Click Link` supports the ``href`` attribute and the link text and addition to the normal ``id`` and ``name``. Examples: | `Click Element` | example | # Match based on ``id`` or ``name``. | | `Click Link` | example | # Match also based on link text and ``href``. | | `Click Button` | example | # Match based on ``id``, ``name`` or ``value``. | If a locator accidentally starts with a prefix recognized as `explicit locator strategy` or `implicit XPath strategy`, it is possible to use the explicit ``default`` prefix to enable the default strategy. Examples: | `Click Element` | name:foo | # Find element with name ``foo``. | | `Click Element` | default:name:foo | # Use default strategy with value ``name:foo``. | | `Click Element` | //foo | # Find element using XPath ``//foo``. | | `Click Element` | default: //foo | # Use default strategy with value ``//foo``. | === Explicit locator strategy === The explicit locator strategy is specified with a prefix using either syntax ``strategy:value`` or ``strategy=value``. The former syntax is preferred, because the latter is identical to Robot Framework's [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#named-argument-syntax| named argument syntax] and that can cause problems. Spaces around the separator are ignored, so ``id:foo``, ``id: foo`` and ``id : foo`` are all equivalent. Locator strategies that are supported by default are listed in the table below. In addition to them, it is possible to register `custom locators`. | = Strategy = | = Match based on = | = Example = | | id | Element ``id``. | ``id:example`` | | name | ``name`` attribute. | ``name:example`` | | identifier | Either ``id`` or ``name``. | ``identifier:example`` | | class | Element ``class``. | ``class:example`` | | tag | Tag name. | ``tag:div`` | | xpath | XPath expression. | ``xpath://div[@id="example"]`` | | css | CSS selector. | ``css:div#example`` | | dom | DOM expression. | ``dom:document.images[5]`` | | link | Exact text a link has. | ``link:The example`` | | partial link | Partial link text. | ``partial link:he ex`` | | sizzle | Sizzle selector deprecated. | ``sizzle:div.example`` | | jquery | jQuery expression. | ``jquery:div.example`` | | default | Keyword specific default behavior. | ``default:example`` | See the `Default locator strategy` section below for more information about how the default strategy works. Using the explicit ``default`` prefix is only necessary if the locator value itself accidentally matches some of the explicit strategies. Different locator strategies have different pros and cons. Using ids, either explicitly like ``id:foo`` or by using the `default locator strategy` simply like ``foo``, is recommended when possible, because the syntax is simple and locating elements by an id is fast for browsers. If an element does not have an id or the id is not stable, other solutions need to be used. If an element has a unique tag name or class, using ``tag``, ``class`` or ``css`` strategy like ``tag:h1``, ``class:example`` or ``css:h1.example`` is often an easy solution. In more complex cases using XPath expressions is typically the best approach. They are very powerful but a downside is that they can also get complex. Examples: | `Click Element` | id:foo | # Element with id 'foo'. | | `Click Element` | css:div#foo h1 | # h1 element under div with id 'foo'. | | `Click Element` | xpath: //div[@id="foo"]//h1 | # Same as the above using XPath, not CSS. | | `Click Element` | xpath: //*[contains(text(), "example")] | # Element containing text 'example'. | *NOTE:* - The ``strategy:value`` syntax is only supported by SeleniumLibrary 3.0 and newer. - Using the ``sizzle`` strategy or its alias ``jquery`` requires that the system under test contains the jQuery library. - Prior to SeleniumLibrary 3.0, table related keywords only supported ``xpath``, ``css`` and ``sizzle/jquery`` strategies. === Implicit XPath strategy === If the locator starts with ``//`` or ``(//``, the locator is considered to be an XPath expression. In other words, using ``//div`` is equivalent to using explicit ``xpath://div``. Examples: | `Click Element` | //div[@id="foo"]//h1 | | `Click Element` | (//div)[2] | The support for the ``(//`` prefix is new in SeleniumLibrary 3.0. == Using WebElements == In addition to specifying a locator as a string, it is possible to use Selenium's WebElement objects. This requires first getting a WebElement, for example, by using the `Get WebElement` keyword. | ${elem} = | `Get WebElement` | id:example | | `Click Element` | ${elem} | | == Custom locators == If more complex lookups are required than what is provided through the default locators, custom lookup strategies can be created. Using custom locators is a two part process. First, create a keyword that returns a WebElement that should be acted on: | Custom Locator Strategy | [Arguments] | ${browser} | ${locator} | ${tag} | ${constraints} | | | ${element}= | Execute Javascript | return window.document.getElementById('${locator}'); | | | [Return] | ${element} | This keyword is a reimplementation of the basic functionality of the ``id`` locator where ``${browser}`` is a reference to a WebDriver instance and ``${locator}`` is name of the locator strategy. To use this locator it must first be registered by using the `Add Location Strategy` keyword: | `Add Location Strategy` | custom | Custom Locator Strategy | The first argument of `Add Location Strategy` specifies the name of the strategy and it must be unique. After registering the strategy, the usage is the same as with other locators: | `Click Element` | custom:example | See the `Add Location Strategy` keyword for more details. = Timeouts, waits and delays = This section discusses different ways how to wait for elements to appear on web pages and to slow down execution speed otherwise. It also explains the `time format` that can be used when setting various timeouts, waits and delays. == Timeout == SeleniumLibrary contains various keywords that have an optional ``timeout`` argument that specifies how long these keywords should wait for certain events or actions. These keywords include, for example, ``Wait ...`` keywords and keywords related to alerts. Additionally `Execute Async Javascript`. although it does not have ``timeout``, argument, uses timeout to define how long asynchronous JavaScript can run. The default timeout these keywords use can be set globally either by using the `Set Selenium Timeout` keyword or with the ``timeout`` argument when `importing` the library. See `time format` below for supported timeout syntax. == Implicit wait == Implicit wait specifies the maximum time how long Selenium waits when searching for elements. It can be set by using the `Set Selenium Implicit Wait` keyword or with the ``implicit_wait`` argument when `importing` the library. See [http://seleniumhq.org/docs/04_webdriver_advanced.html| Selenium documentation] for more information about this functionality. See `time format` below for supported syntax. == Selenium speed == Selenium execution speed can be slowed down globally by using `Set Selenium speed` keyword. This functionality is designed to be used for demonstrating or debugging purposes. Using it to make sure that elements appear on a page is not a good idea, and the above explained timeouts and waits should be used instead. See `time format` below for supported syntax. == Time format == All timeouts and waits can be given as numbers considered seconds (e.g. ``0.5`` or ``42``) or in Robot Framework's time syntax (e.g. ``1.5 seconds`` or ``1 min 30 s``). For more information about the time syntax see the [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#time-format|Robot Framework User Guide]. = Run-on-failure functionality = SeleniumLibrary has a handy feature that it can automatically execute a keyword if any of its own keywords fails. By default it uses the `Capture Page Screenshot` keyword, but this can be changed either by using the `Register Keyword To Run On Failure` keyword or with the ``run_on_failure`` argument when `importing` the library. It is possible to use any keyword from any imported library or resource file. The run-on-failure functionality can be disabled by using a special value ``NOTHING`` or anything considered false (see `Boolean arguments`) such as ``NONE``. = Boolean arguments = Some keywords accept arguments that are handled as Boolean values true or false. If such an argument is given as a string, it is considered false if it is either empty or case-insensitively equal to ``false``, ``no``, ``off``, ``0`` or ``none``. Other strings are considered true regardless their value, and other argument types are tested using same [https://docs.python.org/2/library/stdtypes.html#truth-value-testing|rules as in Python]. True examples: | `Set Screenshot Directory` | ${RESULTS} | persist=True | # Strings are generally true. | | `Set Screenshot Directory` | ${RESULTS} | persist=yes | # Same as the above. | | `Set Screenshot Directory` | ${RESULTS} | persist=${TRUE} | # Python True is true. | | `Set Screenshot Directory` | ${RESULTS} | persist=${42} | # Numbers other than 0 are true. | False examples: | `Set Screenshot Directory` | ${RESULTS} | persist=False | # String false is false. | | `Set Screenshot Directory` | ${RESULTS} | persist=no | # Also string no is false. | | `Set Screenshot Directory` | ${RESULTS} | persist=NONE | # String NONE is false. | | `Set Screenshot Directory` | ${RESULTS} | persist=${EMPTY} | # Empty string is false. | | `Set Screenshot Directory` | ${RESULTS} | persist=${FALSE} | # Python False is false. | | `Set Screenshot Directory` | ${RESULTS} | persist=${NONE} | # Python None is false. | Note that prior to SeleniumLibrary 3.0, all non-empty strings, including ``false``, ``no`` and ``none``, were considered true. Starting from SeleniumLibrary 4.0, strings ``0`` and ``off`` are considered as false. = Plugins = SeleniumLibrary offers plugins as a way to modify, add library keywords and modify some of the internal functionality without creating new library or hacking the source code. Plugins can be only loaded in the library import, with the `plugins` argument and SeleniumLibrary does not offer way to unload the plugins from the SeleniumLibrary. Plugins is new SeleniumLibrary 4.0 == Importing plugins == Importing plugins is similar when importing Robot Framework [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#importing-libraries|libraries]. It is possible import plugin with using [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#using-physical-path-to-library|physical path] or with [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#using-library-name|plugin name], exactly in same way as importing libraries in Robot Framework. SeleniumLibrary plugins are searched from the same [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#module-search-path|module search path] as Robot Framework searches libraries. It is only possible to import plugins written in Python, other programming languages or Robot Framework test data is not supported. Like with Robot Framework library imports, plugin names are case sensitive and spaces are not supported in the plugin name. It is possible to import multiple plugins at the same time by separating plugins with comma. It is possible to have space before and after the comma. Plugins are imported in the order they defined in the `plugins` argument. If two or more plugins declare the same keyword or modify the same method/attribute in the SeleniumLibrary, the last plugin to perform the changes will overwrite the changes made by other plugins. | Library | SeleniumLibrary | plugins=${CURDIR}/MyPlugin.py | # Imports plugin with physical path | | Library | SeleniumLibrary | plugins=plugins.MyPlugin, plugins.MyOtherPlugin | # Import two plugins with name | Generally speaking, plugin are not any different from the classes that are used to implement keyword in the SeleniumLibrary. Example like with [https://github.com/robotframework/SeleniumLibrary/blob/master/src/SeleniumLibrary/keywords/browsermanagement.py|BrowserManagementKeywords] class inherits the [https://github.com/robotframework/SeleniumLibrary/blob/master/src/SeleniumLibrary/base/librarycomponent.py|LibraryComponent] and uses ``@keyword`` decorator to mark which methods are exposed as keywords. == Plugin arguments == When SeleniumLibrary creates instances from the plugin classes, it will by default initiate the class with a single argument, called ``ctx`` (context). ``ctx`` is the instance of the SeleniummLibrary and it provides access to the common methods and attributes used across in the SeleniumLibrary classes. But is recommended to use wrappers provided by the `LibraryComponent`. It is also possible to provide optional arguments to the plugins. Arguments must be separated with a semicolon from the plugin. SeleniumLibrary will not convert arguments and plugin is responsible for converting the argument to proper types. | Library | SeleniumLibrary | plugins=plugins.Plugin;ArgOne;ArgTwo | # Import two plugins with two arguments: ArgOne and ArgTwo | It is possible to provide variable number of arguments and keywords arguments. Named arguments must be defined first, variable number of arguments as second and keywords arguments as last. All arguments must be separated with semicolon. Example if plugin __init__ is defined like this: | class Plugin(LibraryComponent): | | def __init__(self, ctx, arg, *varargs, **kwargs): Then, for example, it is possible to plugin with these arguments: | Library | SeleniumLibrary | plugins=plugins.Plugin;argument1;varg1;varg2;kw1=kwarg1;kw2=kwarg2 | Then the ``argument1`` is given the ``arg`` in the ``__init__``. The ``varg1`` and ``varg2`` variable number arguments are given to the ``*varargs`` argument in the ``__init__``. Finally, the ``kw1=kwarg1`` and ``kw2=kwarg2`` keyword arguments are given to the ``**kwargs`` in the ``__init__``. As in Python, there can be zero or more variable number and keyword arguments. == Plugin API == Plugins must be implemented as Python classes and plugins must inherit the SeleniumLibrary [https://github.com/robotframework/SeleniumLibrary/blob/master/src/SeleniumLibrary/base/librarycomponent.py|LibraryComponent] class. Plugin __init__ must support at least one argument: ``ctx``. Also optional arguments are supported, see `Plugin arguments` for more details how to provide optional arguments to plugins. SeleniumLibrary uses Robot Framework [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#dynamic-library-api|dynamic library API]. The main difference, when compared to libraries using dynamic library API, is that plugins are not responsible for implementing the dynamic library API. SeleniumLibrary is handling the dynamic library API requirements towards Robot Framework. For plugins this means that methods that implements keywords, must be decorated with ``@keyword`` decorator. The ``@keyword`` decorator can be imported from Robot Framework and used in the following way: | from robot.api.deco import keyword | | class Plugin(LibraryComponent): | | @keyword | def keyword(self): | # Code here to implement a keyword. == Handling failures == SeleniumLibrary does not suppress exception raised during plugin import or during keywords discovery from the plugins. In this case the whole SeleniumLibrary import will fail and SeleniumLibrary keywords can not be used from that import. == LibraryComponent == Although ``ctx`` provides access to the common methods and attributes used in the SeleniumLibrary, the [https://github.com/robotframework/SeleniumLibrary/blob/master/src/SeleniumLibrary/base/librarycomponent.py|LibraryComponent] provides more, an easier and IDE friendly access to the common methods and attributes, Example currently active browser can be found from ``self.ctx.driver``, the ``LibraryComponent`` exposes the browser as: ``self.driver``. Plugin classes must inherit the ``LibraryComponent``. The following methods are available from the ``LibraryComponent`` class: | = Method = | = Description = | | find_element | Finds first element matching ``locator``. | | find_elements | Find all elements matching ``locator``. | | is_text_present | Returns True if text is present in the page. | | is_element_enabled | Returns True if element is enabled. | | is_visible | Returns True if element is visible. | | log_source | Calls method defining the `Log Source` keyword. | | assert_page_contains | Raises AssertionError if element is not found from the page. | | assert_page_not_contains | Raises AssertionError if element is found from the page. | | get_timeout | By default returns SeleniumLibrary ``timeout`` argument value. With argument converts string with Robot Framework ``timestr_to_secs`` to seconds. | | info | Wrapper to ``robot.api.logger.info`` method. | | debug | Wrapper to ``robot.api.logger.debug`` method. | | warn | Wrapper to ``robot.api.logger.warn`` method. | | log | Wrapper to ``robot.api.logger.write`` method. | The following attributes are available from the ``LibraryComponent`` class: | = Attribute = | = Description = | | driver | Currently active browser/WebDriver instance in the SeleniumLibrary. | | drivers | [https://github.com/robotframework/SeleniumLibrary/blob/master/src/SeleniumLibrary/keywords/webdrivertools.py|Cache] for the opened browsers/WebDriver instances. | | element_finder | Read/write attribute for the [https://github.com/robotframework/SeleniumLibrary/blob/master/src/SeleniumLibrary/locators/elementfinder.py|ElementFinder] instance. | | ctx | Instance of the SeleniumLibrary. | | log_dir | Folder where output files are written. | See the [https://github.com/robotframework/SeleniumLibrary/blob/master/src/SeleniumLibrary/__init__.py|SeleniumLibrary init], the [https://github.com/robotframework/SeleniumLibrary/blob/master/src/SeleniumLibrary/base/librarycomponent.py|LibraryComponent] and the [https://github.com/robotframework/SeleniumLibrary/blob/master/src/SeleniumLibrary/base/context.py|ContextAware] classes for further implementation details. == Generating keyword documentation == To separate keywords which are added or modified by plugins, SeleniumLibrary will add ``plugin`` [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#keyword-tags|keyword tag] to all keywords added or modified from plugins. When SeleniumLibrary keyword documentation, with plugins, is generated by [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#library-documentation-tool-libdoc|libdoc] it is easy to separate keywords which are added or modified by plugins. Keyword documentation can be example generated by following command: | python -m robot.libdoc SeleniumLibrary::plugins=/path/to/Plugin.py ./SeleniumLibraryWithPlugin.html = EventFiringWebDriver = The Selenium [https://seleniumhq.github.io/selenium/docs/api/py/webdriver_support/selenium.webdriver.support.event_firing_webdriver.html#module-selenium.webdriver.support.event_firing_webdriver|EventFiringWebDriver] offers listener API for firing events before and after certain Selenium API calls. SeleniumLibrary offers support for Selenium ``EventFiringWebDriver`` listener class, by providing possibility to import the listener class with ``event_firing_webdriver`` argument. Refer to the Selenium ``EventFiringWebDriver`` documentation which Selenium API methods which can fire events and how the Selenium listener class should be implemented. EventFiringWebDriver is new in SeleniumLibrary 4.0 == Importing listener class == Importing Selenium listener class is similar when importing Robot Framework [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#importing-libraries|libraries]. It is possible import Selenium listener class with using [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#using-physical-path-to-library|physical path] or with [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#using-library-name|listener name], exactly in same way as importing libraries in Robot Framework. Selenium listener class is searched from the same [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#module-search-path|module search path] as Robot Framework searches libraries. It is only possible to import listener class written in Python, other programming languages or Robot Framework test data is not supported. Like with Robot Framework library imports, Selenium listener class name is case sensitive and spaces are not supported in the class name. It is only possible to import one Selenium listener class and it is not possible to provide arguments for the Selenium listener class. | Library | SeleniumLibrary | event_firing_webdriver=listner.SeleniumListener | # Improts listener with name. | | Library | SeleniumLibrary | event_firing_webdriver=${CURDIR}/MyListener.py | # Imports listner with physical path. | = Thread support = SeleniumLibrary is not thread safe. This is mainly due because the underlying [https://github.com/SeleniumHQ/selenium/wiki/Frequently-Asked-Questions#q-is-webdriver-thread-safe| Selenium tool is not thread safe] within one browser/driver instance. Because of the limitation in the Selenium side, the keywords or the API provided the SeleniumLibrary is not thread safe. """ ROBOT_LIBRARY_SCOPE = 'GLOBAL' ROBOT_LIBRARY_VERSION = __version__ def __init__(self, timeout=5.0, implicit_wait=0.0, run_on_failure='Capture Page Screenshot', screenshot_root_directory=None, plugins=None, event_firing_webdriver=None): """SeleniumLibrary can be imported with several optional arguments. - ``timeout``: Default value for `timeouts` used with ``Wait ...`` keywords. - ``implicit_wait``: Default value for `implicit wait` used when locating elements. - ``run_on_failure``: Default action for the `run-on-failure functionality`. - ``screenshot_root_directory``: Location where possible screenshots are created. If not given, the directory where the log file is written is used. - ``plugins``: Allows extending the SeleniumLibrary with external Python classes. - ``event_firing_webdriver``: Class for wrapping Selenium with [https://seleniumhq.github.io/selenium/docs/api/py/webdriver_support/selenium.webdriver.support.event_firing_webdriver.html#module-selenium.webdriver.support.event_firing_webdriver|EventFiringWebDriver] """ self.timeout = timestr_to_secs(timeout) self.implicit_wait = timestr_to_secs(implicit_wait) self.speed = 0.0 self.run_on_failure_keyword \ = RunOnFailureKeywords.resolve_keyword(run_on_failure) self._running_on_failure_keyword = False self.screenshot_root_directory = screenshot_root_directory self._element_finder = ElementFinder(self) self._plugin_keywords = [] libraries = [ AlertKeywords(self), BrowserManagementKeywords(self), CookieKeywords(self), ElementKeywords(self), FormElementKeywords(self), FrameKeywords(self), JavaScriptKeywords(self), RunOnFailureKeywords(self), ScreenshotKeywords(self), SelectElementKeywords(self), TableElementKeywords(self), WaitingKeywords(self), WindowKeywords(self) ] if is_truthy(plugins): plugin_libs = self._parse_plugins(plugins) libraries = libraries + plugin_libs self._drivers = WebDriverCache() DynamicCore.__init__(self, libraries) self.ROBOT_LIBRARY_LISTENER = LibraryListener() if is_truthy(event_firing_webdriver): self.event_firing_webdriver = self._parse_listener( event_firing_webdriver) else: self.event_firing_webdriver = None _speed_in_secs = Deprecated('_speed_in_secs', 'speed') _timeout_in_secs = Deprecated('_timeout_in_secs', 'timeout') _implicit_wait_in_secs = Deprecated('_implicit_wait_in_secs', 'implicit_wait') _run_on_failure_keyword = Deprecated('_run_on_failure_keyword', 'run_on_failure_keyword') def run_keyword(self, name, args, kwargs): try: return DynamicCore.run_keyword(self, name, args, kwargs) except Exception: self.failure_occurred() raise def get_keyword_tags(self, name): tags = list(DynamicCore.get_keyword_tags(self, name)) if name in self._plugin_keywords: tags.append('plugin') return tags def register_driver(self, driver, alias): """Add's a `driver` to the library WebDriverCache. :param driver: Instance of the Selenium `WebDriver`. :type driver: selenium.webdriver.remote.webdriver.WebDriver :param alias: Alias given for this `WebDriver` instance. :type alias: str :return: The index of the `WebDriver` instance. :rtype: int """ return self._drivers.register(driver, alias) def failure_occurred(self): """Method that is executed when a SeleniumLibrary keyword fails. By default executes the registered run-on-failure keyword. Libraries extending SeleniumLibrary can overwrite this hook method if they want to provide custom functionality instead. """ if self._running_on_failure_keyword or not self.run_on_failure_keyword: return try: self._running_on_failure_keyword = True BuiltIn().run_keyword(self.run_on_failure_keyword) except Exception as err: logger.warn("Keyword '%s' could not be run on failure: %s" % (self.run_on_failure_keyword, err)) finally: self._running_on_failure_keyword = False @property def driver(self): """Current active driver. :rtype: selenium.webdriver.remote.webdriver.WebDriver :raises SeleniumLibrary.errors.NoOpenBrowser: If browser is not open. """ if not self._drivers.current: raise NoOpenBrowser('No browser is open.') return self._drivers.current def find_element(self, locator, parent=None): """Find element matching `locator`. :param locator: Locator to use when searching the element. See library documentation for the supported locator syntax. :type locator: str or selenium.webdriver.remote.webelement.WebElement :param parent: Optional parent `WebElememt` to search child elements from. By default search starts from the root using `WebDriver`. :type parent: selenium.webdriver.remote.webelement.WebElement :return: Found `WebElement`. :rtype: selenium.webdriver.remote.webelement.WebElement :raises SeleniumLibrary.errors.ElementNotFound: If element not found. """ return self._element_finder.find(locator, parent=parent) def find_elements(self, locator, parent=None): """Find all elements matching `locator`. :param locator: Locator to use when searching the element. See library documentation for the supported locator syntax. :type locator: str or selenium.webdriver.remote.webelement.WebElement :param parent: Optional parent `WebElememt` to search child elements from. By default search starts from the root using `WebDriver`. :type parent: selenium.webdriver.remote.webelement.WebElement :return: list of found `WebElement` or e,mpty if elements are not found. :rtype: list[selenium.webdriver.remote.webelement.WebElement] """ return self._element_finder.find(locator, first_only=False, required=False, parent=parent) @property def _cache(self): warnings.warn( '"SeleniumLibrary._cache" is deprecated, ' 'use public API instead.', DeprecationWarning) return self._drivers def _current_browser(self): warnings.warn( '"SeleniumLibrary._current_browser" is deprecated, ' 'use "SeleniumLibrary.driver" instead.', DeprecationWarning) return self.driver def _run_on_failure(self): warnings.warn( '"SeleniumLibrary._run_on_failure" is deprecated, ' 'use "SeleniumLibrary.failure_occurred" instead.', DeprecationWarning) self.failure_occurred() def _parse_plugins(self, plugins): libraries = [] importer = Importer('test library') for parsed_plugin in self._string_to_modules(plugins): plugin = importer.import_class_or_module(parsed_plugin.module) if not isclass(plugin): message = "Importing test library: '%s' failed." % parsed_plugin.module raise DataError(message) plugin = plugin(self, *parsed_plugin.args, **parsed_plugin.kw_args) if not isinstance(plugin, LibraryComponent): message = 'Plugin does not inherit SeleniumLibrary.base.LibraryComponent' raise PluginError(message) self._store_plugin_keywords(plugin) libraries.append(plugin) return libraries def _parse_listener(self, event_firing_webdriver): listener_module = self._string_to_modules(event_firing_webdriver) listener_count = len(listener_module) if listener_count > 1: message = 'Is is possible import only one listener but there was %s listeners.' % listener_count raise ValueError(message) listener_module = listener_module[0] importer = Importer('test library') listener = importer.import_class_or_module(listener_module.module) if not isclass(listener): message = "Importing test Selenium lister class '%s' failed." % listener_module.module raise DataError(message) return listener def _string_to_modules(self, modules): Module = namedtuple('Module', 'module, args, kw_args') parsed_modules = [] for module in modules.split(','): module = module.strip() module_and_args = module.split(';') module_name = module_and_args.pop(0) kw_args = {} args = [] for argument in module_and_args: if '=' in argument: key, value = argument.split('=') kw_args[key] = value else: args.append(argument) module = Module(module=module_name, args=args, kw_args=kw_args) parsed_modules.append(module) return parsed_modules def _store_plugin_keywords(self, plugin): dynamic_core = DynamicCore([plugin]) self._plugin_keywords.extend(dynamic_core.get_keyword_names())
class SeleniumLibrary(DynamicCore): """SeleniumLibrary is a web testing library for Robot Framework. This document explains how to use keywords provided by SeleniumLibrary. For information about installation, support, and more, please visit the [https://github.com/robotframework/SeleniumLibrary|project pages]. For more information about Robot Framework, see http://robotframework.org. SeleniumLibrary uses the Selenium WebDriver modules internally to control a web browser. See http://seleniumhq.org for more information about Selenium in general. == Table of contents == - `Locating elements` - `Timeouts, waits and delays` - `Run-on-failure functionality` - `Boolean arguments` - `Importing` - `Shortcuts` - `Keywords` = Locating elements = All keywords in SeleniumLibrary that need to interact with an element on a web page take an argument typically named ``locator`` that specifies how to find the element. Most often the locator is given as a string using the locator syntax described below, but `using WebElements` is possible too. == Locator syntax == SeleniumLibrary supports finding elements based on different strategies such as the element id, XPath expressions, or CSS selectors. The strategy can either be explicitly specified with a prefix or the strategy can be implicit. === Default locator strategy === By default locators are considered to use the keyword specific default locator strategy. All keywords support finding elements based on ``id`` and ``name`` attributes, but some keywords support additional attributes or other values that make sense in their context. For example, `Click Link` supports the ``href`` attribute and the link text and addition to the normal ``id`` and ``name``. Examples: | `Click Element` | example | # Match based on ``id`` or ``name``. | | `Click Link` | example | # Match also based on link text and ``href``. | | `Click Button` | example | # Match based on ``id``, ``name`` or ``value``. | If a locator accidentally starts with a prefix recognized as `explicit locator strategy` or `implicit XPath strategy`, it is possible to use the explicit ``default`` prefix to enable the default strategy. Examples: | `Click Element` | name:foo | # Find element with name ``foo``. | | `Click Element` | default:name:foo | # Use default strategy with value ``name:foo``. | | `Click Element` | //foo | # Find element using XPath ``//foo``. | | `Click Element` | default: //foo | # Use default strategy with value ``//foo``. | === Explicit locator strategy === The explicit locator strategy is specified with a prefix using either syntax ``strategy:value`` or ``strategy=value``. The former syntax is preferred, because the latter is identical to Robot Framework's [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#named-argument-syntax| named argument syntax] and that can cause problems. Spaces around the separator are ignored, so ``id:foo``, ``id: foo`` and ``id : foo`` are all equivalent. Locator strategies that are supported by default are listed in the table below. In addition to them, it is possible to register `custom locators`. | = Strategy = | = Match based on = | = Example = | | id | Element ``id``. | ``id:example`` | | name | ``name`` attribute. | ``name:example`` | | identifier | Either ``id`` or ``name``. | ``identifier:example`` | | class | Element ``class``. | ``class:example`` | | tag | Tag name. | ``tag:div`` | | xpath | XPath expression. | ``xpath://div[@id="example"]`` | | css | CSS selector. | ``css:div#example`` | | dom | DOM expression. | ``dom:document.images[5]`` | | link | Exact text a link has. | ``link:The example`` | | partial link | Partial link text. | ``partial link:he ex`` | | sizzle | Sizzle selector provided by jQuery. | ``sizzle:div.example`` | | jquery | Same as the above. | ``jquery:div.example`` | | default | Keyword specific default behavior. | ``default:example`` | See the `Default locator strategy` section below for more information about how the default strategy works. Using the explicit ``default`` prefix is only necessary if the locator value itself accidentally matches some of the explicit strategies. Different locator strategies have different pros and cons. Using ids, either explicitly like ``id:foo`` or by using the `default locator strategy` simply like ``foo``, is recommended when possible, because the syntax is simple and locating elements by an id is fast for browsers. If an element does not have an id or the id is not stable, other solutions need to be used. If an element has a unique tag name or class, using ``tag``, ``class`` or ``css`` strategy like ``tag:h1``, ``class:example`` or ``css:h1.example`` is often an easy solution. In more complex cases using XPath expressions is typically the best approach. They are very powerful but a downside is that they can also get complex. Examples: | `Click Element` | id:foo | # Element with id 'foo'. | | `Click Element` | css:div#foo h1 | # h1 element under div with id 'foo'. | | `Click Element` | xpath: //div[@id="foo"]//h1 | # Same as the above using XPath, not CSS. | | `Click Element` | xpath: //*[contains(text(), "example")] | # Element containing text 'example'. | *NOTE:* - The ``strategy:value`` syntax is only supported by SeleniumLibrary 3.0 and newer. - Using the ``sizzle`` strategy or its alias ``jquery`` requires that the system under test contains the jQuery library. - Prior to SeleniumLibrary 3.0, table related keywords only supported ``xpath``, ``css`` and ``sizzle/jquery`` strategies. === Implicit XPath strategy === If the locator starts with ``//`` or ``(//``, the locator is considered to be an XPath expression. In other words, using ``//div`` is equivalent to using explicit ``xpath://div``. Examples: | `Click Element` | //div[@id="foo"]//h1 | | `Click Element` | (//div)[2] | The support for the ``(//`` prefix is new in SeleniumLibrary 3.0. == Using WebElements == In addition to specifying a locator as a string, it is possible to use Selenium's WebElement objects. This requires first getting a WebElement, for example, by using the `Get WebElement` keyword. | ${elem} = | `Get WebElement` | id:example | | `Click Element` | ${elem} | | == Custom locators == If more complex lookups are required than what is provided through the default locators, custom lookup strategies can be created. Using custom locators is a two part process. First, create a keyword that returns a WebElement that should be acted on: | Custom Locator Strategy | [Arguments] | ${browser} | ${strategy} | ${tag} | ${constraints} | | | ${element}= | Execute Javascript | return window.document.getElementById('${criteria}'); | | | [Return] | ${element} | This keyword is a reimplementation of the basic functionality of the ``id`` locator where ``${browser}`` is a reference to a WebDriver instance and ``${strategy}`` is name of the locator strategy. To use this locator it must first be registered by using the `Add Location Strategy` keyword: | `Add Location Strategy` | custom | Custom Locator Strategy | The first argument of `Add Location Strategy` specifies the name of the strategy and it must be unique. After registering the strategy, the usage is the same as with other locators: | `Click Element` | custom:example | See the `Add Location Strategy` keyword for more details. = Timeouts, waits and delays = This section discusses different ways how to wait for elements to appear on web pages and to slow down execution speed otherwise. It also explains the `time format` that can be used when setting various timeouts, waits and delays. == Timeout == SeleniumLibrary contains various keywords that have an optional ``timeout`` argument that specifies how long these keywords should wait for certain events or actions. These keywords include, for example, ``Wait ...`` keywords and keywords related to alerts. The default timeout these keywords use can be set globally either by using the `Set Selenium Timeout` keyword or with the ``timeout`` argument when `importing` the library. See `time format` below for supported timeout syntax. == Implicit wait == Implicit wait specifies the maximum time how long Selenium waits when searching for elements. It can be set by using the `Set Selenium Implicit Wait` keyword or with the ``implicit_wait`` argument when `importing` the library. See [http://seleniumhq.org/docs/04_webdriver_advanced.html| Selenium documentation] for more information about this functionality. See `time format` below for supported syntax. == Selenium speed == Selenium execution speed can be slowed down globally by using `Set Selenium speed` keyword. This functionality is designed to be used for demonstrating or debugging purposes. Using it to make sure that elements appear on a page is not a good idea, and the above explained timeouts and waits should be used instead. See `time format` below for supported syntax. == Time format == All timeouts and waits can be given as numbers considered seconds (e.g. ``0.5`` or ``42``) or in Robot Framework's time syntax (e.g. ``1.5 seconds`` or ``1 min 30 s``). For more information about the time syntax see the [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#time-format|Robot Framework User Guide]. = Run-on-failure functionality = SeleniumLibrary has a handy feature that it can automatically execute a keyword if any of its own keywords fails. By default it uses the `Capture Page Screenshot` keyword, but this can be changed either by using the `Register Keyword To Run On Failure` keyword or with the ``run_on_failure`` argument when `importing` the library. It is possible to use any keyword from any imported library or resource file. The run-on-failure functionality can be disabled by using a special value ``NOTHING`` or anything considered false (see `Boolean arguments`) such as ``NONE``. = Boolean arguments = Some keywords accept arguments that are handled as Boolean values true or false. If such an argument is given as a string, it is considered false if it is either empty or case-insensitively equal to ``false``, ``no`` or ``none``. Other strings are considered true regardless their value, and other argument types are tested using same [https://docs.python.org/2/library/stdtypes.html#truth-value-testing|rules as in Python]. True examples: | `Set Screenshot Directory` | ${RESULTS} | persist=True | # Strings are generally true. | | `Set Screenshot Directory` | ${RESULTS} | persist=yes | # Same as the above. | | `Set Screenshot Directory` | ${RESULTS} | persist=${TRUE} | # Python True is true. | | `Set Screenshot Directory` | ${RESULTS} | persist=${42} | # Numbers other than 0 are true. | False examples: | `Set Screenshot Directory` | ${RESULTS} | persist=False | # String false is false. | | `Set Screenshot Directory` | ${RESULTS} | persist=no | # Also string no is false. | | `Set Screenshot Directory` | ${RESULTS} | persist=NONE | # String NONE is false. | | `Set Screenshot Directory` | ${RESULTS} | persist=${EMPTY} | # Empty string is false. | | `Set Screenshot Directory` | ${RESULTS} | persist=${FALSE} | # Python False is false. | | `Set Screenshot Directory` | ${RESULTS} | persist=${NONE} | # Python None is false. | Note that prior to SeleniumLibrary 3.0, all non-empty strings, including ``false``, ``no`` and ``none``, were considered true. """ ROBOT_LIBRARY_SCOPE = 'GLOBAL' ROBOT_LIBRARY_VERSION = __version__ def __init__(self, timeout=5.0, implicit_wait=0.0, run_on_failure='Capture Page Screenshot', screenshot_root_directory=None): """SeleniumLibrary can be imported with several optional arguments. - ``timeout``: Default value for `timeouts` used with ``Wait ...`` keywords. - ``implicit_wait``: Default value for `implicit wait` used when locating elements. - ``run_on_failure``: Default action for the `run-on-failure functionality`. - ``screenshot_root_directory``: Location where possible screenshots are created. If not given, the directory where the log file is written is used. """ self.timeout = timestr_to_secs(timeout) self.implicit_wait = timestr_to_secs(implicit_wait) self.speed = 0.0 self.run_on_failure_keyword \ = RunOnFailureKeywords.resolve_keyword(run_on_failure) self._running_on_failure_keyword = False self.screenshot_root_directory = screenshot_root_directory libraries = [ AlertKeywords(self), BrowserManagementKeywords(self), CookieKeywords(self), ElementKeywords(self), FormElementKeywords(self), FrameKeywords(self), JavaScriptKeywords(self), RunOnFailureKeywords(self), ScreenshotKeywords(self), SelectElementKeywords(self), TableElementKeywords(self), WaitingKeywords(self), WindowKeywords(self) ] self._drivers = WebDriverCache() DynamicCore.__init__(self, libraries) self.ROBOT_LIBRARY_LISTENER = LibraryListener() self._element_finder = ElementFinder(self) _speed_in_secs = Deprecated('_speed_in_secs', 'speed') _timeout_in_secs = Deprecated('_timeout_in_secs', 'timeout') _implicit_wait_in_secs = Deprecated('_implicit_wait_in_secs', 'implicit_wait') _run_on_failure_keyword = Deprecated('_run_on_failure_keyword', 'run_on_failure_keyword') def run_keyword(self, name, args, kwargs): try: return DynamicCore.run_keyword(self, name, args, kwargs) except Exception: self.failure_occurred() raise def register_driver(self, driver, alias): """Add's a `driver` to the library WebDriverCache. :param driver: Instance of the Selenium `WebDriver`. :type driver: selenium.webdriver.remote.webdriver.WebDriver :param alias: Alias given for this `WebDriver` instance. :type alias: str :return: The index of the `WebDriver` instance. :rtype: int """ return self._drivers.register(driver, alias) def failure_occurred(self): """Method that is executed when a SeleniumLibrary keyword fails. By default executes the registered run-on-failure keyword. Libraries extending SeleniumLibrary can overwrite this hook method if they want to provide custom functionality instead. """ if self._running_on_failure_keyword or not self.run_on_failure_keyword: return try: self._running_on_failure_keyword = True BuiltIn().run_keyword(self.run_on_failure_keyword) except Exception as err: logger.warn("Keyword '%s' could not be run on failure: %s" % (self.run_on_failure_keyword, err)) finally: self._running_on_failure_keyword = False @property def driver(self): """Current active driver. :rtype: selenium.webdriver.remote.webdriver.WebDriver :raises SeleniumLibrary.errors.NoOpenBrowser: If browser is not open. """ if not self._drivers.current: raise NoOpenBrowser('No browser is open.') return self._drivers.current def find_element(self, locator, parent=None): """Find element matching `locator`. :param locator: Locator to use when searching the element. See library documentation for the supported locator syntax. :type locator: str or selenium.webdriver.remote.webelement.WebElement :param parent: Optional parent `WebElememt` to search child elements from. By default search starts from the root using `WebDriver`. :type parent: selenium.webdriver.remote.webelement.WebElement :return: Found `WebElement`. :rtype: selenium.webdriver.remote.webelement.WebElement :raises SeleniumLibrary.errors.ElementNotFound: If element not found. """ return self._element_finder.find(locator, parent=parent) def find_elements(self, locator, parent=None): """Find all elements matching `locator`. :param locator: Locator to use when searching the element. See library documentation for the supported locator syntax. :type locator: str or selenium.webdriver.remote.webelement.WebElement :param parent: Optional parent `WebElememt` to search child elements from. By default search starts from the root using `WebDriver`. :type parent: selenium.webdriver.remote.webelement.WebElement :return: list of found `WebElement` or e,mpty if elements are not found. :rtype: list[selenium.webdriver.remote.webelement.WebElement] """ return self._element_finder.find(locator, first_only=False, required=False, parent=parent) @property def _cache(self): warnings.warn( '"SeleniumLibrary._cache" is deprecated, ' 'use public API instead.', DeprecationWarning) return self._drivers def _current_browser(self): warnings.warn( '"SeleniumLibrary._current_browser" is deprecated, ' 'use "SeleniumLibrary.driver" instead.', DeprecationWarning) return self.driver def _run_on_failure(self): warnings.warn( '"SeleniumLibrary._run_on_failure" is deprecated, ' 'use "SeleniumLibrary.failure_occurred" instead.', DeprecationWarning) self.failure_occurred()
def test_resolve_alias_or_index(self): cache = WebDriverCache() cache.register(mock(), 'foo') cache.register(mock()) cache.register(mock()) index = cache.get_index('foo') self.assertEqual(index, 1) index = cache.get_index(1) self.assertEqual(index, 1) index = cache.get_index(3) self.assertEqual(index, 3) index = cache.get_index(None) self.assertEqual(index, None) index = cache.get_index('None') self.assertEqual(index, None)
def test_same_alias_new_browser(self): cache = WebDriverCache() cache.close() index = cache.get_index("bar") self.assertEqual(index, None)
class SeleniumLibrary(DynamicCore): """SeleniumLibrary is a web testing library for Robot Framework. This document explains how to use keywords provided by SeleniumLibrary. For information about installation, support, and more, please visit the [https://github.com/robotframework/SeleniumLibrary|project pages]. For more information about Robot Framework, see http://robotframework.org. SeleniumLibrary uses the Selenium WebDriver modules internally to control a web browser. See http://seleniumhq.org for more information about Selenium in general and SeleniumLibrary README.rst [https://github.com/robotframework/SeleniumLibrary#browser-drivers|Browser drivers chapter] for more details about WebDriver binary installation. %TOC% = Locating elements = All keywords in SeleniumLibrary that need to interact with an element on a web page take an argument typically named ``locator`` that specifies how to find the element. Most often the locator is given as a string using the locator syntax described below, but `using WebElements` is possible too. == Locator syntax == SeleniumLibrary supports finding elements based on different strategies such as the element id, XPath expressions, or CSS selectors. The strategy can either be explicitly specified with a prefix or the strategy can be implicit. === Default locator strategy === By default, locators are considered to use the keyword specific default locator strategy. All keywords support finding elements based on ``id`` and ``name`` attributes, but some keywords support additional attributes or other values that make sense in their context. For example, `Click Link` supports the ``href`` attribute and the link text and addition to the normal ``id`` and ``name``. Examples: | `Click Element` | example | # Match based on ``id`` or ``name``. | | `Click Link` | example | # Match also based on link text and ``href``. | | `Click Button` | example | # Match based on ``id``, ``name`` or ``value``. | If a locator accidentally starts with a prefix recognized as `explicit locator strategy` or `implicit XPath strategy`, it is possible to use the explicit ``default`` prefix to enable the default strategy. Examples: | `Click Element` | name:foo | # Find element with name ``foo``. | | `Click Element` | default:name:foo | # Use default strategy with value ``name:foo``. | | `Click Element` | //foo | # Find element using XPath ``//foo``. | | `Click Element` | default: //foo | # Use default strategy with value ``//foo``. | === Explicit locator strategy === The explicit locator strategy is specified with a prefix using either syntax ``strategy:value`` or ``strategy=value``. The former syntax is preferred because the latter is identical to Robot Framework's [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#named-argument-syntax| named argument syntax] and that can cause problems. Spaces around the separator are ignored, so ``id:foo``, ``id: foo`` and ``id : foo`` are all equivalent. Locator strategies that are supported by default are listed in the table below. In addition to them, it is possible to register `custom locators`. | = Strategy = | = Match based on = | = Example = | | id | Element ``id``. | ``id:example`` | | name | ``name`` attribute. | ``name:example`` | | identifier | Either ``id`` or ``name``. | ``identifier:example`` | | class | Element ``class``. | ``class:example`` | | tag | Tag name. | ``tag:div`` | | xpath | XPath expression. | ``xpath://div[@id="example"]`` | | css | CSS selector. | ``css:div#example`` | | dom | DOM expression. | ``dom:document.images[5]`` | | link | Exact text a link has. | ``link:The example`` | | partial link | Partial link text. | ``partial link:he ex`` | | sizzle | Sizzle selector deprecated. | ``sizzle:div.example`` | | jquery | jQuery expression. | ``jquery:div.example`` | | default | Keyword specific default behavior. | ``default:example`` | See the `Default locator strategy` section below for more information about how the default strategy works. Using the explicit ``default`` prefix is only necessary if the locator value itself accidentally matches some of the explicit strategies. Different locator strategies have different pros and cons. Using ids, either explicitly like ``id:foo`` or by using the `default locator strategy` simply like ``foo``, is recommended when possible, because the syntax is simple and locating elements by id is fast for browsers. If an element does not have an id or the id is not stable, other solutions need to be used. If an element has a unique tag name or class, using ``tag``, ``class`` or ``css`` strategy like ``tag:h1``, ``class:example`` or ``css:h1.example`` is often an easy solution. In more complex cases using XPath expressions is typically the best approach. They are very powerful but a downside is that they can also get complex. Examples: | `Click Element` | id:foo | # Element with id 'foo'. | | `Click Element` | css:div#foo h1 | # h1 element under div with id 'foo'. | | `Click Element` | xpath: //div[@id="foo"]//h1 | # Same as the above using XPath, not CSS. | | `Click Element` | xpath: //*[contains(text(), "example")] | # Element containing text 'example'. | *NOTE:* - The ``strategy:value`` syntax is only supported by SeleniumLibrary 3.0 and newer. - Using the ``sizzle`` strategy or its alias ``jquery`` requires that the system under test contains the jQuery library. - Prior to SeleniumLibrary 3.0, table related keywords only supported ``xpath``, ``css`` and ``sizzle/jquery`` strategies. === Implicit XPath strategy === If the locator starts with ``//`` or ``(//``, the locator is considered to be an XPath expression. In other words, using ``//div`` is equivalent to using explicit ``xpath://div``. Examples: | `Click Element` | //div[@id="foo"]//h1 | | `Click Element` | (//div)[2] | The support for the ``(//`` prefix is new in SeleniumLibrary 3.0. === Chaining locators === It is possible chain multiple locators together as single locator. Each chained locator must start with locator strategy. Chained locators must be separated with single space, two greater than characters and followed with space. It is also possible mix different locator strategies, example css or xpath. Also a list can also be used to specify multiple locators. This is useful, is some part of locator would match as the locator separator but it should not. Or if there is need to existing WebElement as locator. Although all locators support chaining, some locator strategies do not abey the chaining. This is because some locator strategies use JavaScript to find elements and JavaScript is executed for the whole browser context and not for the element found be the previous locator. Chaining is supported by locator strategies which are based on Selenium API, like `xpath` or `css`, but example chaining is not supported by `sizzle` or `jquery Examples: | `Click Element` | css:.bar >> xpath://a | # To find a link which is present after an element with class "bar" | List examples: | ${locator_list} = | `Create List` | css:div#div_id | xpath://*[text(), " >> "] | | `Page Should Contain Element` | ${locator_list} | | | | ${element} = | Get WebElement | xpath://*[text(), " >> "] | | | ${locator_list} = | `Create List` | css:div#div_id | ${element} | | `Page Should Contain Element` | ${locator_list} | | | Chaining locators in new in SeleniumLibrary 5.0 == Using WebElements == In addition to specifying a locator as a string, it is possible to use Selenium's WebElement objects. This requires first getting a WebElement, for example, by using the `Get WebElement` keyword. | ${elem} = | `Get WebElement` | id:example | | `Click Element` | ${elem} | | == Custom locators == If more complex lookups are required than what is provided through the default locators, custom lookup strategies can be created. Using custom locators is a two part process. First, create a keyword that returns a WebElement that should be acted on: | Custom Locator Strategy | [Arguments] | ${browser} | ${locator} | ${tag} | ${constraints} | | | ${element}= | Execute Javascript | return window.document.getElementById('${locator}'); | | | [Return] | ${element} | This keyword is a reimplementation of the basic functionality of the ``id`` locator where ``${browser}`` is a reference to a WebDriver instance and ``${locator}`` is the name of the locator strategy. To use this locator, it must first be registered by using the `Add Location Strategy` keyword: | `Add Location Strategy` | custom | Custom Locator Strategy | The first argument of `Add Location Strategy` specifies the name of the strategy and it must be unique. After registering the strategy, the usage is the same as with other locators: | `Click Element` | custom:example | See the `Add Location Strategy` keyword for more details. = Browser and Window = There is different conceptual meaning when SeleniumLibrary talks about windows or browsers. This chapter explains those differences. == Browser == When `Open Browser` or `Create WebDriver` keyword is called, it will create a new Selenium WebDriver instance by using the [https://www.seleniumhq.org/docs/03_webdriver.jsp|Selenium WebDriver] API. In SeleniumLibrary terms, a new browser is created. It is possible to start multiple independent browsers (Selenium Webdriver instances) at the same time, by calling `Open Browser` or `Create WebDriver` multiple times. These browsers are usually independent of each other and do not share data like cookies, sessions or profiles. Typically when the browser starts, it creates a single window which is shown to the user. == Window == Windows are the part of a browser that loads the web site and presents it to the user. All content of the site is the content of the window. Windows are children of a browser. In SeleniumLibrary browser is a synonym for WebDriver instance. One browser may have multiple windows. Windows can appear as tabs, as separate windows or pop-ups with different position and size. Windows belonging to the same browser typically share the sessions detail, like cookies. If there is a need to separate sessions detail, example login with two different users, two browsers (Selenium WebDriver instances) must be created. New windows can be opened example by the application under test or by example `Execute Javascript` keyword: | `Execute Javascript` window.open() # Opens a new window with location about:blank The example below opens multiple browsers and windows, to demonstrate how the different keywords can be used to interact with browsers, and windows attached to these browsers. Structure: | BrowserA | Window 1 (location=https://robotframework.org/) | Window 2 (location=https://robocon.io/) | Window 3 (location=https://github.com/robotframework/) | | BrowserB | Window 1 (location=https://github.com/) Example: | `Open Browser` | https://robotframework.org | ${BROWSER} | alias=BrowserA | # BrowserA with first window is opened. | | `Execute Javascript` | window.open() | | | # In BrowserA second window is opened. | | `Switch Window` | locator=NEW | | | # Switched to second window in BrowserA | | `Go To` | https://robocon.io | | | # Second window navigates to robocon site. | | `Execute Javascript` | window.open() | | | # In BrowserA third window is opened. | | ${handle} | `Switch Window` | locator=NEW | | # Switched to third window in BrowserA | | `Go To` | https://github.com/robotframework/ | | | # Third windows goes to robot framework github site. | | `Open Browser` | https://github.com | ${BROWSER} | alias=BrowserB | # BrowserB with first windows is opened. | | ${location} | `Get Location` | | | # ${location} is: https://www.github.com | | `Switch Window` | ${handle} | browser=BrowserA | | # BrowserA second windows is selected. | | ${location} | `Get Location` | | | # ${location} = https://robocon.io/ | | @{locations 1} | `Get Locations` | | | # By default, lists locations under the currectly active browser (BrowserA). | | @{locations 2} | `Get Locations` | browser=ALL | | # By using browser=ALL argument keyword list all locations from all browsers. | The above example, @{locations 1} contains the following items: https://robotframework.org/, https://robocon.io/ and https://github.com/robotframework/'. The @{locations 2} contains the following items: https://robotframework.org/, https://robocon.io/, https://github.com/robotframework/' and 'https://github.com/. = Timeouts, waits, and delays = This section discusses different ways how to wait for elements to appear on web pages and to slow down execution speed otherwise. It also explains the `time format` that can be used when setting various timeouts, waits, and delays. == Timeout == SeleniumLibrary contains various keywords that have an optional ``timeout`` argument that specifies how long these keywords should wait for certain events or actions. These keywords include, for example, ``Wait ...`` keywords and keywords related to alerts. Additionally `Execute Async Javascript`. Although it does not have ``timeout``, argument, uses a timeout to define how long asynchronous JavaScript can run. The default timeout these keywords use can be set globally either by using the `Set Selenium Timeout` keyword or with the ``timeout`` argument when `importing` the library. See `time format` below for supported timeout syntax. == Implicit wait == Implicit wait specifies the maximum time how long Selenium waits when searching for elements. It can be set by using the `Set Selenium Implicit Wait` keyword or with the ``implicit_wait`` argument when `importing` the library. See [https://www.seleniumhq.org/docs/04_webdriver_advanced.jsp| Selenium documentation] for more information about this functionality. See `time format` below for supported syntax. == Selenium speed == Selenium execution speed can be slowed down globally by using `Set Selenium speed` keyword. This functionality is designed to be used for demonstrating or debugging purposes. Using it to make sure that elements appear on a page is not a good idea. The above-explained timeouts and waits should be used instead. See `time format` below for supported syntax. == Time format == All timeouts and waits can be given as numbers considered seconds (e.g. ``0.5`` or ``42``) or in Robot Framework's time syntax (e.g. ``1.5 seconds`` or ``1 min 30 s``). For more information about the time syntax see the [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#time-format|Robot Framework User Guide]. = Run-on-failure functionality = SeleniumLibrary has a handy feature that it can automatically execute a keyword if any of its own keywords fails. By default, it uses the `Capture Page Screenshot` keyword, but this can be changed either by using the `Register Keyword To Run On Failure` keyword or with the ``run_on_failure`` argument when `importing` the library. It is possible to use any keyword from any imported library or resource file. The run-on-failure functionality can be disabled by using a special value ``NOTHING`` or anything considered false (see `Boolean arguments`) such as ``NONE``. = Boolean arguments = Starting from 5.0 SeleniumLibrary relies on Robot Framework to perform the boolean conversion based on keyword arguments [https://docs.python.org/3/library/typing.html|type hint]. More details in Robot Framework [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#supported-conversions|user guide] Please note SeleniumLibrary 3 and 4 did have own custom methods to covert arguments to boolean values. = EventFiringWebDriver = The SeleniumLibrary offers support for [https://seleniumhq.github.io/selenium/docs/api/py/webdriver_support/selenium.webdriver.support.event_firing_webdriver.html#module-selenium.webdriver.support.event_firing_webdriver|EventFiringWebDriver]. See the Selenium and SeleniumLibrary [https://github.com/robotframework/SeleniumLibrary/blob/master/docs/extending/extending.rst#EventFiringWebDriver|EventFiringWebDriver support] documentation for further details. EventFiringWebDriver is new in SeleniumLibrary 4.0 = Thread support = SeleniumLibrary is not thread-safe. This is mainly due because the underlying [https://github.com/SeleniumHQ/selenium/wiki/Frequently-Asked-Questions#q-is-webdriver-thread-safe| Selenium tool is not thread-safe] within one browser/driver instance. Because of the limitation in the Selenium side, the keywords or the API provided by the SeleniumLibrary is not thread-safe. = Plugins = SeleniumLibrary offers plugins as a way to modify and add library keywords and modify some of the internal functionality without creating a new library or hacking the source code. See [https://github.com/robotframework/SeleniumLibrary/blob/master/docs/extending/extending.rst#Plugins|plugin API] documentation for further details. Plugin API is new SeleniumLibrary 4.0 """ ROBOT_LIBRARY_SCOPE = "GLOBAL" ROBOT_LIBRARY_VERSION = __version__ def __init__( self, timeout=timedelta(seconds=5), implicit_wait=timedelta(seconds=0), run_on_failure="Capture Page Screenshot", screenshot_root_directory: Optional[str] = None, plugins: Optional[str] = None, event_firing_webdriver: Optional[str] = None, ): """SeleniumLibrary can be imported with several optional arguments. - ``timeout``: Default value for `timeouts` used with ``Wait ...`` keywords. - ``implicit_wait``: Default value for `implicit wait` used when locating elements. - ``run_on_failure``: Default action for the `run-on-failure functionality`. - ``screenshot_root_directory``: Path to folder where possible screenshots are created or EMBED. See `Set Screenshot Directory` keyword for further details about EMBED. If not given, the directory where the log file is written is used. - ``plugins``: Allows extending the SeleniumLibrary with external Python classes. - ``event_firing_webdriver``: Class for wrapping Selenium with [https://seleniumhq.github.io/selenium/docs/api/py/webdriver_support/selenium.webdriver.support.event_firing_webdriver.html#module-selenium.webdriver.support.event_firing_webdriver|EventFiringWebDriver] """ self.timeout = _convert_timeout(timeout) self.implicit_wait = _convert_timeout(implicit_wait) self.speed = 0.0 self.run_on_failure_keyword = RunOnFailureKeywords.resolve_keyword( run_on_failure) self._running_on_failure_keyword = False self.screenshot_root_directory = screenshot_root_directory self._resolve_screenshot_root_directory() self._element_finder = ElementFinder(self) self._plugin_keywords = [] libraries = [ AlertKeywords(self), BrowserManagementKeywords(self), CookieKeywords(self), ElementKeywords(self), FormElementKeywords(self), FrameKeywords(self), JavaScriptKeywords(self), RunOnFailureKeywords(self), ScreenshotKeywords(self), SelectElementKeywords(self), TableElementKeywords(self), WaitingKeywords(self), WindowKeywords(self), ] self.ROBOT_LIBRARY_LISTENER = LibraryListener() self._running_keyword = None self.event_firing_webdriver = None if is_truthy(event_firing_webdriver): self.event_firing_webdriver = self._parse_listener( event_firing_webdriver) self._plugins = [] if is_truthy(plugins): plugin_libs = self._parse_plugins(plugins) self._plugins = plugin_libs libraries = libraries + plugin_libs self._drivers = WebDriverCache() DynamicCore.__init__(self, libraries) def run_keyword(self, name: str, args: tuple, kwargs: dict): try: return DynamicCore.run_keyword(self, name, args, kwargs) except Exception: self.failure_occurred() raise def get_keyword_tags(self, name: str) -> list: tags = list(DynamicCore.get_keyword_tags(self, name)) if name in self._plugin_keywords: tags.append("plugin") return tags def get_keyword_documentation(self, name: str) -> str: if name == "__intro__": return self._get_intro_documentation() return DynamicCore.get_keyword_documentation(self, name) def _parse_plugin_doc(self): Doc = namedtuple("Doc", "doc, name") for plugin in self._plugins: yield Doc( doc=getdoc(plugin) or "No plugin documentation found.", name=plugin.__class__.__name__, ) def _get_intro_documentation(self): intro = DynamicCore.get_keyword_documentation(self, "__intro__") for plugin_doc in self._parse_plugin_doc(): intro = f"{intro}\n\n" intro = f"{intro}= Plugin: {plugin_doc.name} =\n\n" intro = f"{intro}{plugin_doc.doc}" return intro def register_driver(self, driver: WebDriver, alias: str): """Add's a `driver` to the library WebDriverCache. :param driver: Instance of the Selenium `WebDriver`. :type driver: selenium.webdriver.remote.webdriver.WebDriver :param alias: Alias given for this `WebDriver` instance. :type alias: str :return: The index of the `WebDriver` instance. :rtype: int """ return self._drivers.register(driver, alias) def failure_occurred(self): """Method that is executed when a SeleniumLibrary keyword fails. By default, executes the registered run-on-failure keyword. Libraries extending SeleniumLibrary can overwrite this hook method if they want to provide custom functionality instead. """ if self._running_on_failure_keyword or not self.run_on_failure_keyword: return try: self._running_on_failure_keyword = True BuiltIn().run_keyword(self.run_on_failure_keyword) except Exception as err: logger.warn( f"Keyword '{self.run_on_failure_keyword}' could not be run on failure: {err}" ) finally: self._running_on_failure_keyword = False @property def driver(self) -> WebDriver: """Current active driver. :rtype: selenium.webdriver.remote.webdriver.WebDriver :raises SeleniumLibrary.errors.NoOpenBrowser: If browser is not open. """ if not self._drivers.current: raise NoOpenBrowser("No browser is open.") return self._drivers.current def find_element(self, locator: str, parent: Optional[WebElement] = None) -> WebElement: """Find element matching `locator`. :param locator: Locator to use when searching the element. See library documentation for the supported locator syntax. :type locator: str or selenium.webdriver.remote.webelement.WebElement :param parent: Optional parent `WebElememt` to search child elements from. By default, search starts from the root using `WebDriver`. :type parent: selenium.webdriver.remote.webelement.WebElement :return: Found `WebElement`. :rtype: selenium.webdriver.remote.webelement.WebElement :raises SeleniumLibrary.errors.ElementNotFound: If element not found. """ return self._element_finder.find(locator, parent=parent) def find_elements(self, locator: str, parent: WebElement = None) -> List[WebElement]: """Find all elements matching `locator`. :param locator: Locator to use when searching the element. See library documentation for the supported locator syntax. :type locator: str or selenium.webdriver.remote.webelement.WebElement :param parent: Optional parent `WebElememt` to search child elements from. By default, search starts from the root using `WebDriver`. :type parent: selenium.webdriver.remote.webelement.WebElement :return: list of found `WebElement` or e,mpty if elements are not found. :rtype: list[selenium.webdriver.remote.webelement.WebElement] """ return self._element_finder.find(locator, first_only=False, required=False, parent=parent) def _parse_plugins(self, plugins): libraries = [] importer = Importer("test library") for parsed_plugin in self._string_to_modules(plugins): plugin = importer.import_class_or_module(parsed_plugin.module) if not isclass(plugin): message = f"Importing test library: '{parsed_plugin.module}' failed." raise DataError(message) plugin = plugin(self, *parsed_plugin.args, **parsed_plugin.kw_args) if not isinstance(plugin, LibraryComponent): message = ( "Plugin does not inherit SeleniumLibrary.base.LibraryComponent" ) raise PluginError(message) self._store_plugin_keywords(plugin) libraries.append(plugin) return libraries def _parse_listener(self, event_firing_webdriver): listener_module = self._string_to_modules(event_firing_webdriver) listener_count = len(listener_module) if listener_count > 1: message = f"Is is possible import only one listener but there was {listener_count} listeners." raise ValueError(message) listener_module = listener_module[0] importer = Importer("test library") listener = importer.import_class_or_module(listener_module.module) if not isclass(listener): message = f"Importing test Selenium lister class '{listener_module.module}' failed." raise DataError(message) return listener def _string_to_modules(self, modules): Module = namedtuple("Module", "module, args, kw_args") parsed_modules = [] for module in modules.split(","): module = module.strip() module_and_args = module.split(";") module_name = module_and_args.pop(0) kw_args = {} args = [] for argument in module_and_args: if "=" in argument: key, value = argument.split("=") kw_args[key] = value else: args.append(argument) module = Module(module=module_name, args=args, kw_args=kw_args) parsed_modules.append(module) return parsed_modules def _store_plugin_keywords(self, plugin): dynamic_core = DynamicCore([plugin]) self._plugin_keywords.extend(dynamic_core.get_keyword_names()) def _resolve_screenshot_root_directory(self): screenshot_root_directory = self.screenshot_root_directory if is_string(screenshot_root_directory): if screenshot_root_directory.upper() == EMBED: self.screenshot_root_directory = EMBED
def __init__(self, timeout=5.0, implicit_wait=0.0, run_on_failure='Capture Page Screenshot', screenshot_root_directory=None, plugins=None): """SeleniumLibrary can be imported with several optional arguments. - ``timeout``: Default value for `timeouts` used with ``Wait ...`` keywords. - ``implicit_wait``: Default value for `implicit wait` used when locating elements. - ``run_on_failure``: Default action for the `run-on-failure functionality`. - ``screenshot_root_directory``: Location where possible screenshots are created. If not given, the directory where the log file is written is used. - ``plugins``: Allows extending the SeleniumLibrary with external Python classes. """ self.timeout = timestr_to_secs(timeout) self.implicit_wait = timestr_to_secs(implicit_wait) self.speed = 0.0 self.run_on_failure_keyword \ = RunOnFailureKeywords.resolve_keyword(run_on_failure) self._running_on_failure_keyword = False self.screenshot_root_directory = screenshot_root_directory self._element_finder = ElementFinder(self) self._plugin_keywords = [] libraries = [ AlertKeywords(self), BrowserManagementKeywords(self), CookieKeywords(self), ElementKeywords(self), FormElementKeywords(self), FrameKeywords(self), JavaScriptKeywords(self), RunOnFailureKeywords(self), ScreenshotKeywords(self), SelectElementKeywords(self), TableElementKeywords(self), WaitingKeywords(self), WindowKeywords(self) ] if is_truthy(plugins): parsed_plugins = self._string_to_modules(plugins) for index, plugin in enumerate( self._import_modules(parsed_plugins)): if not isclass(plugin): message = "Importing test library: '%s' failed." % parsed_plugins[ index].plugin raise DataError(message) plugin = plugin(self, *parsed_plugins[index].args, **parsed_plugins[index].kw_args) if not isinstance(plugin, LibraryComponent): message = 'Plugin does not inherit SeleniumLibrary.base.LibraryComponent' raise PluginError(message) self._store_plugin_keywords(plugin) libraries.append(plugin) self._drivers = WebDriverCache() DynamicCore.__init__(self, libraries) self.ROBOT_LIBRARY_LISTENER = LibraryListener()