From c9d34bebeb000efe8d335b26abbffff94ba7fc3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Rohner?= Date: Mon, 8 Jul 2019 22:47:19 +0200 Subject: [PATCH 01/22] Added Keywords "Get Browser Ids" and "Get Browser aliases" to get a lists of all browsers. --- .../keywords/browsermanagement.py | 16 ++++++++++++++++ src/SeleniumLibrary/keywords/webdrivertools.py | 12 ++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/SeleniumLibrary/keywords/browsermanagement.py b/src/SeleniumLibrary/keywords/browsermanagement.py index bc31324d3..58f0030ff 100644 --- a/src/SeleniumLibrary/keywords/browsermanagement.py +++ b/src/SeleniumLibrary/keywords/browsermanagement.py @@ -350,6 +350,22 @@ def switch_browser(self, index_or_alias): self.debug('Switched to browser with Selenium session id %s.' % self.driver.session_id) + @ keyword + def get_browser_ids(self): + """Returns ids of all active browser as list. + + New in SeleniumLibrary 4.0 + """ + return self.drivers.active_driver_ids + + @keyword + def get_browser_aliases(self): + """Returns aliases of all active browser as list. + + New in SeleniumLibrary 4.0 + """ + return self.drivers.active_aliases + @keyword def get_session_id(self): """Returns the currently active browser session id. diff --git a/src/SeleniumLibrary/keywords/webdrivertools.py b/src/SeleniumLibrary/keywords/webdrivertools.py index 98ffedc4f..368cca9eb 100644 --- a/src/SeleniumLibrary/keywords/webdrivertools.py +++ b/src/SeleniumLibrary/keywords/webdrivertools.py @@ -294,6 +294,18 @@ def active_drivers(self): open_drivers.append(driver) return open_drivers + @property + def active_driver_ids(self): + open_driver_ids = [] + for index, driver in enumerate(self._connections): + if driver not in self._closed: + open_driver_ids.append(index + 1) + return open_driver_ids + + @property + def active_aliases(self): + return self._aliases + def close(self): if self.current: driver = self.current From a01d4e9f66e5b8bc2394be3578a3ee976702af41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Rohner?= Date: Mon, 8 Jul 2019 22:48:59 +0200 Subject: [PATCH 02/22] Added "Switch Window" as replacement for select window. Added "browser" Option to all wondow Getters to get windows information of all browsers. --- src/SeleniumLibrary/keywords/window.py | 37 ++++++++++-------- src/SeleniumLibrary/locators/windowmanager.py | 38 ++++++++++++++++++- 2 files changed, 58 insertions(+), 17 deletions(-) diff --git a/src/SeleniumLibrary/keywords/window.py b/src/SeleniumLibrary/keywords/window.py index a572402af..2c214d994 100644 --- a/src/SeleniumLibrary/keywords/window.py +++ b/src/SeleniumLibrary/keywords/window.py @@ -31,7 +31,12 @@ def __init__(self, ctx): @keyword def select_window(self, locator='MAIN', timeout=None): - """Selects browser window matching ``locator``. + """*DEPRECATED in SeleniumLibrary 4.0.* use `Switch Window` instead.""" + self.switch_window(locator, timeout) + + @keyword + def switch_window(self, locator='MAIN', timeout=None, browser='CURRENT'): + """Switches to browser window matching ``locator``. If the window is found, all subsequent commands use the selected window, until this keyword is used again. If the window is not @@ -117,37 +122,37 @@ def close_window(self): self.driver.close() @keyword - def get_window_handles(self): - """Return all current window handles as a list. + def get_window_handles(self, browser='CURRENT'): + """Return all child window handles of the current browser as a list. Can be used as a list of windows to exclude with `Select Window`. Prior to SeleniumLibrary 3.0, this keyword was named `List Windows`. """ - return self.driver.window_handles + return self._window_manager.get_window_handles(browser) @keyword - def get_window_identifiers(self): - """Returns and logs id attributes of all known browser windows.""" - ids = [info.id for info in self._window_manager.get_window_infos()] + def get_window_identifiers(self, browser='CURRENT'): + """Returns and logs id attributes of the current browser windows.""" + ids = [info.id for info in self._window_manager.get_window_infos(browser)] return self._log_list(ids) @keyword - def get_window_names(self): - """Returns and logs names of all known browser windows.""" - names = [info.name for info in self._window_manager.get_window_infos()] + def get_window_names(self, browser='CURRENT'): + """Returns and logs names of the current browser windows.""" + names = [info.name for info in self._window_manager.get_window_infos(browser)] return self._log_list(names) @keyword - def get_window_titles(self): - """Returns and logs titles of all known browser windows.""" - titles = [info.title for info in self._window_manager.get_window_infos()] + def get_window_titles(self, browser='CURRENT'): + """Returns and logs titles of the current browser windows.""" + titles = [info.title for info in self._window_manager.get_window_infos(browser)] return self._log_list(titles) @keyword - def get_locations(self): - """Returns and logs URLs of all known browser windows.""" - urls = [info.url for info in self._window_manager.get_window_infos()] + def get_locations(self, browser='CURRENT'): + """Returns and logs URLs of the current browser windows.""" + urls = [info.url for info in self._window_manager.get_window_infos(browser)] return self._log_list(urls) @keyword diff --git a/src/SeleniumLibrary/locators/windowmanager.py b/src/SeleniumLibrary/locators/windowmanager.py index 72cdf4fc3..b3365dbef 100644 --- a/src/SeleniumLibrary/locators/windowmanager.py +++ b/src/SeleniumLibrary/locators/windowmanager.py @@ -39,7 +39,42 @@ def __init__(self, ctx): 'default': self._select_by_default } - def get_window_infos(self): + def get_window_handles(self, browser): + if browser == 'ALL': + handles = [] + current_index = self.drivers.current_index + for index, driver in enumerate(self.drivers, 1): + self.drivers.switch(index) + handles.extend(self.driver.window_handles) + self.drivers.switch(current_index) + return handles + elif browser == 'CURRENT': + return self.driver.window_handles + else: + current_index = self.drivers.current_index + self.drivers.switch(browser) + handles = self.driver.window_handles + self.drivers.switch(current_index) + return handles + + def get_window_infos(self, browser='CURRENT'): + current_index = self.drivers.current_index + if browser == 'ALL': + infos = [] + for index, driver in enumerate(self.drivers, 1): + self.drivers.switch(index) + infos.extend(self._get_window_infos()) + self.drivers.switch(current_index) + return infos + elif browser == 'CURRENT': + return self._get_window_infos() + else: + self.drivers.switch(browser) + infos = self._get_window_infos() + self.drivers.switch(current_index) + return infos + + def _get_window_infos(self): infos = [] try: starting_handle = self.driver.current_window_handle @@ -55,6 +90,7 @@ def get_window_infos(self): return infos def select(self, locator, timeout=0): + # Todo: try to select a windows with browser identifier. CURRENT, ALL, while True: try: return self._select(locator) From 171f45a89e0d22ecbe490db806e9656c99712aa3 Mon Sep 17 00:00:00 2001 From: Snooz82 Date: Mon, 8 Jul 2019 23:02:29 +0200 Subject: [PATCH 03/22] Added Option "browser" to "Switch Window" (only CURRENT or are possible). Switches Browser before. --- src/SeleniumLibrary/keywords/window.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/SeleniumLibrary/keywords/window.py b/src/SeleniumLibrary/keywords/window.py index 2c214d994..1178e8627 100644 --- a/src/SeleniumLibrary/keywords/window.py +++ b/src/SeleniumLibrary/keywords/window.py @@ -114,6 +114,8 @@ def switch_window(self, locator='MAIN', timeout=None, browser='CURRENT'): except NoSuchWindowException: pass finally: + if not browser == 'CURRENT': # Todo: Option ALL is necessary but complicated. + self.drivers.switch(browser) self._window_manager.select(locator, timeout) @keyword From 9c13ba62dd944effcd14733ef3e73a4614518278 Mon Sep 17 00:00:00 2001 From: Snooz82 Date: Wed, 10 Jul 2019 00:22:08 +0200 Subject: [PATCH 04/22] Updated Doc-Strings. Created Browser/Window Documentation Moved Window related Keywords from BrowsermanagementKeywords to WindowsKeywords --- src/SeleniumLibrary/__init__.py | 67 ++++++++ .../keywords/browsermanagement.py | 119 +------------ src/SeleniumLibrary/keywords/window.py | 159 ++++++++++++++++-- src/SeleniumLibrary/locators/windowmanager.py | 9 +- 4 files changed, 225 insertions(+), 129 deletions(-) diff --git a/src/SeleniumLibrary/__init__.py b/src/SeleniumLibrary/__init__.py index bfcd2d8a2..84d9bae0a 100644 --- a/src/SeleniumLibrary/__init__.py +++ b/src/SeleniumLibrary/__init__.py @@ -62,6 +62,7 @@ class SeleniumLibrary(DynamicCore): == Table of contents == - `Locating elements` + - `Browsers and Windows` - `Timeouts, waits and delays` - `Run-on-failure functionality` - `Boolean arguments` @@ -223,6 +224,72 @@ class SeleniumLibrary(DynamicCore): See the `Add Location Strategy` keyword for more details. + = Browsers and Windows = + + In this section the concept of Browsers or Webdrivers and its Windows + will be explained. + + == Browsers == + + In Selenium Library there are two ways to start a Browser and control it. + The first one is the `Open Browser` keyword. This one should be used as + default keyword. + The second one is the `Create Webdriver` keyword which was useful, when + browser options needed to be set. Since Selenium Library 4.0.x `Open Browser` + also supports option why `Create Webdriver` will be deprecated. + When opening a browser a new Webdriver process is startet and connected to + Selenium Library. When using Selenium Grid this browser may be started on any + Grid node that has matching capabilities. + It is possible to start multiple independent browsers at the same test case. + These browsers do typically not share any data like sessions etc. + Each browser starts with one window. One Browser may have multiple windows. + In example when a Pop-Up has been opened or a new side is opened in a new + browser tab. + + There are some browser related keywords: + - `Open Browser` to create a new browser object + - `Create Webdriver` to create a new browser object + - `Get Browser Ids` to get the IDs of all open browsers + _ `Get Browser Aliases` to get the aliases of all open browsers + - `Switch Browser` to use a different opened browser as current browser + - `Close Browser` closes the current browser and all its windows + - `Close All Browsers` closes all browsers that has been opened by this library instance + + == Windows == + + Windows are the part of a browser that loads the web site and presents + it to the user. All Content of the site is content of the window. + Browser windows are children of a browser object. One browser may + have multiple windows. Windows can appear as tabs or as separate windows + with different position and size. + Windows of the same browser typically share the same sessions. This is + the reason why it is not possible to log in with two different users + of the same site in these windows. To solve this issue it is necessary + to work with two different browser objects. + There are multiple window related keywords. + + When working with multiple windows these two keywords are mostly relevant. + - `Switch Window` to use a different open window of a browser + - `Close Window` to close the currently used window + + To open a new window, the site has to open content as new tab. + It may also be possible to use `Execute Javascript` Keyword: + + | `Execute Javascript` | window.open() | + + == Example == + + In this example it is required that github.com is open once with a logged + in ``User-A`` and without any user logged in. + For this two browsers needed to be opened. + + | Browser A | | (firefox) | + | | Window 1 | (location=https://robotframework.org) | + | | Window 2 | (location=https://robocon.io) | + | | Window 3 | (location=https://github.com/robotframework/SeleniumLibrary) | + | Browser B | | (firefox) | + | | Window 1 | (location=https://github.com/robotframework/SeleniumLibrary) | + = Timeouts, waits and delays = This section discusses different ways how to wait for elements to diff --git a/src/SeleniumLibrary/keywords/browsermanagement.py b/src/SeleniumLibrary/keywords/browsermanagement.py index 58f0030ff..7f34ba59f 100644 --- a/src/SeleniumLibrary/keywords/browsermanagement.py +++ b/src/SeleniumLibrary/keywords/browsermanagement.py @@ -22,7 +22,7 @@ from SeleniumLibrary.base import keyword, LibraryComponent from SeleniumLibrary.locators import WindowManager -from SeleniumLibrary.utils import (is_truthy, is_noney, secs_to_timestr, +from SeleniumLibrary.utils import (is_truthy, secs_to_timestr, timestr_to_secs) from .webdrivertools import WebDriverCreator @@ -352,7 +352,9 @@ def switch_browser(self, index_or_alias): @ keyword def get_browser_ids(self): - """Returns ids of all active browser as list. + """Returns index of all active browser as list. + + See `Switch Browser` for more information and examples. New in SeleniumLibrary 4.0 """ @@ -362,6 +364,8 @@ def get_browser_ids(self): def get_browser_aliases(self): """Returns aliases of all active browser as list. + See `Switch Browser` for more information and examples. + New in SeleniumLibrary 4.0 """ return self.drivers.active_aliases @@ -374,117 +378,6 @@ def get_session_id(self): """ return self.driver.session_id - @keyword - def get_source(self): - """Returns the entire HTML source of the current page or frame.""" - return self.driver.page_source - - @keyword - def get_title(self): - """Returns the title of current page.""" - return self.driver.title - - @keyword - def get_location(self): - """Returns the current browser URL.""" - return self.driver.current_url - - @keyword - def location_should_be(self, url, message=None): - """Verifies that current URL is exactly ``url``. - - The ``url`` argument contains the exact url that should exist in browser. - - The ``message`` argument can be used to override the default error - message. - - ``message`` argument new in SeleniumLibrary 3.2.0. - """ - actual = self.get_location() - if actual != url: - if is_noney(message): - message = ("Location should have been '%s' but " - "was '%s'." % (url, actual)) - raise AssertionError(message) - self.info("Current location is '%s'." % url) - - @keyword - def location_should_contain(self, expected, message=None): - """Verifies that current URL contains ``expected``. - - The ``expected`` argument contains the expected value in url. - - The ``message`` argument can be used to override the default error - message. - - ``message`` argument new in SeleniumLibrary 3.2.0. - """ - actual = self.get_location() - if expected not in actual: - if is_noney(message): - message = ("Location should have contained '%s' but " - "it was '%s'." % (expected, actual)) - raise AssertionError(message) - self.info("Current location contains '%s'." % expected) - - @keyword - def log_location(self): - """Logs and returns the current URL.""" - url = self.get_location() - self.info(url) - return url - - @keyword - def log_source(self, loglevel='INFO'): - """Logs and returns the HTML source of the current page or frame. - - The ``loglevel`` argument defines the used log level. Valid log - levels are ``WARN``, ``INFO`` (default), ``DEBUG``, ``TRACE`` - and ``NONE`` (no logging). - """ - source = self.get_source() - self.log(source, loglevel) - return source - - @keyword - def log_title(self): - """Logs and returns the title of current page.""" - title = self.get_title() - self.info(title) - return title - - @keyword - def title_should_be(self, title, message=None): - """Verifies that current page title equals ``title``. - - The ``message`` argument can be used to override the default error - message. - - ``message`` argument is new in SeleniumLibrary 3.1. - """ - actual = self.get_title() - if actual != title: - if is_noney(message): - message = "Title should have been '%s' but was '%s'." % (title, actual) - raise AssertionError(message) - self.info("Page title is '%s'." % title) - - @keyword - def go_back(self): - """Simulates the user clicking the back button on their browser.""" - self.driver.back() - - @keyword - def go_to(self, url): - """Navigates the active browser instance to the provided ``url``.""" - self.info("Opening url '%s'" % url) - self.driver.get(url) - - @keyword - def reload_page(self): - """Simulates user reloading page.""" - self.driver.refresh() - @keyword def get_selenium_speed(self): """Gets the delay that is waited after each Selenium command. diff --git a/src/SeleniumLibrary/keywords/window.py b/src/SeleniumLibrary/keywords/window.py index 1178e8627..64c33b1c6 100644 --- a/src/SeleniumLibrary/keywords/window.py +++ b/src/SeleniumLibrary/keywords/window.py @@ -20,7 +20,7 @@ from SeleniumLibrary.base import keyword, LibraryComponent from SeleniumLibrary.locators import WindowManager -from SeleniumLibrary.utils import plural_or_not +from SeleniumLibrary.utils import (plural_or_not, is_string, is_noney) class WindowKeywords(LibraryComponent): @@ -43,10 +43,7 @@ def switch_window(self, locator='MAIN', timeout=None, browser='CURRENT'): found, this keyword fails. The previous window handle is returned, and can be used to return back to it later. - Notice that in this context _window_ means a pop-up window opened - when doing something on an existing window. It is not possible to - select windows opened with `Open Browser`, `Switch Browser` must - be used instead. Notice also that alerts should be handled with + Notice that alerts should be handled with `Handle Alert` or other alert related keywords. The ``locator`` can be specified using different strategies somewhat @@ -96,6 +93,12 @@ def switch_window(self, locator='MAIN', timeout=None, browser='CURRENT'): | `Select Window` | ${excludes} | | # Select window using excludes | | `Title Should Be` | Pop-up 3 | | + The ``browser`` argument allows with ``index_or_alias`` to implicitly switch to + a specific browser when switching to a window. See `Switch Browser` + + - If the ``browser`` is ``CURRENT`` (case-insensitive), no other browser is + selected. + *NOTE:* - The ``strategy:value`` syntax is only supported by SeleniumLibrary @@ -114,7 +117,7 @@ def switch_window(self, locator='MAIN', timeout=None, browser='CURRENT'): except NoSuchWindowException: pass finally: - if not browser == 'CURRENT': # Todo: Option ALL is necessary but complicated. + if not is_string(browser) or not browser.upper() == 'CURRENT': self.drivers.switch(browser) self._window_manager.select(locator, timeout) @@ -125,35 +128,56 @@ def close_window(self): @keyword def get_window_handles(self, browser='CURRENT'): - """Return all child window handles of the current browser as a list. + """Returns all child window handles of the selected browser as a list. Can be used as a list of windows to exclude with `Select Window`. + How to select the ``browser`` scope of this keyword, see `Get Locations`. + Prior to SeleniumLibrary 3.0, this keyword was named `List Windows`. """ return self._window_manager.get_window_handles(browser) @keyword def get_window_identifiers(self, browser='CURRENT'): - """Returns and logs id attributes of the current browser windows.""" + """Returns and logs id attributes of all windows of the selected browser. + + How to select the ``browser`` scope of this keyword, see `Get Locations`.""" ids = [info.id for info in self._window_manager.get_window_infos(browser)] return self._log_list(ids) @keyword def get_window_names(self, browser='CURRENT'): - """Returns and logs names of the current browser windows.""" + """Returns and logs names of all windows of the selected browser. + + How to select the ``browser`` scope of this keyword, see `Get Locations`.""" names = [info.name for info in self._window_manager.get_window_infos(browser)] return self._log_list(names) @keyword def get_window_titles(self, browser='CURRENT'): - """Returns and logs titles of the current browser windows.""" + """Returns and logs titles of all windows of the selected browser. + + How to select the ``browser`` scope of this keyword, see `Get Locations`.""" titles = [info.title for info in self._window_manager.get_window_infos(browser)] return self._log_list(titles) @keyword def get_locations(self, browser='CURRENT'): - """Returns and logs URLs of the current browser windows.""" + """Returns and logs URLs of all windows of the selected browser. + + *Browser Scope:* + + The ``browser`` argument specifies the browser that shall return + its windows information. + + - ``browser`` can be ``index_or_alias`` like in `Switch Browser`. + + - If ``browser`` is ``CURRENT`` (default, case-insensitive) + the currently active browser is selected. + + - If ``browser`` is ``ALL`` (case-insensitive) + the window information of all windows of all opened browsers are returned.""" urls = [info.url for info in self._window_manager.get_window_infos(browser)] return self._log_list(urls) @@ -256,6 +280,119 @@ def set_window_position(self, x, y): """ self.driver.set_window_position(int(x), int(y)) + + @keyword + def get_source(self): + """Returns the entire HTML source of the current page or frame.""" + return self.driver.page_source + + @keyword + def get_title(self): + """Returns the title of current page.""" + return self.driver.title + + @keyword + def get_location(self): + """Returns the current browser window URL.""" + return self.driver.current_url + + @keyword + def location_should_be(self, url, message=None): + """Verifies that current URL is exactly ``url``. + + The ``url`` argument contains the exact url that should exist in browser. + + The ``message`` argument can be used to override the default error + message. + + ``message`` argument new in SeleniumLibrary 3.2.0. + """ + actual = self.get_location() + if actual != url: + if is_noney(message): + message = ("Location should have been '%s' but " + "was '%s'." % (url, actual)) + raise AssertionError(message) + self.info("Current location is '%s'." % url) + + @keyword + def location_should_contain(self, expected, message=None): + """Verifies that current URL contains ``expected``. + + The ``expected`` argument contains the expected value in url. + + The ``message`` argument can be used to override the default error + message. + + ``message`` argument new in SeleniumLibrary 3.2.0. + """ + actual = self.get_location() + if expected not in actual: + if is_noney(message): + message = ("Location should have contained '%s' but " + "it was '%s'." % (expected, actual)) + raise AssertionError(message) + self.info("Current location contains '%s'." % expected) + + @keyword + def log_location(self): + """Logs and returns the current browser window URL.""" + url = self.get_location() + self.info(url) + return url + + @keyword + def log_source(self, loglevel='INFO'): + """Logs and returns the HTML source of the current page or frame. + + The ``loglevel`` argument defines the used log level. Valid log + levels are ``WARN``, ``INFO`` (default), ``DEBUG``, ``TRACE`` + and ``NONE`` (no logging). + """ + source = self.get_source() + self.log(source, loglevel) + return source + + @keyword + def log_title(self): + """Logs and returns the title of current page.""" + title = self.get_title() + self.info(title) + return title + + @keyword + def title_should_be(self, title, message=None): + """Verifies that current page title equals ``title``. + + The ``message`` argument can be used to override the default error + message. + + ``message`` argument is new in SeleniumLibrary 3.1. + """ + actual = self.get_title() + if actual != title: + if is_noney(message): + message = "Title should have been '%s' but was '%s'." % (title, actual) + raise AssertionError(message) + self.info("Page title is '%s'." % title) + + @keyword + def go_back(self): + """Simulates the user clicking the back button on their browser.""" + self.driver.back() + + @keyword + def go_to(self, url): + """Navigates the active browser instance to the provided ``url``.""" + self.info("Opening url '%s'" % url) + self.driver.get(url) + + @keyword + def reload_page(self): + """Simulates user reloading page.""" + self.driver.refresh() + + def _log_list(self, items, what='item'): msg = [ 'Altogether %s %s%s.' diff --git a/src/SeleniumLibrary/locators/windowmanager.py b/src/SeleniumLibrary/locators/windowmanager.py index b3365dbef..d898d5bf9 100644 --- a/src/SeleniumLibrary/locators/windowmanager.py +++ b/src/SeleniumLibrary/locators/windowmanager.py @@ -40,7 +40,7 @@ def __init__(self, ctx): } def get_window_handles(self, browser): - if browser == 'ALL': + if is_string(browser) and browser == 'ALL': handles = [] current_index = self.drivers.current_index for index, driver in enumerate(self.drivers, 1): @@ -48,7 +48,7 @@ def get_window_handles(self, browser): handles.extend(self.driver.window_handles) self.drivers.switch(current_index) return handles - elif browser == 'CURRENT': + elif is_string(browser) and browser == 'CURRENT': return self.driver.window_handles else: current_index = self.drivers.current_index @@ -59,14 +59,14 @@ def get_window_handles(self, browser): def get_window_infos(self, browser='CURRENT'): current_index = self.drivers.current_index - if browser == 'ALL': + if is_string(browser) and browser.upper() == 'ALL': infos = [] for index, driver in enumerate(self.drivers, 1): self.drivers.switch(index) infos.extend(self._get_window_infos()) self.drivers.switch(current_index) return infos - elif browser == 'CURRENT': + elif is_string(browser) and browser.upper() == 'CURRENT': return self._get_window_infos() else: self.drivers.switch(browser) @@ -90,7 +90,6 @@ def _get_window_infos(self): return infos def select(self, locator, timeout=0): - # Todo: try to select a windows with browser identifier. CURRENT, ALL, while True: try: return self._select(locator) From fca3b880385d6a88007c8d6d4088f4d063b312fb Mon Sep 17 00:00:00 2001 From: Snooz82 Date: Wed, 10 Jul 2019 11:22:42 +0200 Subject: [PATCH 05/22] Added example for Browser and window handling in Doc --- src/SeleniumLibrary/__init__.py | 50 +++++++++++++++++++------- src/SeleniumLibrary/keywords/window.py | 10 +++--- 2 files changed, 43 insertions(+), 17 deletions(-) diff --git a/src/SeleniumLibrary/__init__.py b/src/SeleniumLibrary/__init__.py index 84d9bae0a..944df8790 100644 --- a/src/SeleniumLibrary/__init__.py +++ b/src/SeleniumLibrary/__init__.py @@ -62,7 +62,7 @@ class SeleniumLibrary(DynamicCore): == Table of contents == - `Locating elements` - - `Browsers and Windows` + - `Browser and Window` - `Timeouts, waits and delays` - `Run-on-failure functionality` - `Boolean arguments` @@ -224,12 +224,12 @@ class SeleniumLibrary(DynamicCore): See the `Add Location Strategy` keyword for more details. - = Browsers and Windows = + = Browser and Window = In this section the concept of Browsers or Webdrivers and its Windows will be explained. - == Browsers == + == Browser == In Selenium Library there are two ways to start a Browser and control it. The first one is the `Open Browser` keyword. This one should be used as @@ -255,7 +255,7 @@ class SeleniumLibrary(DynamicCore): - `Close Browser` closes the current browser and all its windows - `Close All Browsers` closes all browsers that has been opened by this library instance - == Windows == + == 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 content of the window. @@ -275,7 +275,7 @@ class SeleniumLibrary(DynamicCore): To open a new window, the site has to open content as new tab. It may also be possible to use `Execute Javascript` Keyword: - | `Execute Javascript` | window.open() | + | `Execute Javascript` window.open() # Opens a new window with location about:blank == Example == @@ -283,13 +283,39 @@ class SeleniumLibrary(DynamicCore): in ``User-A`` and without any user logged in. For this two browsers needed to be opened. - | Browser A | | (firefox) | - | | Window 1 | (location=https://robotframework.org) | - | | Window 2 | (location=https://robocon.io) | - | | Window 3 | (location=https://github.com/robotframework/SeleniumLibrary) | - | Browser B | | (firefox) | - | | Window 1 | (location=https://github.com/robotframework/SeleniumLibrary) | - + Structure: + | Browser A (firefox) + | ├──── Window 1 (location=https://robotframework.org/) + | ├──── Window 2 (location=https://robocon.io/) + | └──── Window 3 (location=https://github.com/robotframework/) + | Browser B (firefox) + | └──── Window 1 (location=https://github.com/) + + Robot Framework Example: + | `Open Browser` https://robotframework.org alias=BrowserA # BrowserA with Window1 is open + | `Execute Javascript` window.open() + | `Switch Window` locator=NEW # Window2 opened and switched to it + | `Go To` https://robocon.io # Window2 go to robocon site + | `Execute Javascript` window.open() + | ${handle} `Switch Window` locator=NEW # Window3 opened and switched to it + | `Go To` https://github.com/robotframework/ # Window3 go to robocon site + | `Open Browser` https://github.com alias=BrowserB # BrowserB with Window1 is open + | ${locations} `Get Location` # ${Location} is https://www.github.com + | `Switch Window` ${handle} browser=BrowserA # BrowserA Window2 is selected + | ${locations} `Get Location` # ${Location} = https://robocon.io/ + | @{locations} `Get Locations` # @{Location} = + | # [ + | # 'https://robotframework.org/', + | # 'https://robocon.io/', + | # 'https://github.com/robotframework/' + | # ] + | @{locations} `Get Locations` browser=ALL # @{Location} = + | # [ + | # 'https://robotframework.org/', + | # 'https://robocon.io/', + | # 'https://github.com/robotframework/', + | # 'https://github.com/' + | # ] = Timeouts, waits and delays = This section discusses different ways how to wait for elements to diff --git a/src/SeleniumLibrary/keywords/window.py b/src/SeleniumLibrary/keywords/window.py index 64c33b1c6..1e59b6624 100644 --- a/src/SeleniumLibrary/keywords/window.py +++ b/src/SeleniumLibrary/keywords/window.py @@ -79,18 +79,18 @@ def switch_window(self, locator='MAIN', timeout=None, browser='CURRENT'): Example: | `Click Link` | popup1 | | # Open new window | - | `Select Window` | example | | # Select window using default strategy | + | `Switch Window` | example | | # Select window using default strategy | | `Title Should Be` | Pop-up 1 | | | `Click Button` | popup2 | | # Open another window | - | ${handle} = | `Select Window` | NEW | # Select latest opened window | + | ${handle} = | `Switch Window` | NEW | # Select latest opened window | | `Title Should Be` | Pop-up 2 | | - | `Select Window` | ${handle} | | # Select window using handle | + | `Switch Window` | ${handle} | | # Select window using handle | | `Title Should Be` | Pop-up 1 | | - | `Select Window` | MAIN | | # Select the main window | + | `Switch Window` | MAIN | | # Select the main window | | `Title Should Be` | Main | | | ${excludes} = | `Get Window Handles` | | # Get list of current windows | | `Click Link` | popup3 | | # Open one more window | - | `Select Window` | ${excludes} | | # Select window using excludes | + | `Switch Window` | ${excludes} | | # Select window using excludes | | `Title Should Be` | Pop-up 3 | | The ``browser`` argument allows with ``index_or_alias`` to implicitly switch to From 1275d5b8e9f5db9ca87ee402e3b6eee1c5505d2c Mon Sep 17 00:00:00 2001 From: Snooz82 Date: Wed, 10 Jul 2019 11:26:44 +0200 Subject: [PATCH 06/22] Select Window deprecation warning to silent. --- src/SeleniumLibrary/keywords/window.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SeleniumLibrary/keywords/window.py b/src/SeleniumLibrary/keywords/window.py index 1e59b6624..0df364741 100644 --- a/src/SeleniumLibrary/keywords/window.py +++ b/src/SeleniumLibrary/keywords/window.py @@ -31,7 +31,7 @@ def __init__(self, ctx): @keyword def select_window(self, locator='MAIN', timeout=None): - """*DEPRECATED in SeleniumLibrary 4.0.* use `Switch Window` instead.""" + """DEPRECATED in SeleniumLibrary 4.0. , use `Switch Window` instead.""" self.switch_window(locator, timeout) @keyword From 7b209bd8f6e9cba9593b4e5588feed41d5f20ce3 Mon Sep 17 00:00:00 2001 From: Snooz82 Date: Wed, 10 Jul 2019 22:07:14 +0200 Subject: [PATCH 07/22] more improvements in documentation on window related keywords. --- src/SeleniumLibrary/__init__.py | 48 ++++++++----------- .../keywords/browsermanagement.py | 15 +++++- src/SeleniumLibrary/keywords/window.py | 4 +- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/SeleniumLibrary/__init__.py b/src/SeleniumLibrary/__init__.py index 944df8790..cb8467ab9 100644 --- a/src/SeleniumLibrary/__init__.py +++ b/src/SeleniumLibrary/__init__.py @@ -250,7 +250,7 @@ class SeleniumLibrary(DynamicCore): - `Open Browser` to create a new browser object - `Create Webdriver` to create a new browser object - `Get Browser Ids` to get the IDs of all open browsers - _ `Get Browser Aliases` to get the aliases of all open browsers + - `Get Browser Aliases` to get the aliases of all open browsers - `Switch Browser` to use a different opened browser as current browser - `Close Browser` closes the current browser and all its windows - `Close All Browsers` closes all browsers that has been opened by this library instance @@ -258,7 +258,7 @@ class SeleniumLibrary(DynamicCore): == 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 content of the window. + it to the user. All content of the site is content of the window. Browser windows are children of a browser object. One browser may have multiple windows. Windows can appear as tabs or as separate windows with different position and size. @@ -279,9 +279,9 @@ class SeleniumLibrary(DynamicCore): == Example == - In this example it is required that github.com is open once with a logged - in ``User-A`` and without any user logged in. - For this two browsers needed to be opened. + In this example there could be the required that the site github.com is open once with a logged + in ``User-A`` and once without any user logged in. + For this two browsers would be necessary to be opened. Structure: | Browser A (firefox) @@ -292,30 +292,20 @@ class SeleniumLibrary(DynamicCore): | └──── Window 1 (location=https://github.com/) Robot Framework Example: - | `Open Browser` https://robotframework.org alias=BrowserA # BrowserA with Window1 is open - | `Execute Javascript` window.open() - | `Switch Window` locator=NEW # Window2 opened and switched to it - | `Go To` https://robocon.io # Window2 go to robocon site - | `Execute Javascript` window.open() - | ${handle} `Switch Window` locator=NEW # Window3 opened and switched to it - | `Go To` https://github.com/robotframework/ # Window3 go to robocon site - | `Open Browser` https://github.com alias=BrowserB # BrowserB with Window1 is open - | ${locations} `Get Location` # ${Location} is https://www.github.com - | `Switch Window` ${handle} browser=BrowserA # BrowserA Window2 is selected - | ${locations} `Get Location` # ${Location} = https://robocon.io/ - | @{locations} `Get Locations` # @{Location} = - | # [ - | # 'https://robotframework.org/', - | # 'https://robocon.io/', - | # 'https://github.com/robotframework/' - | # ] - | @{locations} `Get Locations` browser=ALL # @{Location} = - | # [ - | # 'https://robotframework.org/', - | # 'https://robocon.io/', - | # 'https://github.com/robotframework/', - | # 'https://github.com/' - | # ] + | `Open Browser` | https://robotframework.org | alias=BrowserA | # BrowserA with Window1 is open | + | `Execute Javascript` | window.open() | | | + | `Switch Window` | locator=NEW | | # Window2 opened and switched to it | + | `Go To` | https://robocon.io | | # Window2 go to robocon site | + | `Execute Javascript` | window.open() | | | + | ${handle} | `Switch Window` | locator=NEW | # Window3 opened and switched to it | + | `Go To` | https://github.com/robotframework/ | | # Window3 go to robot framework github site | + | `Open Browser` | https://github.com | alias=BrowserB | # BrowserB with Window1 is open | + | ${location} | `Get Location` | | # ${location} is https://www.github.com | + | `Switch Window` | ${handle} | browser=BrowserA | # BrowserA Window2 is selected | + | ${location} | `Get Location` | | # ${location} = https://robocon.io/ | + | @{locations} | `Get Locations` | | # @{locations} = [ 'https://robotframework.org/', 'https://robocon.io/', 'https://github.com/robotframework/' ] | + | @{locations} | `Get Locations` | browser=ALL | # @{locations} = [ 'https://robotframework.org/', 'https://robocon.io/', 'https://github.com/robotframework/', 'https://github.com/' ] | + = Timeouts, waits and delays = This section discusses different ways how to wait for elements to diff --git a/src/SeleniumLibrary/keywords/browsermanagement.py b/src/SeleniumLibrary/keywords/browsermanagement.py index 7f34ba59f..cc3b30487 100644 --- a/src/SeleniumLibrary/keywords/browsermanagement.py +++ b/src/SeleniumLibrary/keywords/browsermanagement.py @@ -350,7 +350,7 @@ def switch_browser(self, index_or_alias): self.debug('Switched to browser with Selenium session id %s.' % self.driver.session_id) - @ keyword + @keyword def get_browser_ids(self): """Returns index of all active browser as list. @@ -362,7 +362,18 @@ def get_browser_ids(self): @keyword def get_browser_aliases(self): - """Returns aliases of all active browser as list. + """Returns aliases of all active browser as NormalizedDict. + The dictionary contains the aliases as keys and the index as value. + This can be accessed as dictionary ``${aliases.key}`` or as list ``@{aliases}[0]``. + + Example: + | `Open Browser` | https://example.com | alias=BrowserA | | + | `Open Browser` | https://example.com | alias=BrowserB | | + | &{aliases} | `Get Browser Aliases` | | # &{aliases} = { BrowserA=1|BrowserB=2 } | + | `Log` | ${aliases.BrowserA} | | # logs ``1`` | + | FOR | ${alias} | IN | @{aliases} | + | | `Log` | ${alias} | # logs ``BrowserA`` and ``BrowserB`` | + | END | | | | See `Switch Browser` for more information and examples. diff --git a/src/SeleniumLibrary/keywords/window.py b/src/SeleniumLibrary/keywords/window.py index 0df364741..d5e6d3587 100644 --- a/src/SeleniumLibrary/keywords/window.py +++ b/src/SeleniumLibrary/keywords/window.py @@ -123,7 +123,7 @@ def switch_window(self, locator='MAIN', timeout=None, browser='CURRENT'): @keyword def close_window(self): - """Closes currently opened pop-up window.""" + """Closes currently opened and selected browser window/tab. """ self.driver.close() @keyword @@ -383,7 +383,7 @@ def go_back(self): @keyword def go_to(self, url): - """Navigates the active browser instance to the provided ``url``.""" + """Navigates the current browser window to the provided ``url``.""" self.info("Opening url '%s'" % url) self.driver.get(url) From 7bd1b0679b97bdacf89edf404d514e8087cda2d0 Mon Sep 17 00:00:00 2001 From: Snooz82 Date: Wed, 10 Jul 2019 22:22:22 +0200 Subject: [PATCH 08/22] small improvement to Get Browser Aliases documentation. --- src/SeleniumLibrary/keywords/browsermanagement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SeleniumLibrary/keywords/browsermanagement.py b/src/SeleniumLibrary/keywords/browsermanagement.py index cc3b30487..97fb24002 100644 --- a/src/SeleniumLibrary/keywords/browsermanagement.py +++ b/src/SeleniumLibrary/keywords/browsermanagement.py @@ -362,7 +362,7 @@ def get_browser_ids(self): @keyword def get_browser_aliases(self): - """Returns aliases of all active browser as NormalizedDict. + """Returns aliases of all active browser that has an alias as NormalizedDict. The dictionary contains the aliases as keys and the index as value. This can be accessed as dictionary ``${aliases.key}`` or as list ``@{aliases}[0]``. From efa0c5c4b1f3c39e3dc2d46bf1e6658c80e16c46 Mon Sep 17 00:00:00 2001 From: Snooz82 Date: Mon, 15 Jul 2019 10:51:46 +0200 Subject: [PATCH 09/22] Moved window related keywords back to BrowserManagementKeywords to oversee the relevant changes better. --- .../keywords/browsermanagement.py | 113 ++++++++++++++++- src/SeleniumLibrary/keywords/window.py | 115 +----------------- 2 files changed, 113 insertions(+), 115 deletions(-) diff --git a/src/SeleniumLibrary/keywords/browsermanagement.py b/src/SeleniumLibrary/keywords/browsermanagement.py index 97fb24002..0dd1f4880 100644 --- a/src/SeleniumLibrary/keywords/browsermanagement.py +++ b/src/SeleniumLibrary/keywords/browsermanagement.py @@ -22,7 +22,7 @@ from SeleniumLibrary.base import keyword, LibraryComponent from SeleniumLibrary.locators import WindowManager -from SeleniumLibrary.utils import (is_truthy, secs_to_timestr, +from SeleniumLibrary.utils import (is_truthy, is_noney, secs_to_timestr, timestr_to_secs) from .webdrivertools import WebDriverCreator @@ -381,6 +381,117 @@ def get_browser_aliases(self): """ return self.drivers.active_aliases + @keyword + def get_source(self): + """Returns the entire HTML source of the current page or frame.""" + return self.driver.page_source + + @keyword + def get_title(self): + """Returns the title of current page.""" + return self.driver.title + + @keyword + def get_location(self): + """Returns the current browser window URL.""" + return self.driver.current_url + + @keyword + def location_should_be(self, url, message=None): + """Verifies that current URL is exactly ``url``. + + The ``url`` argument contains the exact url that should exist in browser. + + The ``message`` argument can be used to override the default error + message. + + ``message`` argument new in SeleniumLibrary 3.2.0. + """ + actual = self.get_location() + if actual != url: + if is_noney(message): + message = ("Location should have been '%s' but " + "was '%s'." % (url, actual)) + raise AssertionError(message) + self.info("Current location is '%s'." % url) + + @keyword + def location_should_contain(self, expected, message=None): + """Verifies that current URL contains ``expected``. + + The ``expected`` argument contains the expected value in url. + + The ``message`` argument can be used to override the default error + message. + + ``message`` argument new in SeleniumLibrary 3.2.0. + """ + actual = self.get_location() + if expected not in actual: + if is_noney(message): + message = ("Location should have contained '%s' but " + "it was '%s'." % (expected, actual)) + raise AssertionError(message) + self.info("Current location contains '%s'." % expected) + + @keyword + def log_location(self): + """Logs and returns the current browser window URL.""" + url = self.get_location() + self.info(url) + return url + + @keyword + def log_source(self, loglevel='INFO'): + """Logs and returns the HTML source of the current page or frame. + + The ``loglevel`` argument defines the used log level. Valid log + levels are ``WARN``, ``INFO`` (default), ``DEBUG``, ``TRACE`` + and ``NONE`` (no logging). + """ + source = self.get_source() + self.log(source, loglevel) + return source + + @keyword + def log_title(self): + """Logs and returns the title of current page.""" + title = self.get_title() + self.info(title) + return title + + @keyword + def title_should_be(self, title, message=None): + """Verifies that current page title equals ``title``. + + The ``message`` argument can be used to override the default error + message. + + ``message`` argument is new in SeleniumLibrary 3.1. + """ + actual = self.get_title() + if actual != title: + if is_noney(message): + message = "Title should have been '%s' but was '%s'." % (title, actual) + raise AssertionError(message) + self.info("Page title is '%s'." % title) + + @keyword + def go_back(self): + """Simulates the user clicking the back button on their browser.""" + self.driver.back() + + @keyword + def go_to(self, url): + """Navigates the current browser window to the provided ``url``.""" + self.info("Opening url '%s'" % url) + self.driver.get(url) + + @keyword + def reload_page(self): + """Simulates user reloading page.""" + self.driver.refresh() + @keyword def get_session_id(self): """Returns the currently active browser session id. diff --git a/src/SeleniumLibrary/keywords/window.py b/src/SeleniumLibrary/keywords/window.py index d5e6d3587..8af4835c8 100644 --- a/src/SeleniumLibrary/keywords/window.py +++ b/src/SeleniumLibrary/keywords/window.py @@ -20,7 +20,7 @@ from SeleniumLibrary.base import keyword, LibraryComponent from SeleniumLibrary.locators import WindowManager -from SeleniumLibrary.utils import (plural_or_not, is_string, is_noney) +from SeleniumLibrary.utils import (plural_or_not, is_string) class WindowKeywords(LibraryComponent): @@ -280,119 +280,6 @@ def set_window_position(self, x, y): """ self.driver.set_window_position(int(x), int(y)) - - @keyword - def get_source(self): - """Returns the entire HTML source of the current page or frame.""" - return self.driver.page_source - - @keyword - def get_title(self): - """Returns the title of current page.""" - return self.driver.title - - @keyword - def get_location(self): - """Returns the current browser window URL.""" - return self.driver.current_url - - @keyword - def location_should_be(self, url, message=None): - """Verifies that current URL is exactly ``url``. - - The ``url`` argument contains the exact url that should exist in browser. - - The ``message`` argument can be used to override the default error - message. - - ``message`` argument new in SeleniumLibrary 3.2.0. - """ - actual = self.get_location() - if actual != url: - if is_noney(message): - message = ("Location should have been '%s' but " - "was '%s'." % (url, actual)) - raise AssertionError(message) - self.info("Current location is '%s'." % url) - - @keyword - def location_should_contain(self, expected, message=None): - """Verifies that current URL contains ``expected``. - - The ``expected`` argument contains the expected value in url. - - The ``message`` argument can be used to override the default error - message. - - ``message`` argument new in SeleniumLibrary 3.2.0. - """ - actual = self.get_location() - if expected not in actual: - if is_noney(message): - message = ("Location should have contained '%s' but " - "it was '%s'." % (expected, actual)) - raise AssertionError(message) - self.info("Current location contains '%s'." % expected) - - @keyword - def log_location(self): - """Logs and returns the current browser window URL.""" - url = self.get_location() - self.info(url) - return url - - @keyword - def log_source(self, loglevel='INFO'): - """Logs and returns the HTML source of the current page or frame. - - The ``loglevel`` argument defines the used log level. Valid log - levels are ``WARN``, ``INFO`` (default), ``DEBUG``, ``TRACE`` - and ``NONE`` (no logging). - """ - source = self.get_source() - self.log(source, loglevel) - return source - - @keyword - def log_title(self): - """Logs and returns the title of current page.""" - title = self.get_title() - self.info(title) - return title - - @keyword - def title_should_be(self, title, message=None): - """Verifies that current page title equals ``title``. - - The ``message`` argument can be used to override the default error - message. - - ``message`` argument is new in SeleniumLibrary 3.1. - """ - actual = self.get_title() - if actual != title: - if is_noney(message): - message = "Title should have been '%s' but was '%s'." % (title, actual) - raise AssertionError(message) - self.info("Page title is '%s'." % title) - - @keyword - def go_back(self): - """Simulates the user clicking the back button on their browser.""" - self.driver.back() - - @keyword - def go_to(self, url): - """Navigates the current browser window to the provided ``url``.""" - self.info("Opening url '%s'" % url) - self.driver.get(url) - - @keyword - def reload_page(self): - """Simulates user reloading page.""" - self.driver.refresh() - - def _log_list(self, items, what='item'): msg = [ 'Altogether %s %s%s.' From c635d48a440bb25cbec81ec32c0cc9ae8d98ce1c Mon Sep 17 00:00:00 2001 From: Snooz82 Date: Mon, 15 Jul 2019 10:57:34 +0200 Subject: [PATCH 10/22] Fixed ordering get_session_id --- .../keywords/browsermanagement.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/SeleniumLibrary/keywords/browsermanagement.py b/src/SeleniumLibrary/keywords/browsermanagement.py index 0dd1f4880..62675df8b 100644 --- a/src/SeleniumLibrary/keywords/browsermanagement.py +++ b/src/SeleniumLibrary/keywords/browsermanagement.py @@ -381,6 +381,14 @@ def get_browser_aliases(self): """ return self.drivers.active_aliases + @keyword + def get_session_id(self): + """Returns the currently active browser session id. + + New in SeleniumLibrary 3.2 + """ + return self.driver.session_id + @keyword def get_source(self): """Returns the entire HTML source of the current page or frame.""" @@ -492,14 +500,6 @@ def reload_page(self): """Simulates user reloading page.""" self.driver.refresh() - @keyword - def get_session_id(self): - """Returns the currently active browser session id. - - New in SeleniumLibrary 3.2 - """ - return self.driver.session_id - @keyword def get_selenium_speed(self): """Gets the delay that is waited after each Selenium command. From cfb63b420cced2ab545e5d5ae86d6f34f5e7f242 Mon Sep 17 00:00:00 2001 From: Snooz82 Date: Mon, 15 Jul 2019 11:09:37 +0200 Subject: [PATCH 11/22] Fixed Unittest to 173 Keywords instead of 170. --- src/SeleniumLibrary/locators/windowmanager.py | 5 ++++- utest/test/api/test_plugins.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/SeleniumLibrary/locators/windowmanager.py b/src/SeleniumLibrary/locators/windowmanager.py index d898d5bf9..be2e4135a 100644 --- a/src/SeleniumLibrary/locators/windowmanager.py +++ b/src/SeleniumLibrary/locators/windowmanager.py @@ -58,7 +58,10 @@ def get_window_handles(self, browser): return handles def get_window_infos(self, browser='CURRENT'): - current_index = self.drivers.current_index + try: + current_index = self.drivers.current_index + except AttributeError: + current_index = None if is_string(browser) and browser.upper() == 'ALL': infos = [] for index, driver in enumerate(self.drivers, 1): diff --git a/utest/test/api/test_plugins.py b/utest/test/api/test_plugins.py index 277e4501b..b594866c3 100644 --- a/utest/test/api/test_plugins.py +++ b/utest/test/api/test_plugins.py @@ -22,7 +22,7 @@ def setUpClass(cls): def test_no_libraries(self): for item in [None, 'None', '']: sl = SeleniumLibrary(plugins=item) - self.assertEqual(len(sl.get_keyword_names()), 170) + self.assertEqual(len(sl.get_keyword_names()), 173) def test_parse_library(self): plugin = 'path.to.MyLibrary' From 0230744e201042df943d3049d30f7452928ed6da Mon Sep 17 00:00:00 2001 From: Snooz82 Date: Mon, 15 Jul 2019 11:25:27 +0200 Subject: [PATCH 12/22] Set encoding to UTF-8 to be able to use all characters in Docstring --- src/SeleniumLibrary/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/SeleniumLibrary/__init__.py b/src/SeleniumLibrary/__init__.py index cb8467ab9..99ee9c67e 100644 --- a/src/SeleniumLibrary/__init__.py +++ b/src/SeleniumLibrary/__init__.py @@ -1,3 +1,6 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- + # Copyright 2008-2011 Nokia Networks # Copyright 2011-2016 Ryan Tomac, Ed Manlove and contributors # Copyright 2016- Robot Framework Foundation From 31bf5ea64a4f95d276a08ceb1ca3f7377adc9798 Mon Sep 17 00:00:00 2001 From: Snooz82 Date: Mon, 15 Jul 2019 22:20:57 +0200 Subject: [PATCH 13/22] Fixed Issues with aTest --- src/SeleniumLibrary/__init__.py | 16 +++++++--------- src/SeleniumLibrary/keywords/window.py | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/SeleniumLibrary/__init__.py b/src/SeleniumLibrary/__init__.py index 99ee9c67e..6f052636b 100644 --- a/src/SeleniumLibrary/__init__.py +++ b/src/SeleniumLibrary/__init__.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python -# -*- coding: UTF-8 -*- - # Copyright 2008-2011 Nokia Networks # Copyright 2011-2016 Ryan Tomac, Ed Manlove and contributors # Copyright 2016- Robot Framework Foundation @@ -287,12 +284,13 @@ class SeleniumLibrary(DynamicCore): For this two browsers would be necessary to be opened. Structure: - | Browser A (firefox) - | ├──── Window 1 (location=https://robotframework.org/) - | ├──── Window 2 (location=https://robocon.io/) - | └──── Window 3 (location=https://github.com/robotframework/) - | Browser B (firefox) - | └──── Window 1 (location=https://github.com/) + | BrowserA (firefox) + | Window 1 (location=https://robotframework.org/) + | Window 2 (location=https://robocon.io/) + | Window 3 (location=https://github.com/robotframework/) + | + | BrowserB (firefox) + | Window 1 (location=https://github.com/) Robot Framework Example: | `Open Browser` | https://robotframework.org | alias=BrowserA | # BrowserA with Window1 is open | diff --git a/src/SeleniumLibrary/keywords/window.py b/src/SeleniumLibrary/keywords/window.py index 8af4835c8..1036c9062 100644 --- a/src/SeleniumLibrary/keywords/window.py +++ b/src/SeleniumLibrary/keywords/window.py @@ -32,7 +32,7 @@ def __init__(self, ctx): @keyword def select_window(self, locator='MAIN', timeout=None): """DEPRECATED in SeleniumLibrary 4.0. , use `Switch Window` instead.""" - self.switch_window(locator, timeout) + return self.switch_window(locator, timeout) @keyword def switch_window(self, locator='MAIN', timeout=None, browser='CURRENT'): From 338170df8bd58377a9cab42e88fc4e3f4472b748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Rohner?= <> Date: Wed, 17 Jul 2019 00:06:25 +0200 Subject: [PATCH 14/22] Added acceptance tests for edited Window- and Browser-Information handling. Modified dynamic_content.html to be able to manipulate the Window title freely. --- .../multiple_browsers_multiple_windows.robot | 144 ++++++++++++++++++ .../html/javascript/dynamic_content.html | 22 ++- 2 files changed, 160 insertions(+), 6 deletions(-) create mode 100644 atest/acceptance/multiple_browsers_multiple_windows.robot diff --git a/atest/acceptance/multiple_browsers_multiple_windows.robot b/atest/acceptance/multiple_browsers_multiple_windows.robot new file mode 100644 index 000000000..09079662a --- /dev/null +++ b/atest/acceptance/multiple_browsers_multiple_windows.robot @@ -0,0 +1,144 @@ +*** Setting *** +Documentation These tests must open own browser because windows opened by +... earlier tests would otherwise be visible to Get Window XXX keywords +... even if those windows were closed. +Suite Setup Open 3 Browsers with Windows +Suite Teardown Close All Browsers +Resource resource.robot + +*** Variables *** +@{BrowserA_exp_Titles}= WindowA1 WindowA2 WindowA3 +@{BrowserB_exp_Titles}= WindowB1 WindowB2 +@{BrowserC_exp_Titles}= WindowC1 +@{All_Browsers_exp_Titles}= @{BrowserA_exp_Titles} @{BrowserB_exp_Titles} @{BrowserC_exp_Titles} +@{exp_Aliases}= BrowserA BrowserB BrowserC +@{exp_IDs}= ${1} ${2} ${3} + + +*** Test Cases *** +Check Titles of Multiple Browser-Windows + @{BrowserA_Titles}= Get Window Titles browser=BrowserA + Should Be Equal ${BrowserA_Titles} ${BrowserA_exp_Titles} + @{BrowserB_Titles}= Get Window Titles browser=BrowserB + Should Be Equal ${BrowserB_Titles} ${BrowserB_exp_Titles} + @{BrowserC_Titles}= Get Window Titles browser=BrowserC + Should Be Equal ${BrowserC_Titles} ${BrowserC_exp_Titles} + @{All_Browsers_Titles}= Get Window Titles browser=ALL + Should Be Equal ${All_Browsers_Titles} ${All_Browsers_exp_Titles} + +Check Count of Handle + Check Handle Count 3 BrowserA + Check Handle Count 2 BrowserB + Check Handle Count 1 BrowserC + Check Handle Count 6 ALL + +Check Count of Names + Check Name Count 3 BrowserA + Check Name Count 2 BrowserB + Check Name Count 1 BrowserC + Check Name Count 6 ALL + +Check Count of Identifiers + Check Identifiers Count 3 BrowserA + Check Identifiers Count 2 BrowserB + Check Identifiers Count 1 BrowserC + Check Identifiers Count 6 ALL + +Check Locations + @{Locations}= Get Locations browser=ALL + FOR ${index} ${location} IN ENUMERATE @{Locations} + Should Be Equal As Strings ${location} ${FRONT_PAGE}javascript/dynamic_content.html?${index+1} + END + ${count} Get Length ${Locations} + Should Be Equal As Integers 6 ${count} + +Get Browser Ids and Alias + @{Aliases}= Get Browser Aliases + Should Be Equal ${Aliases} ${exp_Aliases} + &{Aliases}= Get Browser Aliases + Should Be Equal ${Aliases.BrowserA} ${1} + Should Be Equal ${Aliases.BrowserB} ${2} + Should Be Equal ${Aliases.BrowserC} ${3} + @{IDs}= Get Browser Ids + Should Be Equal ${IDs} ${exp_IDs} + +Switch Window to Different Browser + Switch Browser BrowserC + Switch Window WindowC1 + Location Should Be ${FRONT_PAGE}javascript/dynamic_content.html?6 + Switch Window title:WindowA1 browser=BrowserA + Location Should Be ${FRONT_PAGE}javascript/dynamic_content.html?1 + Switch Window url:${FRONT_PAGE}javascript/dynamic_content.html?4 browser=BrowserB + Title Should Be WindowB1 + +Get Specific Locations and Title + Switch Browser BrowserA + Switch Window title:WindowA1 + Location Should Be ${FRONT_PAGE}javascript/dynamic_content.html?1 + @{Locations}= Get Locations browser=BrowserB + Should Be Equal @{Locations}[0] ${FRONT_PAGE}javascript/dynamic_content.html?4 + Should Be Equal @{Locations}[1] ${FRONT_PAGE}javascript/dynamic_content.html?5 + ${count}= Get Length ${Locations} + Should Be Equal As Integers ${count} 2 + @{Titles}= Get Window Titles browser=BrowserC + Should Be Equal @{Titles}[0] WindowC1 + ${count}= Get Length ${Titles} + Should Be Equal As Integers ${count} 1 + + +Fail Switching Window and Locations From Different Browser + Switch Browser BrowserA + Switch Window WindowA1 + ${Error_Msg}= Run Keyword And Expect Error * Switch Window WindowB1 + Should Be Equal As Strings ${Error_Msg} No window matching handle, name, title or URL 'WindowB1' found. + ${Error_Msg}= Run Keyword And Expect Error * Get Locations browser=UnknownBrowser + Should Be Equal As Strings ${Error_Msg} Non-existing index or alias 'UnknownBrowser'. + ${Error_Msg}= Run Keyword And Expect Error * Get Window Handles browser=${4} + Should Be Equal As Strings ${Error_Msg} Non-existing index or alias '4'. + + + +*** Keywords *** +Open 3 Browsers with Windows + Close All Browsers + Open Browser With Alias And Title BrowserA WindowA1 1 + Open New Window and set Title WindowA2 2 + Open New Window And Set Title WindowA3 3 + Open Browser With Alias And Title BrowserB WindowB1 4 + Open New Window And Set Title WindowB2 5 + Open Browser With Alias And Title BrowserC WindowC1 6 + +Open New Window And Set Title + [Arguments] ${title} ${id} + Execute Javascript window.open("dynamic_content.html?${id}") + Switch Window locator=NEW + Set Window Title ${title} + +Open Browser With Alias And Title + [Arguments] ${alias} ${title} ${id} + Open Browser ${FRONT_PAGE}javascript/dynamic_content.html?${id} ${BROWSER} alias=${alias} + Set Window Title ${title} + +Set Window Title + [Arguments] ${title} + Input Text id:titleChangeTxt ${title} + Click Button id:titleChangeBtn + Title Should Be ${title} + +Check Handle Count + [Arguments] ${length} ${browser_alias}=${None} + @{WinCountBrowser}= Get Window Handles ${browser_alias} + ${len}= Get Length ${WinCountBrowser} + Should Be Equal As Integers ${len} ${length} + +Check Name Count + [Arguments] ${length} ${browser_alias}=${None} + @{WinCountBrowser}= Get Window Names ${browser_alias} + ${len}= Get Length ${WinCountBrowser} + Should Be Equal As Integers ${len} ${length} + +Check Identifiers Count + [Arguments] ${length} ${browser_alias}=${None} + @{WinCountBrowser}= Get Window Identifiers ${browser_alias} + ${len}= Get Length ${WinCountBrowser} + Should Be Equal As Integers ${len} ${length} \ No newline at end of file diff --git a/atest/resources/html/javascript/dynamic_content.html b/atest/resources/html/javascript/dynamic_content.html index 396fed74b..284d5d4ee 100644 --- a/atest/resources/html/javascript/dynamic_content.html +++ b/atest/resources/html/javascript/dynamic_content.html @@ -17,10 +17,10 @@ add content
title to ääää

- Change Title
- Add Content
+ Change Title
+ Add Content

@@ -30,8 +30,18 @@ - - + +

+

+ + + + + + +
+

From 4bb064e9591c8eaf51ed86180cd2cc76ff899142 Mon Sep 17 00:00:00 2001 From: Snooz82 Date: Wed, 17 Jul 2019 00:38:27 +0200 Subject: [PATCH 15/22] Get rid of the for loop in atest because of Robot Framework 3.0.4 in Python 2.7 atests... --- .../acceptance/multiple_browsers_multiple_windows.robot | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/atest/acceptance/multiple_browsers_multiple_windows.robot b/atest/acceptance/multiple_browsers_multiple_windows.robot index 09079662a..f0f547002 100644 --- a/atest/acceptance/multiple_browsers_multiple_windows.robot +++ b/atest/acceptance/multiple_browsers_multiple_windows.robot @@ -46,9 +46,12 @@ Check Count of Identifiers Check Locations @{Locations}= Get Locations browser=ALL - FOR ${index} ${location} IN ENUMERATE @{Locations} - Should Be Equal As Strings ${location} ${FRONT_PAGE}javascript/dynamic_content.html?${index+1} - END + Should Be Equal As Strings @{Locations}[0] ${FRONT_PAGE}javascript/dynamic_content.html?1 + Should Be Equal As Strings @{Locations}[1] ${FRONT_PAGE}javascript/dynamic_content.html?2 + Should Be Equal As Strings @{Locations}[2] ${FRONT_PAGE}javascript/dynamic_content.html?3 + Should Be Equal As Strings @{Locations}[3] ${FRONT_PAGE}javascript/dynamic_content.html?4 + Should Be Equal As Strings @{Locations}[4] ${FRONT_PAGE}javascript/dynamic_content.html?5 + Should Be Equal As Strings @{Locations}[5] ${FRONT_PAGE}javascript/dynamic_content.html?6 ${count} Get Length ${Locations} Should Be Equal As Integers 6 ${count} From d12a0b378558017f2a94bf1ef1957e8249935f0c Mon Sep 17 00:00:00 2001 From: Snooz82 Date: Mon, 22 Jul 2019 12:16:02 +0200 Subject: [PATCH 16/22] Cleaned up all Lines that are only made out of whitespaces. --- src/SeleniumLibrary/keywords/browsermanagement.py | 4 ++-- src/SeleniumLibrary/keywords/cookie.py | 2 +- src/SeleniumLibrary/keywords/element.py | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/SeleniumLibrary/keywords/browsermanagement.py b/src/SeleniumLibrary/keywords/browsermanagement.py index 62675df8b..e39e875e1 100644 --- a/src/SeleniumLibrary/keywords/browsermanagement.py +++ b/src/SeleniumLibrary/keywords/browsermanagement.py @@ -180,7 +180,7 @@ def open_browser(self, url, browser='firefox', alias=None, | options = webdriver.ChromeOptions() | options.add_argument('--disable-dev-shm-usage') | return options - + Then the `${options}` variable can be used as argument to ``options``. @@ -388,7 +388,7 @@ def get_session_id(self): New in SeleniumLibrary 3.2 """ return self.driver.session_id - + @keyword def get_source(self): """Returns the entire HTML source of the current page or frame.""" diff --git a/src/SeleniumLibrary/keywords/cookie.py b/src/SeleniumLibrary/keywords/cookie.py index efff19e29..3d74a898a 100644 --- a/src/SeleniumLibrary/keywords/cookie.py +++ b/src/SeleniumLibrary/keywords/cookie.py @@ -52,7 +52,7 @@ def get_cookies(self, as_dict=False): sending HTTP requests. The dictionary format is helpful when the result can be passed to requests library's Create Session keyword's optional cookies parameter. - + The `` as_dict`` argument is new in SeleniumLibrary 3.3 """ if is_falsy(as_dict): diff --git a/src/SeleniumLibrary/keywords/element.py b/src/SeleniumLibrary/keywords/element.py index 880ea390c..317d9c16a 100644 --- a/src/SeleniumLibrary/keywords/element.py +++ b/src/SeleniumLibrary/keywords/element.py @@ -435,12 +435,12 @@ def get_element_size(self, locator): @keyword def cover_element(self, locator): """Will cover elements identified by ``locator`` with a blue div without breaking page layout. - + See the `Locating elements` section for details about the locator syntax. - + New in SeleniumLibrary 3.3.0 - + Example: |`Cover Element` | css:div#container | """ From a2af1cda1b5e5503c3893b72aa5aa4ff45ec51cc Mon Sep 17 00:00:00 2001 From: Snooz82 Date: Mon, 22 Jul 2019 12:17:46 +0200 Subject: [PATCH 17/22] fixed not needed parenthesis --- src/SeleniumLibrary/keywords/window.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SeleniumLibrary/keywords/window.py b/src/SeleniumLibrary/keywords/window.py index 1036c9062..14ed82332 100644 --- a/src/SeleniumLibrary/keywords/window.py +++ b/src/SeleniumLibrary/keywords/window.py @@ -20,7 +20,7 @@ from SeleniumLibrary.base import keyword, LibraryComponent from SeleniumLibrary.locators import WindowManager -from SeleniumLibrary.utils import (plural_or_not, is_string) +from SeleniumLibrary.utils import plural_or_not, is_string class WindowKeywords(LibraryComponent): From ce19471c59d876da140ec3d419893e3fd804c227 Mon Sep 17 00:00:00 2001 From: Snooz82 Date: Mon, 29 Jul 2019 01:19:31 +0200 Subject: [PATCH 18/22] Updating Doc. implementing review comments. --- .../multiple_browsers_multiple_windows.robot | 31 ++++++++------ src/SeleniumLibrary/__init__.py | 41 +++++++++++++------ .../keywords/browsermanagement.py | 7 ++++ 3 files changed, 54 insertions(+), 25 deletions(-) diff --git a/atest/acceptance/multiple_browsers_multiple_windows.robot b/atest/acceptance/multiple_browsers_multiple_windows.robot index f0f547002..8746d16fe 100644 --- a/atest/acceptance/multiple_browsers_multiple_windows.robot +++ b/atest/acceptance/multiple_browsers_multiple_windows.robot @@ -7,24 +7,31 @@ Suite Teardown Close All Browsers Resource resource.robot *** Variables *** -@{BrowserA_exp_Titles}= WindowA1 WindowA2 WindowA3 -@{BrowserB_exp_Titles}= WindowB1 WindowB2 -@{BrowserC_exp_Titles}= WindowC1 -@{All_Browsers_exp_Titles}= @{BrowserA_exp_Titles} @{BrowserB_exp_Titles} @{BrowserC_exp_Titles} -@{exp_Aliases}= BrowserA BrowserB BrowserC -@{exp_IDs}= ${1} ${2} ${3} +@{BrowserA_EXP_TITLES}= WindowA1 WindowA2 WindowA3 +@{BrowserB_EXP_TITLES}= WindowB1 WindowB2 +@{BrowserC_EXP_TITLES}= WindowC1 +@{ALL_BROWSERS_EXP_TITLES}= @{BrowserA_EXP_TITLES} @{BrowserB_EXP_TITLES} @{BrowserC_EXP_TITLES} +@{EXP_ALIASES}= BrowserA BrowserB BrowserC +@{EXP_IDS}= ${1} ${2} ${3} *** Test Cases *** Check Titles of Multiple Browser-Windows @{BrowserA_Titles}= Get Window Titles browser=BrowserA - Should Be Equal ${BrowserA_Titles} ${BrowserA_exp_Titles} + Should Be Equal ${BrowserA_Titles} ${BrowserA_EXP_TITLES} @{BrowserB_Titles}= Get Window Titles browser=BrowserB - Should Be Equal ${BrowserB_Titles} ${BrowserB_exp_Titles} + Should Be Equal ${BrowserB_Titles} ${BrowserB_EXP_TITLES} @{BrowserC_Titles}= Get Window Titles browser=BrowserC - Should Be Equal ${BrowserC_Titles} ${BrowserC_exp_Titles} + Should Be Equal ${BrowserC_Titles} ${BrowserC_EXP_TITLES} @{All_Browsers_Titles}= Get Window Titles browser=ALL - Should Be Equal ${All_Browsers_Titles} ${All_Browsers_exp_Titles} + Should Be Equal ${All_Browsers_Titles} ${ALL_BROWSERS_EXP_TITLES} + +Test + @{browser_ids}= Get Browser Ids + FOR ${id} IN @{browser_ids} + @{window_titles}= Get Window Titles browser=${id} + Log Browser ${id} has these windows: ${window_titles} + END Check Count of Handle Check Handle Count 3 BrowserA @@ -57,13 +64,13 @@ Check Locations Get Browser Ids and Alias @{Aliases}= Get Browser Aliases - Should Be Equal ${Aliases} ${exp_Aliases} + Should Be Equal ${Aliases} ${EXP_ALIASES} &{Aliases}= Get Browser Aliases Should Be Equal ${Aliases.BrowserA} ${1} Should Be Equal ${Aliases.BrowserB} ${2} Should Be Equal ${Aliases.BrowserC} ${3} @{IDs}= Get Browser Ids - Should Be Equal ${IDs} ${exp_IDs} + Should Be Equal ${IDs} ${EXP_IDS} Switch Window to Different Browser Switch Browser BrowserC diff --git a/src/SeleniumLibrary/__init__.py b/src/SeleniumLibrary/__init__.py index 6f052636b..d3386e9bf 100644 --- a/src/SeleniumLibrary/__init__.py +++ b/src/SeleniumLibrary/__init__.py @@ -226,32 +226,47 @@ class SeleniumLibrary(DynamicCore): = Browser and Window = - In this section the concept of Browsers or Webdrivers and its Windows + In this section the concept of Browsers or WebDrivers and its Windows will be explained. == Browser == - In Selenium Library there are two ways to start a Browser and control it. - The first one is the `Open Browser` keyword. This one should be used as + To control a browser, Selenium needs a BrowserDriver + that receives commands from [https://www.seleniumhq.org/docs/03_webdriver.jsp|Selenium WebDriver]. + (like GeckoDriver for Firefox, ChromeDriver for Chrome, etc) + These browser specific BrowserDrivers translates the WebDriver API to the browser specific API. + WebDriver Api is a [https://www.w3.org/TR/webdriver1/|W3C Standard]. + Each browser vendor has different browser APIs that may change with the version + of these browsers as well. + That the reason why there must be a specific version of the BrowserDriver + for SeleniumLibrary available, + that matches to the version of the remotely controlled browser. + Each started browser is controlled by a separate instance of Selenium WebDriver. + + In Selenium Library there are two keywords that start a browser + (and a WebDriver instance) to control the browser. + The first one is the `Open Browser` keyword, which can be used as default keyword. - The second one is the `Create Webdriver` keyword which was useful, when + The second one is the `Create WebDriver` keyword which was useful, when browser options needed to be set. Since Selenium Library 4.0.x `Open Browser` - also supports option why `Create Webdriver` will be deprecated. - When opening a browser a new Webdriver process is startet and connected to - Selenium Library. When using Selenium Grid this browser may be started on any - Grid node that has matching capabilities. - It is possible to start multiple independent browsers at the same test case. - These browsers do typically not share any data like sessions etc. + also supports options why `Create WebDriver` will be deprecated. + When opening a browser, a new WebDriver process is started and connected to + Selenium Library. When using [https://www.seleniumhq.org/docs/07_selenium_grid.jsp|Selenium Grid] + , this browser may be started on any Grid Node that has matching capabilities. + It is possible to start multiple independent browser instances at the same time. + These browsers are usually independent to each other and do not share data + like cookies, sessions or profiles. + Each browser starts with one window. One Browser may have multiple windows. In example when a Pop-Up has been opened or a new side is opened in a new browser tab. There are some browser related keywords: - - `Open Browser` to create a new browser object - - `Create Webdriver` to create a new browser object + - `Open Browser` to create a new browser/WebDriver object + - `Create Webdriver` to create a new browser/WebDriver object - `Get Browser Ids` to get the IDs of all open browsers - `Get Browser Aliases` to get the aliases of all open browsers - - `Switch Browser` to use a different opened browser as current browser + - `Switch Browser` to use a different opened browser as currently controlled browser - `Close Browser` closes the current browser and all its windows - `Close All Browsers` closes all browsers that has been opened by this library instance diff --git a/src/SeleniumLibrary/keywords/browsermanagement.py b/src/SeleniumLibrary/keywords/browsermanagement.py index e39e875e1..d98dfc05f 100644 --- a/src/SeleniumLibrary/keywords/browsermanagement.py +++ b/src/SeleniumLibrary/keywords/browsermanagement.py @@ -354,6 +354,13 @@ def switch_browser(self, index_or_alias): def get_browser_ids(self): """Returns index of all active browser as list. + Example: + | @{browser_ids}= | Get Browser Ids | | | + | FOR | ${id} | IN | @{browser_ids} | + | | @{window_titles}= | Get Window Titles | browser=${id} | + | | Log | Browser ${id} has these windows: ${window_titles} | | + | END | | | | + See `Switch Browser` for more information and examples. New in SeleniumLibrary 4.0 From b03365fce6a365af6f483094280758d03cec4e47 Mon Sep 17 00:00:00 2001 From: Snooz82 Date: Fri, 2 Aug 2019 00:48:35 +0200 Subject: [PATCH 19/22] Prepare Solve Conflict --- .../keywords/webdrivertools/__init__.py | 20 +++++++++ .../webdrivertools/sl_file_detector.py | 45 +++++++++++++++++++ .../{ => webdrivertools}/webdrivertools.py | 0 3 files changed, 65 insertions(+) create mode 100644 src/SeleniumLibrary/keywords/webdrivertools/__init__.py create mode 100644 src/SeleniumLibrary/keywords/webdrivertools/sl_file_detector.py rename src/SeleniumLibrary/keywords/{ => webdrivertools}/webdrivertools.py (100%) diff --git a/src/SeleniumLibrary/keywords/webdrivertools/__init__.py b/src/SeleniumLibrary/keywords/webdrivertools/__init__.py new file mode 100644 index 000000000..115e35abd --- /dev/null +++ b/src/SeleniumLibrary/keywords/webdrivertools/__init__.py @@ -0,0 +1,20 @@ +# Copyright 2008-2011 Nokia Networks +# Copyright 2011-2016 Ryan Tomac, Ed Manlove and contributors +# Copyright 2016- Robot Framework Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .webdrivertools import WebDriverCreator +from .webdrivertools import WebDriverCache +from .webdrivertools import SeleniumOptions +from .sl_file_detector import SelLibLocalFileDetector diff --git a/src/SeleniumLibrary/keywords/webdrivertools/sl_file_detector.py b/src/SeleniumLibrary/keywords/webdrivertools/sl_file_detector.py new file mode 100644 index 000000000..b920ecd2c --- /dev/null +++ b/src/SeleniumLibrary/keywords/webdrivertools/sl_file_detector.py @@ -0,0 +1,45 @@ +# Copyright 2008-2011 Nokia Networks +# Copyright 2011-2016 Ryan Tomac, Ed Manlove and contributors +# Copyright 2016- Robot Framework Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from robot.libraries.BuiltIn import BuiltIn +from selenium.webdriver.remote.file_detector import FileDetector, LocalFileDetector + +import SeleniumLibrary + + +class SelLibLocalFileDetector(FileDetector): + + def __init__(self): + self.selenium_file_detector = LocalFileDetector() + + def is_local_file(self, *keys): + if self.choose_file(): + return self.selenium_file_detector.is_local_file(*keys) + return None + + def choose_file(self): + try: + sl = self._get_sl() + except Exception: + sl = None + if sl and sl._running_keyword == 'choose_file': + return True + return False + + def _get_sl(self): + libraries = BuiltIn().get_library_instance(all=True) + for library in libraries: + if isinstance(libraries[library], SeleniumLibrary.SeleniumLibrary): + return libraries[library] diff --git a/src/SeleniumLibrary/keywords/webdrivertools.py b/src/SeleniumLibrary/keywords/webdrivertools/webdrivertools.py similarity index 100% rename from src/SeleniumLibrary/keywords/webdrivertools.py rename to src/SeleniumLibrary/keywords/webdrivertools/webdrivertools.py From e2cda089c638dcc3ffe251bbc6c51401abdfb2d8 Mon Sep 17 00:00:00 2001 From: Snooz82 Date: Fri, 2 Aug 2019 01:06:01 +0200 Subject: [PATCH 20/22] prepare for solve conflicts --- src/SeleniumLibrary/__init__.py | 4 ++++ src/SeleniumLibrary/keywords/browsermanagement.py | 2 +- src/SeleniumLibrary/keywords/formelement.py | 10 +++++++++- .../keywords/webdrivertools/webdrivertools.py | 7 +++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/SeleniumLibrary/__init__.py b/src/SeleniumLibrary/__init__.py index d3386e9bf..cc062b93e 100644 --- a/src/SeleniumLibrary/__init__.py +++ b/src/SeleniumLibrary/__init__.py @@ -499,13 +499,17 @@ def __init__(self, timeout=5.0, implicit_wait=0.0, self.event_firing_webdriver = self._parse_listener(event_firing_webdriver) else: self.event_firing_webdriver = None + self._running_keyword = None def run_keyword(self, name, args, kwargs): + self._running_keyword = name try: return DynamicCore.run_keyword(self, name, args, kwargs) except Exception: self.failure_occurred() raise + finally: + self._running_keyword = None def get_keyword_tags(self, name): tags = list(DynamicCore.get_keyword_tags(self, name)) diff --git a/src/SeleniumLibrary/keywords/browsermanagement.py b/src/SeleniumLibrary/keywords/browsermanagement.py index d98dfc05f..8a197b50a 100644 --- a/src/SeleniumLibrary/keywords/browsermanagement.py +++ b/src/SeleniumLibrary/keywords/browsermanagement.py @@ -212,7 +212,7 @@ def open_browser(self, url, browser='firefox', alias=None, method: | `Open Browser` | http://example.com | Chrome | options=add_argument("--disable-popup-blocking"); add_argument("--ignore-certificate-errors") | # Sting format | | ${options} = | Get Options | | | # Selenium options instance | - | `Open Browser` | http://example.com | Chrome | options=${options 2} | | + | `Open Browser` | http://example.com | Chrome | options=${options} | | If the provided configuration options are not enough, it is possible to use `Create Webdriver` to customize browser initialization even diff --git a/src/SeleniumLibrary/keywords/formelement.py b/src/SeleniumLibrary/keywords/formelement.py index f91710d1f..06d069256 100644 --- a/src/SeleniumLibrary/keywords/formelement.py +++ b/src/SeleniumLibrary/keywords/formelement.py @@ -251,11 +251,19 @@ def input_text(self, locator, text, clear=True): is not cleared from the element. Use `Input Password` if you do not want the given ``text`` to be logged. + If [https://github.com/SeleniumHQ/selenium/wiki/Grid2|Selenium Grid] + is used and the ``text`` argument points to a file in the file system, + then this keyword prevents the Selenium to transfer the file to the + Selenium Grid hub. Instead this keyword will send the ``text`` string + as is to the element. If file should be transferred to the hub and + upload should be performed, please use `Choose File` keyword. + See the `Locating elements` section for details about the locator syntax. See the `Boolean arguments` section how Boolean values are handled. - The `clear` argument is new in SeleniumLibrary 4.0 + Disabling the file upload the Selenium Grid node and the `clear` + argument are new in SeleniumLibrary 4.0 """ self.info("Typing text '%s' into text field '%s'." % (text, locator)) self._input_text_into_text_field(locator, text, clear) diff --git a/src/SeleniumLibrary/keywords/webdrivertools/webdrivertools.py b/src/SeleniumLibrary/keywords/webdrivertools/webdrivertools.py index 368cca9eb..889a88f56 100644 --- a/src/SeleniumLibrary/keywords/webdrivertools/webdrivertools.py +++ b/src/SeleniumLibrary/keywords/webdrivertools/webdrivertools.py @@ -27,6 +27,7 @@ from selenium.webdriver import FirefoxProfile from SeleniumLibrary.utils import is_falsy, is_truthy, is_noney, is_string +from SeleniumLibrary.keywords.webdrivertools.sl_file_detector import SelLibLocalFileDetector class WebDriverCreator(object): @@ -251,10 +252,16 @@ def create_iphone(self, desired_capabilities, remote_url, options=None, service_ def _remote(self, desired_capabilities, remote_url, profile_dir=None, options=None): remote_url = str(remote_url) + file_detector = self._get_sl_file_detector() return webdriver.Remote(command_executor=remote_url, browser_profile=profile_dir, options=options, + file_detector=file_detector, **desired_capabilities) + def _get_sl_file_detector(self): + # To ease unit testing. + return SelLibLocalFileDetector() + def _get_log_path(self, log_file): if is_noney(log_file): return None From dd6f77063068702c6e65dc122dc88fa4948d7f44 Mon Sep 17 00:00:00 2001 From: Snooz82 Date: Fri, 2 Aug 2019 02:18:57 +0200 Subject: [PATCH 21/22] Added an exapmle Test to switch to w window of another Browser by its Location using the Get Browser Ids Keyword --- .../multiple_browsers_multiple_windows.robot | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/atest/acceptance/multiple_browsers_multiple_windows.robot b/atest/acceptance/multiple_browsers_multiple_windows.robot index 8746d16fe..4e3571f4d 100644 --- a/atest/acceptance/multiple_browsers_multiple_windows.robot +++ b/atest/acceptance/multiple_browsers_multiple_windows.robot @@ -26,13 +26,6 @@ Check Titles of Multiple Browser-Windows @{All_Browsers_Titles}= Get Window Titles browser=ALL Should Be Equal ${All_Browsers_Titles} ${ALL_BROWSERS_EXP_TITLES} -Test - @{browser_ids}= Get Browser Ids - FOR ${id} IN @{browser_ids} - @{window_titles}= Get Window Titles browser=${id} - Log Browser ${id} has these windows: ${window_titles} - END - Check Count of Handle Check Handle Count 3 BrowserA Check Handle Count 2 BrowserB @@ -72,6 +65,14 @@ Get Browser Ids and Alias @{IDs}= Get Browser Ids Should Be Equal ${IDs} ${EXP_IDS} +Select Window by Location + Switch Browser BrowserA + Switch Window WindowA1 + Switch Window By Location ${FRONT_PAGE}javascript/dynamic_content.html?5 + ${location} Get Location + Should Be Equal ${FRONT_PAGE}javascript/dynamic_content.html?5 ${location} + Title Should Be WindowB2 + Switch Window to Different Browser Switch Browser BrowserC Switch Window WindowC1 @@ -151,4 +152,12 @@ Check Identifiers Count [Arguments] ${length} ${browser_alias}=${None} @{WinCountBrowser}= Get Window Identifiers ${browser_alias} ${len}= Get Length ${WinCountBrowser} - Should Be Equal As Integers ${len} ${length} \ No newline at end of file + Should Be Equal As Integers ${len} ${length} + +Switch Window By Location + [Arguments] ${selected_location} + @{IDs}= Get Browser Ids + :FOR ${id} IN @{IDs} + \ @{locations}= Get Locations browser=${id} + \ Run Keyword If '${selected_location}' in $locations + ... Switch Window url:${selected_location} browser=${id} From 83d8a9dfcf2b212a3fa578c219af1f010c4a29bc Mon Sep 17 00:00:00 2001 From: Tatu Aalto <2665023+aaltat@users.noreply.github.com> Date: Mon, 12 Aug 2019 14:16:10 +0300 Subject: [PATCH 22/22] Updated documentation --- src/SeleniumLibrary/__init__.py | 127 ++++++++++++-------------------- 1 file changed, 49 insertions(+), 78 deletions(-) diff --git a/src/SeleniumLibrary/__init__.py b/src/SeleniumLibrary/__init__.py index cc062b93e..0a53d434a 100644 --- a/src/SeleniumLibrary/__init__.py +++ b/src/SeleniumLibrary/__init__.py @@ -226,102 +226,73 @@ class SeleniumLibrary(DynamicCore): = Browser and Window = - In this section the concept of Browsers or WebDrivers and its Windows - will be explained. + There is different conseptual meaning when SeleniumLibrary talks + windows and browsers. This chapter explains those differences. == Browser == - To control a browser, Selenium needs a BrowserDriver - that receives commands from [https://www.seleniumhq.org/docs/03_webdriver.jsp|Selenium WebDriver]. - (like GeckoDriver for Firefox, ChromeDriver for Chrome, etc) - These browser specific BrowserDrivers translates the WebDriver API to the browser specific API. - WebDriver Api is a [https://www.w3.org/TR/webdriver1/|W3C Standard]. - Each browser vendor has different browser APIs that may change with the version - of these browsers as well. - That the reason why there must be a specific version of the BrowserDriver - for SeleniumLibrary available, - that matches to the version of the remotely controlled browser. - Each started browser is controlled by a separate instance of Selenium WebDriver. - - In Selenium Library there are two keywords that start a browser - (and a WebDriver instance) to control the browser. - The first one is the `Open Browser` keyword, which can be used as - default keyword. - The second one is the `Create WebDriver` keyword which was useful, when - browser options needed to be set. Since Selenium Library 4.0.x `Open Browser` - also supports options why `Create WebDriver` will be deprecated. - When opening a browser, a new WebDriver process is started and connected to - Selenium Library. When using [https://www.seleniumhq.org/docs/07_selenium_grid.jsp|Selenium Grid] - , this browser may be started on any Grid Node that has matching capabilities. - It is possible to start multiple independent browser instances at the same time. - These browsers are usually independent to each other and do not share data - like cookies, sessions or profiles. - - Each browser starts with one window. One Browser may have multiple windows. - In example when a Pop-Up has been opened or a new side is opened in a new - browser tab. - - There are some browser related keywords: - - `Open Browser` to create a new browser/WebDriver object - - `Create Webdriver` to create a new browser/WebDriver object - - `Get Browser Ids` to get the IDs of all open browsers - - `Get Browser Aliases` to get the aliases of all open browsers - - `Switch Browser` to use a different opened browser as currently controlled browser - - `Close Browser` closes the current browser and all its windows - - `Close All Browsers` closes all browsers that has been opened by this library instance - + 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 broser 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 to each other and do not share data like cookies, + sessions or profiles. Typicall when browser starts, it + creates a single window in the desktop. + == 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 content of the window. - Browser windows are children of a browser object. One browser may - have multiple windows. Windows can appear as tabs or as separate windows - with different position and size. - Windows of the same browser typically share the same sessions. This is - the reason why it is not possible to log in with two different users - of the same site in these windows. To solve this issue it is necessary - to work with two different browser objects. - There are multiple window related keywords. - - When working with multiple windows these two keywords are mostly relevant. - - `Switch Window` to use a different open window of a browser - - `Close Window` to close the currently used window - - To open a new window, the site has to open content as new tab. - It may also be possible to use `Execute Javascript` Keyword: + Windows are children of a WebDriver instance, in SeleniumLibrary + WebDriver is referred as browser. One browser may have multiple + windows. Windows can appear as tabs or as separate windows with + different position and size. Windows belonning 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 browser (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 - == Example == - - In this example there could be the required that the site github.com is open once with a logged - in ``User-A`` and once without any user logged in. - For this two browsers would be necessary to be opened. + In the example in below opens multiple browser and windows, + to demonstrate how the different keywords can be used to interact + with a browser and windows atteched to the browser. Structure: - | BrowserA (firefox) + | BrowserA | Window 1 (location=https://robotframework.org/) | Window 2 (location=https://robocon.io/) | Window 3 (location=https://github.com/robotframework/) | - | BrowserB (firefox) + | BrowserB | Window 1 (location=https://github.com/) - Robot Framework Example: - | `Open Browser` | https://robotframework.org | alias=BrowserA | # BrowserA with Window1 is open | - | `Execute Javascript` | window.open() | | | - | `Switch Window` | locator=NEW | | # Window2 opened and switched to it | - | `Go To` | https://robocon.io | | # Window2 go to robocon site | - | `Execute Javascript` | window.open() | | | - | ${handle} | `Switch Window` | locator=NEW | # Window3 opened and switched to it | - | `Go To` | https://github.com/robotframework/ | | # Window3 go to robot framework github site | - | `Open Browser` | https://github.com | alias=BrowserB | # BrowserB with Window1 is open | - | ${location} | `Get Location` | | # ${location} is https://www.github.com | - | `Switch Window` | ${handle} | browser=BrowserA | # BrowserA Window2 is selected | - | ${location} | `Get Location` | | # ${location} = https://robocon.io/ | - | @{locations} | `Get Locations` | | # @{locations} = [ 'https://robotframework.org/', 'https://robocon.io/', 'https://github.com/robotframework/' ] | - | @{locations} | `Get Locations` | browser=ALL | # @{locations} = [ 'https://robotframework.org/', 'https://robocon.io/', 'https://github.com/robotframework/', '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 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. | + | @{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