Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
46ceebc
Selenium options string to object
aaltat May 31, 2019
9a0ba84
Renamed test class and test name
aaltat Jun 1, 2019
4c60ba4
Selenium options support for other types
aaltat Jun 1, 2019
c29f2ad
Selenium options for methods
aaltat Jun 1, 2019
d99ab2d
Test for Chrome add_experimental_option
aaltat Jun 1, 2019
44de3bd
Test for options attributes
aaltat Jun 1, 2019
332cb88
Changed SeleniumOptions logic
aaltat Jun 2, 2019
12dcd1b
Options for Chrome, Firefox and Ie
aaltat Jun 2, 2019
eefcc8b
Removing not needed formatter from unit test
aaltat Jun 3, 2019
3cec432
Removed because Python3.4 can not keep list order with dict
aaltat Jun 3, 2019
5e1eb78
Cleaning unit tests
aaltat Jun 6, 2019
435f08d
Ie supports options and service_log_path from different version
aaltat Jun 6, 2019
1ed9761
Ie support for Selenium options
aaltat Jun 6, 2019
43746c5
Fixed test for Python 2
aaltat Jun 6, 2019
0dffd43
Selenium options for Opera
aaltat Jun 8, 2019
0473aee
Safari does not support Selenium options
aaltat Jun 8, 2019
c14343f
PhantomJS does not support Selenium options
aaltat Jun 8, 2019
575c5d4
Htmlunit does not support Selenium options
aaltat Jun 8, 2019
ca55383
Htmlunit with JS does not support Selenium options
aaltat Jun 8, 2019
f3113d3
Added Android to support options and fixed htmlunit also to support o…
aaltat Jun 8, 2019
cd2ff5e
Added missing tests
aaltat Jun 8, 2019
ac5b3c7
Remote URL and options with IE
aaltat Jun 11, 2019
b6e6d38
Remote URL and options with Opera
aaltat Jun 11, 2019
27dc8c1
Improved test
aaltat Jun 11, 2019
8f51376
Add support also for Selenium object instance
aaltat Jun 11, 2019
0767097
Improved attribute settting
aaltat Jun 13, 2019
5bc3e80
Options for iPhone
aaltat Jun 13, 2019
f668697
Options support for WebDriverCreator
aaltat Jun 14, 2019
4456616
Fix for pypy35
aaltat Jun 14, 2019
9b003d7
Support for Selenium options in Open Browser keyword
aaltat Jun 14, 2019
8cdd0c3
Test for not existing method in Selenium Options
aaltat Jun 15, 2019
164a1ee
Removing whitespace from acceptance tests
aaltat Jun 20, 2019
49aac86
Support for space before and after comma in Selenium options
aaltat Jun 20, 2019
ad58fe8
Documentation for Selenium options
aaltat Jun 20, 2019
24702f2
Fixed bug in doc and acceptance tests
aaltat Jun 20, 2019
7a1c51d
Changed options format
aaltat Jun 28, 2019
c19fb3f
Python 2 support for tokens
aaltat Jun 28, 2019
bed6a6e
More tests
aaltat Jun 28, 2019
603f47a
Fixed acceptance tests
aaltat Jun 28, 2019
4c39246
Tuning
aaltat Jun 28, 2019
79b7c17
Better splitting for semicolon
aaltat Jun 29, 2019
6364362
More tests
aaltat Jun 29, 2019
599e4c5
Fixed docs to tell new string format
aaltat Jun 29, 2019
3f1973f
Add skip for unit test in Jython
aaltat Jun 29, 2019
38e72eb
Improved documentation
aaltat Jul 1, 2019
273626a
Improved code readability for WebDriverCreator headless brosers
aaltat Jul 1, 2019
30abcf2
Improved WebDriverTools _has_* methods
aaltat Jul 1, 2019
f9b1f5f
Android uses Chrome options
aaltat Jul 2, 2019
d725905
Not being too strict about Selenium options instances
aaltat Jul 2, 2019
5f4f1ab
Added missing approval test file
aaltat Jul 2, 2019
c1b4f65
Adding test for few error cases
aaltat Jul 4, 2019
27e0370
Fixed docs for Android using Chrome options
aaltat Jul 4, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions atest/acceptance/multiple_browsers_options.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
*** Settings ***
Suite Teardown Close All Browsers
Library ../resources/testlibs/get_selenium_options.py
Resource resource.robot
Documentation Creating test which would work on all browser is not possible. When testing with other
... browser than Chrome it is OK that these test will fail. SeleniumLibrary CI is run with Chrome only
... and therefore there is tests for Chrome only.

*** Test Cases ***
Chrome Browser With Selenium Options As String
[Documentation]
... LOG 1:2 DEBUG GLOB: *"goog:chromeOptions"*
... LOG 1:2 DEBUG GLOB: *args": ["--disable-dev-shm-usage"?*
Open Browser ${FRONT PAGE} ${BROWSER} remote_url=${REMOTE_URL}
... desired_capabilities=${DESIRED_CAPABILITIES} options=add_argument("--disable-dev-shm-usage")

Chrome Browser With Selenium Options As String With Attirbute As True
[Documentation]
... LOG 1:2 DEBUG GLOB: *"goog:chromeOptions"*
... LOG 1:2 DEBUG GLOB: *args": ["--disable-dev-shm-usage"?*
... LOG 1:2 DEBUG GLOB: *"--headless"*
Open Browser ${FRONT PAGE} ${BROWSER} remote_url=${REMOTE_URL}
... desired_capabilities=${DESIRED_CAPABILITIES} options=add_argument ( "--disable-dev-shm-usage" ) ; headless = True

Chrome Browser With Selenium Options Object
[Documentation]
... LOG 2:2 DEBUG GLOB: *"goog:chromeOptions"*
... LOG 2:2 DEBUG GLOB: *args": ["--disable-dev-shm-usage"?*
${options} = Get Chrome Options
Open Browser ${FRONT PAGE} ${BROWSER} remote_url=${REMOTE_URL}
... desired_capabilities=${DESIRED_CAPABILITIES} options=${options}

Chrome Browser With Selenium Options Invalid Method
Run Keyword And Expect Error AttributeError: 'Options' object has no attribute 'not_here_method'
... Open Browser ${FRONT PAGE} ${BROWSER} remote_url=${REMOTE_URL}
... desired_capabilities=${DESIRED_CAPABILITIES} options=not_here_method("arg1")


Chrome Browser With Selenium Options Argument With Semicolon
[Documentation]
... LOG 1:2 DEBUG GLOB: *"goog:chromeOptions"*
... LOG 1:2 DEBUG GLOB: *["has;semicolon"*
Open Browser ${FRONT PAGE} ${BROWSER} remote_url=${REMOTE_URL}
... desired_capabilities=${DESIRED_CAPABILITIES} options=add_argument("has;semicolon")
7 changes: 7 additions & 0 deletions atest/resources/testlibs/get_selenium_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from selenium import webdriver


def get_chrome_options():
options = webdriver.ChromeOptions()
options.add_argument('--disable-dev-shm-usage')
return options
96 changes: 84 additions & 12 deletions src/SeleniumLibrary/keywords/browsermanagement.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def close_browser(self):
@keyword
def open_browser(self, url, browser='firefox', alias=None,
remote_url=False, desired_capabilities=None,
ff_profile_dir=None, service_log_path=None):
ff_profile_dir=None, options=None, service_log_path=None):
"""Opens a new browser instance to the given ``url``.

The ``browser`` argument specifies which browser to use, and the
Expand Down Expand Up @@ -119,6 +119,71 @@ def open_browser(self, url, browser='firefox', alias=None,
``ff_profile_dir`` can also be instance of the
[https://seleniumhq.github.io/selenium/docs/api/py/webdriver_firefox/selenium.webdriver.firefox.firefox_profile.html?highlight=firefoxprofile#selenium.webdriver.firefox.firefox_profile.FirefoxProfile|selenium.webdriver.FirefoxProfile].

Optional ``options`` argument allows to define browser specific
Selenium options. Example for Chrome, the ``options`` argument
allows defining the following
[https://seleniumhq.github.io/selenium/docs/api/py/webdriver_chrome/selenium.webdriver.chrome.options.html#selenium.webdriver.chrome.options.Options|methods and attributes]
and for Firefox these
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just checking to make sure Firefox profile does not overlap or interfere with Firefox options ..? Read through the API docs and it looks like no they are mutually exclusive.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those two partially overlap but generally Firefox options allows to do more. But they are not exclusive, one can define both and all should work fine. I did briefly tested the scenario and did not find problems. Although I did not dive deeply on the matter and I don't know if same setting is defined in both, which one has priority over other.

[https://seleniumhq.github.io/selenium/docs/api/py/webdriver_firefox/selenium.webdriver.firefox.options.html?highlight=firefox#selenium.webdriver.firefox.options.Options|methods and attributes]
are available. Please note that not all browsers supported by the
SeleniumLibrary have Selenium options available. Therefore please
consult the Selenium documentation which browsers do support
the Selenium options. If ``browser`` argument is `android` then
[https://seleniumhq.github.io/selenium/docs/api/py/webdriver_chrome/selenium.webdriver.chrome.options.html#selenium.webdriver.chrome.options.Options|Chrome options]
is used. Selenium options are also supported, when ``remote_url``
argument is used.

The SeleniumLibrary ``options`` argument accepts Selenium
options in two different formats: as a string and as Python object
which is an instance of the Selenium options class.

The string format allows to define Selenium options methods
or attributes and their arguments in Robot Framework test data.
The method and attributes names are case and space sensitive and
must match to the Selenium options methods and attributes names.
When defining a method, is must defined in similar way as in
python: method name, opening parenthesis, zero to many arguments
and closing parenthesis. If there is need to define multiple
arguments for a single method, arguments must be separated with
comma, just like in Python. Example: `add_argument("--headless")`
or `add_experimental_option("key", "value")`. Attributes are
defined in similar way as in Python: attribute name, equal sing
and attribute value. Example, `headless=True`. Multiple methods
and attributes must separated by a semicolon, example:
`add_argument("--headless");add_argument("--start-maximized")`.

Arguments allow defining Python data types and arguments are
evaluated by using Python
[https://docs.python.org/3/library/ast.html#ast.literal_eval|ast.literal_eval].
Strings must be quoted with single or double quotes, example "value"
or 'value'. It is also possible define other Python builtin
data types, example `True` or `None`, by not using quotes
around the arguments.

The string format is space friendly and usually spaces do not alter
the defining the methods or attributes. There are two exceptions.
In some Robot Framework test data formats, two or more spaces are
considered as cell separator and instead of defining a single
argument, two or more arguments may be defined. Spaces in string
arguments are not removed and are left as is. Example
`add_argument ( "--headless" )` is same as
`add_argument("--headless")`. But `add_argument(" --headless ")` is
not same same as `add_argument ( "--headless" )`, because
spaces inside of quotes are not removed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One difficulty with supporting a string formatted options is being backwards compatible over time or change the rules of how the string is formatted. You might try out the formatting with several users to see how it works for them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a worry which I also have, but there are some safe guards. Calling the Selenium options is as much as possible backwards compatible, because behind the scenes it does a similar trick as the Create WebDriver keyword with Python getattr, setattr and callable, more details in here. Therefore keyword leaves to the user to correctly defined and maintain the methods and attributes user wants to call.

But there is a part that worries me and that is the importing options. First Selenium options import path may change or the options class name may change. In the past, that part has been quite stable and I have not seen any changes in the upcoming Selenium 4.0 release either. But who knows what future will bring.

Also mobile devices, which are supported on the Open Browser are always done by using the webdriver.Remote. The remote WebDriver supports options object, but those mobile devices don't have their own opinions object (Like example Chrome or Firefox has). Therefore the keyword does not support creating options object for those mobile devices by using the string format. Also there is feature/bug when Selenium options instance object is passed directly in, for mobile devices, it will fail, but that feature can be lifted away. But there is some indication that for example Android might directly support Chrome options http://chromedriver.chromium.org/getting-started/getting-started---android Also I know that some users are using SeleniumLibrary for mobile testing because, according to some users feedback: "SeleniumLibrary has better keywords but is little bit tricky to setup." Therefore it might be interesting to support also options for mobile devices, but currently I lack the knowledge how that part actually works and the Travis testing infrastructure is not not setup (Although there seems to be support for mobile)

In any case, if the limitation in the string format is left as it is, it should be documented. Also we should remove the limitation when the Selenium options instance is passed in for mobile devices. That limitation should be easy to change, because now we support only two formats.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's impossible to make this fully future compatible because Selenium doesn't have a stable API for specifying options. The current approach to support specifying options like method('arg') and attr = 'value' has a benefit that users can adjust their code if Selenium changes.

Copy link
Contributor Author

@aaltat aaltat Jul 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is true. I still need to think should we do some extra for the mobile users. But perhaps easiest way is to:

  1. Fix the bug with mobile and Selenium options
  2. Android == Chrome for selenium options. + Need documentation.

Make alpha release and ask specially for people using mobile to take a look.

As last format ``options`` argument also support receiving
the Selenium options as Python class instance. In this case, the
instance is used as is and the SeleniumLibrary will not convert
the instance to other formats.
For example, if the following code return value is saved to
`${options}` variable in the Robot Framework data:
| options = webdriver.ChromeOptions()
| options.add_argument('--disable-dev-shm-usage')
| return options

Then the `${options}` variable can be used as argument to
``options``.

Optional ``service_log_path`` argument defines the name of the
file where to write the browser driver logs. If the
``service_log_path`` argument contain a marker ``{index}``, it
Expand All @@ -142,6 +207,13 @@ def open_browser(self, url, browser='firefox', alias=None,
| Should Be Equal | ${1_index} | ${4_index} | | | |
| Should Be Equal | ${2_index} | ${2} | | | |

Example when using
[https://seleniumhq.github.io/selenium/docs/api/py/webdriver_chrome/selenium.webdriver.chrome.options.html#selenium.webdriver.chrome.options.Options|Chrome options]
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} | |

If the provided configuration options are not enough, it is possible
to use `Create Webdriver` to customize browser initialization even
more.
Expand All @@ -150,10 +222,10 @@ def open_browser(self, url, browser='firefox', alias=None,
new in SeleniumLibrary 3.1.

Using ``alias`` to decide, is the new browser opened is new
in SeleniumLibrary 4.0. Also the ``service_log_path`` is new
in SeleniumLibrary 4.0. Support for ``ff_profile_dir`` accepting
instance of the `selenium.webdriver.FirefoxProfile` is new in
SeleniumLibrary 4.0.
in SeleniumLibrary 4.0. The ``options`` and ``service_log_path``
are new in SeleniumLibrary 4.0. Support for ``ff_profile_dir``
accepting instance of the `selenium.webdriver.FirefoxProfile`
is new in SeleniumLibrary 4.0.
"""
index = self.drivers.get_index(alias)
if index:
Expand All @@ -163,19 +235,19 @@ def open_browser(self, url, browser='firefox', alias=None,
return index
return self._make_new_browser(url, browser, alias, remote_url,
desired_capabilities, ff_profile_dir,
service_log_path)
options, service_log_path)

def _make_new_browser(self, url, browser='firefox', alias=None,
remote_url=False, desired_capabilities=None,
ff_profile_dir=None, service_log_path=None):
ff_profile_dir=None, options=None, service_log_path=None):
if is_truthy(remote_url):
self.info("Opening browser '%s' to base url '%s' through "
"remote server at '%s'." % (browser, url, remote_url))
else:
self.info("Opening browser '%s' to base url '%s'." % (browser, url))
driver = self._make_driver(browser, desired_capabilities,
ff_profile_dir, remote_url,
service_log_path)
options, service_log_path)
driver = self._wrap_event_firing_webdriver(driver)
try:
driver.get(url)
Expand Down Expand Up @@ -504,11 +576,11 @@ def set_browser_implicit_wait(self, value):
"""
self.driver.implicitly_wait(timestr_to_secs(value))

def _make_driver(self, browser, desired_capabilities=None,
profile_dir=None, remote=None, service_log_path=None):
def _make_driver(self, browser, desired_capabilities=None, profile_dir=None,
remote=None, options=None, service_log_path=None):
driver = WebDriverCreator(self.log_dir).create_driver(
browser=browser, desired_capabilities=desired_capabilities,
remote_url=remote, profile_dir=profile_dir, service_log_path=service_log_path)
browser=browser, desired_capabilities=desired_capabilities, remote_url=remote,
profile_dir=profile_dir, options=options, service_log_path=service_log_path)
driver.set_script_timeout(self.ctx.timeout)
driver.implicitly_wait(self.ctx.implicit_wait)
if self.ctx.speed:
Expand Down
Loading