diff --git a/atest/acceptance/multiple_browsers_options.robot b/atest/acceptance/multiple_browsers_options.robot new file mode 100644 index 000000000..680f4fdc5 --- /dev/null +++ b/atest/acceptance/multiple_browsers_options.robot @@ -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") diff --git a/atest/resources/testlibs/get_selenium_options.py b/atest/resources/testlibs/get_selenium_options.py new file mode 100644 index 000000000..91cbef2e1 --- /dev/null +++ b/atest/resources/testlibs/get_selenium_options.py @@ -0,0 +1,7 @@ +from selenium import webdriver + + +def get_chrome_options(): + options = webdriver.ChromeOptions() + options.add_argument('--disable-dev-shm-usage') + return options diff --git a/src/SeleniumLibrary/keywords/browsermanagement.py b/src/SeleniumLibrary/keywords/browsermanagement.py index 9d4c6d894..bc31324d3 100644 --- a/src/SeleniumLibrary/keywords/browsermanagement.py +++ b/src/SeleniumLibrary/keywords/browsermanagement.py @@ -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 @@ -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 + [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. + + 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 @@ -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. @@ -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: @@ -163,11 +235,11 @@ 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)) @@ -175,7 +247,7 @@ def _make_new_browser(self, url, browser='firefox', alias=None, 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) @@ -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: diff --git a/src/SeleniumLibrary/keywords/webdrivertools.py b/src/SeleniumLibrary/keywords/webdrivertools.py index 68c3e5844..98ffedc4f 100644 --- a/src/SeleniumLibrary/keywords/webdrivertools.py +++ b/src/SeleniumLibrary/keywords/webdrivertools.py @@ -13,16 +13,20 @@ # 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. +import ast +import importlib import inspect import os +import token import warnings +from tokenize import generate_tokens from robot.api import logger -from robot.utils import ConnectionCache +from robot.utils import ConnectionCache, StringIO from selenium import webdriver from selenium.webdriver import FirefoxProfile -from SeleniumLibrary.utils import is_falsy, is_truthy, is_noney +from SeleniumLibrary.utils import is_falsy, is_truthy, is_noney, is_string class WebDriverCreator(object): @@ -49,23 +53,26 @@ class WebDriverCreator(object): def __init__(self, log_dir): self.log_dir = log_dir + self.selenium_options = SeleniumOptions() def create_driver(self, browser, desired_capabilities, remote_url, - profile_dir=None, service_log_path=None): + profile_dir=None, options=None, service_log_path=None): + browser = self._normalise_browser_name(browser) creation_method = self._get_creator_method(browser) desired_capabilities = self._parse_capabilities(desired_capabilities, browser) service_log_path = self._get_log_path(service_log_path) + options = self.selenium_options.create(self.browser_names.get(browser), options) if service_log_path: logger.info('Browser driver log file created to: %s' % service_log_path) self._create_directory(service_log_path) if (creation_method == self.create_firefox or creation_method == self.create_headless_firefox): - return creation_method(desired_capabilities, remote_url, - profile_dir, service_log_path=service_log_path) - return creation_method(desired_capabilities, remote_url, service_log_path=service_log_path) + return creation_method(desired_capabilities, remote_url, profile_dir, + options=options, service_log_path=service_log_path) + return creation_method(desired_capabilities, remote_url, options=options, + service_log_path=service_log_path) def _get_creator_method(self, browser): - browser = browser.lower().replace(' ', '') if browser in self.browser_names: return getattr(self, 'create_{}'.format(self.browser_names[browser])) raise ValueError('{} is not a supported browser.'.format(browser)) @@ -105,8 +112,9 @@ def create_chrome(self, desired_capabilities, remote_url, options=None, service_ return self._remote(desired_capabilities, remote_url, options=options) return webdriver.Chrome(options=options, service_log_path=service_log_path, **desired_capabilities) - def create_headless_chrome(self, desired_capabilities, remote_url, service_log_path=None): - options = webdriver.ChromeOptions() + def create_headless_chrome(self, desired_capabilities, remote_url, options=None, service_log_path=None): + if not options: + options = webdriver.ChromeOptions() # Can be changed to options.headless = True when minimum Selenium version is 3.12.0 or greater. options.set_headless() return self.create_chrome(desired_capabilities, remote_url, options, service_log_path) @@ -136,86 +144,109 @@ def _geckodriver_log(self): return log_file def create_headless_firefox(self, desired_capabilities, remote_url, - ff_profile_dir, service_log_path=None): - options = webdriver.FirefoxOptions() + ff_profile_dir, options=None, service_log_path=None): + if not options: + options = webdriver.FirefoxOptions() # Can be changed to options.headless = True when minimum Selenium version is 3.12.0 or greater. options.set_headless() return self.create_firefox(desired_capabilities, remote_url, ff_profile_dir, options, service_log_path) - def create_ie(self, desired_capabilities, remote_url, service_log_path=None): + def create_ie(self, desired_capabilities, remote_url, options=None, service_log_path=None): if is_truthy(remote_url): defaul_caps = webdriver.DesiredCapabilities.INTERNETEXPLORER.copy() desired_capabilities = self._remote_capabilities_resolver(desired_capabilities, defaul_caps) - return self._remote(desired_capabilities, remote_url) - if self._has_service_log_path(webdriver.Ie): - return webdriver.Ie(service_log_path=service_log_path, **desired_capabilities) - logger.warn('This version of Selenium does not support service_log_path argument.') + return self._remote(desired_capabilities, remote_url, options=options) + if self._has_service_log_path(webdriver.Ie) and self._has_options(webdriver.Ie): + # service_log_path is supported from Selenium 3.14 onwards + # If can be removed when minimum Selenium version is 3.14.0 or greater + return webdriver.Ie(options=options, service_log_path=service_log_path, **desired_capabilities) + elif not self._has_service_log_path(webdriver.Ie) and self._has_options(webdriver.Ie): + # options is supported from Selenium 3.10 onwards + # If can be removed when minimum Selenium version is 3.10.0 or greater + logger.warn('This version of Selenium does not support service_log_path argument.') + return webdriver.Ie(options=options, **desired_capabilities) + logger.warn('This version of Selenium does not support options or service_log_path argument.') return webdriver.Ie(**desired_capabilities) def _has_service_log_path(self, web_driver): signature = inspect.getargspec(web_driver.__init__) - return True if 'service_log_path' in signature.args else False + return 'service_log_path' in signature.args + + def _has_options(self, web_driver): + signature = inspect.getargspec(web_driver.__init__) + return 'options' in signature.args - def create_edge(self, desired_capabilities, remote_url, service_log_path=None): + def create_edge(self, desired_capabilities, remote_url, options=None, service_log_path=None): if is_truthy(remote_url): defaul_caps = webdriver.DesiredCapabilities.EDGE.copy() desired_capabilities = self._remote_capabilities_resolver(desired_capabilities, defaul_caps) return self._remote(desired_capabilities, remote_url) - if self._has_service_log_path(webdriver.Ie): + if self._has_options(webdriver.Edge) and self._has_service_log_path(webdriver.Edge): + # options is supported from Selenium 4.0 onwards + # If can be removed when minimum Selenium version is 4.0 or greater + return webdriver.Edge(options=options, service_log_path=service_log_path, **desired_capabilities) + if not self._has_options(webdriver.Edge) and self._has_service_log_path(webdriver.Edge): + # service_log_path is supported from Selenium 3.14 onwards + # If can be removed when minimum Selenium version is 3.14.0 or greater + logger.warn('This version of Selenium does not support options argument.') return webdriver.Edge(service_log_path=service_log_path, **desired_capabilities) - logger.warn('This version of Selenium does not support service_log_path argument.') + logger.warn('This version of Selenium does not support options or service_log_path argument.') return webdriver.Edge(**desired_capabilities) - def create_opera(self, desired_capabilities, remote_url, service_log_path=None): + def create_opera(self, desired_capabilities, remote_url, options=None, service_log_path=None): if is_truthy(remote_url): defaul_caps = webdriver.DesiredCapabilities.OPERA.copy() desired_capabilities = self._remote_capabilities_resolver(desired_capabilities, defaul_caps) - return self._remote(desired_capabilities, remote_url) - return webdriver.Opera(service_log_path=service_log_path, **desired_capabilities) + return self._remote(desired_capabilities, remote_url, options=options) + return webdriver.Opera(options=options, service_log_path=service_log_path, **desired_capabilities) - def create_safari(self, desired_capabilities, remote_url): + def create_safari(self, desired_capabilities, remote_url, options=None, service_log_path=None): if is_truthy(remote_url): defaul_caps = webdriver.DesiredCapabilities.SAFARI.copy() desired_capabilities = self._remote_capabilities_resolver(desired_capabilities, defaul_caps) return self._remote(desired_capabilities, remote_url) + if options or service_log_path: + logger.warn('Safari browser does not support Selenium options or service_log_path.') return webdriver.Safari(**desired_capabilities) - def create_phantomjs(self, desired_capabilities, remote_url, service_log_path=None): + def create_phantomjs(self, desired_capabilities, remote_url, options=None, service_log_path=None): warnings.warn('SeleniumLibrary support for PhantomJS has been deprecated, ' 'please use headlesschrome or headlessfirefox instead.') if is_truthy(remote_url): defaul_caps = webdriver.DesiredCapabilities.PHANTOMJS.copy() desired_capabilities = self._remote_capabilities_resolver(desired_capabilities, defaul_caps) return self._remote(desired_capabilities, remote_url) + if options: + logger.warn('PhantomJS browser does not support Selenium options.') return webdriver.PhantomJS(service_log_path=service_log_path, **desired_capabilities) - def create_htmlunit(self, desired_capabilities, remote_url, service_log_path=None): - if service_log_path: - logger.warn('Htmlunit does not support service_log_path argument.') + def create_htmlunit(self, desired_capabilities, remote_url, options=None, service_log_path=None): + if service_log_path or options: + logger.warn('Htmlunit does not support Selenium options or service_log_path argument.') defaul_caps = webdriver.DesiredCapabilities.HTMLUNIT.copy() desired_capabilities = self._remote_capabilities_resolver(desired_capabilities, defaul_caps) - return self._remote(desired_capabilities, remote_url) + return self._remote(desired_capabilities, remote_url, options=options) - def create_htmlunit_with_js(self, desired_capabilities, remote_url, service_log_path=None): - if service_log_path: - logger.warn('Htmlunit does not support service_log_path argument.') + def create_htmlunit_with_js(self, desired_capabilities, remote_url, options=None, service_log_path=None): + if service_log_path or options: + logger.warn('Htmlunit with JS does not support service_log_path argument.') defaul_caps = webdriver.DesiredCapabilities.HTMLUNITWITHJS.copy() desired_capabilities = self._remote_capabilities_resolver(desired_capabilities, defaul_caps) - return self._remote(desired_capabilities, remote_url) + return self._remote(desired_capabilities, remote_url, options=options) - def create_android(self, desired_capabilities, remote_url, service_log_path=None): + def create_android(self, desired_capabilities, remote_url, options=None, service_log_path=None): if service_log_path: logger.warn('Android does not support service_log_path argument.') defaul_caps = webdriver.DesiredCapabilities.ANDROID.copy() desired_capabilities = self._remote_capabilities_resolver(desired_capabilities, defaul_caps) - return self._remote(desired_capabilities, remote_url) + return self._remote(desired_capabilities, remote_url, options=options) - def create_iphone(self, desired_capabilities, remote_url, service_log_path=None): + def create_iphone(self, desired_capabilities, remote_url, options=None, service_log_path=None): if service_log_path: logger.warn('iPhone does not support service_log_path argument.') defaul_caps = webdriver.DesiredCapabilities.IPHONE.copy() desired_capabilities = self._remote_capabilities_resolver(desired_capabilities, defaul_caps) - return self._remote(desired_capabilities, remote_url) + return self._remote(desired_capabilities, remote_url, options=options) def _remote(self, desired_capabilities, remote_url, profile_dir=None, options=None): @@ -241,6 +272,9 @@ def _create_directory(self, path): if not os.path.exists(target_dir): os.makedirs(target_dir) + def _normalise_browser_name(self, browser): + return browser.lower().replace(' ', '') + class WebDriverCache(ConnectionCache): @@ -309,3 +343,65 @@ def _get_index(self, alias_or_index): return self._resolve_alias_or_index(alias_or_index) except ValueError: return None + + +class SeleniumOptions(object): + + def create(self, browser, options): + if is_falsy(options): + return None + selenium_options = self._import_options(browser) + if not is_string(options): + return options + options = self._parse(options) + selenium_options = selenium_options() + for option in options: + for key in option: + attr = getattr(selenium_options, key) + if callable(attr): + attr(*option[key]) + else: + setattr(selenium_options, key, *option[key]) + return selenium_options + + def _import_options(self, browser): + if browser == 'android': + browser = 'chrome' # Android uses ChromeOptions() + browser = browser.replace('headless_', '', 1) + options = importlib.import_module('selenium.webdriver.%s.options' % browser) + return options.Options + + def _parse(self, options): + result = [] + for item in self._split(options): + try: + result.append(self._parse_to_tokens(item)) + except ValueError: + raise ValueError('Unable to parse option: "%s"' % item) + return result + + def _parse_to_tokens(self, item): + result = {} + method = None + arguments = [] + tokens = generate_tokens(StringIO(item).readline) + for toknum, tokval, _, _, _ in tokens: + if toknum == token.NAME and not method: + method = tokval + elif toknum == token.STRING: + arguments.append(ast.literal_eval(tokval)) + elif toknum in [token.NAME, token.NUMBER] and method: + arguments.append(ast.literal_eval(tokval)) + result[method] = arguments + return result + + def _split(self, options): + split_options = [] + start_position = 0 + tokens = generate_tokens(StringIO(options).readline) + for toknum, tokval, tokpos, _, _ in tokens: + if toknum == token.OP and tokval == ';': + split_options.append(options[start_position:tokpos[1]]) + start_position = tokpos[1] + 1 + split_options.append(options[start_position:]) + return split_options diff --git a/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_create_with_android.approved.txt b/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_create_with_android.approved.txt new file mode 100644 index 000000000..4ec4a5658 --- /dev/null +++ b/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_create_with_android.approved.txt @@ -0,0 +1,3 @@ +Selenium options with android + +0) [[], {'androidPackage': 'com.android.chrome'}] diff --git a/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_get_options.approved.txt b/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_get_options.approved.txt new file mode 100644 index 000000000..8b9a62aaa --- /dev/null +++ b/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_get_options.approved.txt @@ -0,0 +1,3 @@ +Selenium options with string. + +0) ['--proxy-server=66.97.38.58:80'] diff --git a/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_importer.approved.txt b/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_importer.approved.txt new file mode 100644 index 000000000..21faaba19 --- /dev/null +++ b/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_importer.approved.txt @@ -0,0 +1,15 @@ +Selenium options import + +0) +1) +2) +3) +4) +5) +6) +7) phantomjs No module named +8) safari No module named +9) htmlunit No module named +10) htmlunit_with_js No module named +11) +12) iphone No module named diff --git a/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_options_create.approved.txt b/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_options_create.approved.txt new file mode 100644 index 000000000..6c55e276f --- /dev/null +++ b/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_options_create.approved.txt @@ -0,0 +1,9 @@ +Selenium options + +0) ['--disable-dev-shm-usage'] +1) ['--disable-dev-shm-usage', '--headless'] +2) ['--disable-dev-shm-usage', '--headless', '--proxy-server=66.97.38.58:80'] +3) setattr +4) ['--disable-dev-shm-usage'] +5) None +6) None diff --git a/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_parse_options_string.approved.txt b/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_parse_options_string.approved.txt new file mode 100644 index 000000000..b065be279 --- /dev/null +++ b/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_parse_options_string.approved.txt @@ -0,0 +1,18 @@ +Selenium options string to dict + +0) [{'method': ['arg1']}] +1) [{'method': ['arg1', 'arg2']}] +2) [{'method': [True]}] +3) [{'method': [1]}] +4) [{'method': ['arg1', 2, None, False, 'arg2']}] +5) [{'method': [' arg1 ', 2, None, False, ' arg2 ']}] +6) [{'attribute': ['arg1']}] +7) [{'attribute': [True]}] +8) [{'method': ['arg1']}, {'attribute': [True]}] +9) [{'method': ['arg1']}, {'attribute': [True]}, {'method': ['arg2']}] +10) [{'attribute': []}] +11) [{'method': []}] +12) [{'method': ['--proxy 10.10.1.3:2345']}] +13) [{'method': [';arg1']}] +14) [{'method': ['arg1', 2, 'arg2']}] +15) [{'method': ['arg1']}] diff --git a/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_parse_options_string_errors.approved.txt b/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_parse_options_string_errors.approved.txt new file mode 100644 index 000000000..d83ca949e --- /dev/null +++ b/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_parse_options_string_errors.approved.txt @@ -0,0 +1,9 @@ +Selenium options string errors + +0) method("arg1) Unable to parse option: "method("arg1)" +1) method(arg1") Unable to parse option: "method(arg1")" +2) method(arg1) Unable to parse option: "method(arg1)" +3) attribute=arg1 Unable to parse option: "attribute=arg1" +4) attribute=webdriver Unable to parse option: "attribute=webdriver" +5) method(argument="value") Unable to parse option: "method(argument="value")" +6) [{'method': ['key', 'value']}] diff --git a/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_split_options.approved.txt b/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_split_options.approved.txt new file mode 100644 index 000000000..05775fd3e --- /dev/null +++ b/utest/test/keywords/approved_files/SeleniumOptionsParserTests.test_split_options.approved.txt @@ -0,0 +1,8 @@ +Selenium options string splitting + +0) ['method("arg1")', 'method("arg2")'] +1) ['method("arg1")'] +2) ['attribute=True'] +3) ['attribute="semi;colons;middle"', 'other_attribute=True'] +4) ['method("arg1;")', 'method(";arg2;")'] +5) [' method ( " arg1 ") ', ' method ( " arg2 " ) '] diff --git a/utest/test/keywords/test_keyword_arguments_browsermanagement.py b/utest/test/keywords/test_keyword_arguments_browsermanagement.py index b2daef532..5cda8a556 100644 --- a/utest/test/keywords/test_keyword_arguments_browsermanagement.py +++ b/utest/test/keywords/test_keyword_arguments_browsermanagement.py @@ -22,12 +22,12 @@ def test_open_browser(self): remote_url = '"http://localhost:4444/wd/hub"' browser = mock() when(self.brorser)._make_driver('firefox', None, - None, False, None).thenReturn(browser) + None, False, None, None).thenReturn(browser) alias = self.brorser.open_browser(url) self.assertEqual(alias, None) when(self.brorser)._make_driver('firefox', None, - None, remote_url, None).thenReturn(browser) + None, remote_url, None, None).thenReturn(browser) alias = self.brorser.open_browser(url, alias='None', remote_url=remote_url) self.assertEqual(alias, None) diff --git a/utest/test/keywords/test_selenium_options_parser.py b/utest/test/keywords/test_selenium_options_parser.py new file mode 100644 index 000000000..683da52eb --- /dev/null +++ b/utest/test/keywords/test_selenium_options_parser.py @@ -0,0 +1,373 @@ +import inspect +import unittest +import os + +from mockito import mock, when, unstub, ANY +from robot.utils import JYTHON +from selenium import webdriver + +try: + from approvaltests.approvals import verify_all + from approvaltests.reporters.generic_diff_reporter_factory import GenericDiffReporterFactory +except ImportError: + if JYTHON: + verify = None + GenericDiffReporterFactory = None + else: + raise + +from SeleniumLibrary.keywords.webdrivertools import SeleniumOptions, WebDriverCreator + + +class SeleniumOptionsParserTests(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.options = SeleniumOptions() + + def setUp(self): + path = os.path.dirname(__file__) + reporter_json = os.path.abspath(os.path.join(path, '..', 'approvals_reporters.json')) + factory = GenericDiffReporterFactory() + factory.load(reporter_json) + self.reporter = factory.get_first_working() + self.results = [] + + @unittest.skipIf(JYTHON, 'ApprovalTest does not work with Jython') + def test_parse_options_string(self): + self.results.append(self.options._parse('method("arg1")')) + self.results.append(self.options._parse('method("arg1", "arg2")')) + self.results.append(self.options._parse('method(True)')) + self.results.append(self.options._parse('method(1)')) + self.results.append(self.options._parse('method("arg1", 2, None, False, "arg2")')) + self.results.append(self.options._parse('method ( " arg1 " , 2 , None , False , " arg2 " )')) + self.results.append(self.options._parse('attribute="arg1"')) + self.results.append(self.options._parse('attribute = True')) + self.results.append(self.options._parse('method("arg1");attribute=True')) + self.results.append(self.options._parse('method("arg1") ; attribute=True ; method("arg2")')) + self.results.append(self.options._parse('attribute')) + self.results.append(self.options._parse('method()')) + self.results.append(self.options._parse('method("--proxy 10.10.1.3:2345")')) + self.results.append(self.options._parse('method(";arg1")')) + self.results.append(self.options._parse('method ( "arg1" , 2 ,"arg2" )')) + self.results.append(self.options._parse("method('arg1')")) + verify_all('Selenium options string to dict', self.results, reporter=self.reporter) + + @unittest.skipIf(JYTHON, 'ApprovalTest does not work with Jython') + def test_parse_options_string_errors(self): + self.results.append(self.error_formatter(self.options._parse, 'method("arg1)', True)) + self.results.append(self.error_formatter(self.options._parse, 'method(arg1")', True)) + self.results.append(self.error_formatter(self.options._parse, 'method(arg1)', True)) + self.results.append(self.error_formatter(self.options._parse, 'attribute=arg1', True)) + self.results.append(self.error_formatter(self.options._parse, 'attribute=webdriver', True)) + self.results.append(self.error_formatter(self.options._parse, 'method(argument="value")', True)) + self.results.append(self.error_formatter(self.options._parse, 'method({"key": "value"})', True)) + verify_all('Selenium options string errors', self.results, reporter=self.reporter) + + @unittest.skipIf(JYTHON, 'ApprovalTest does not work with Jython') + def test_split_options(self): + self.results.append(self.options._split('method("arg1");method("arg2")')) + self.results.append(self.options._split('method("arg1")')) + self.results.append(self.options._split('attribute=True')) + self.results.append(self.options._split('attribute="semi;colons;middle";other_attribute=True')) + self.results.append(self.options._split('method("arg1;");method(";arg2;")')) + self.results.append(self.options._split(' method ( " arg1 ") ; method ( " arg2 " ) ')) + verify_all('Selenium options string splitting', self.results, reporter=self.reporter) + + @unittest.skipIf(JYTHON, 'ApprovalTest does not work with Jython') + def test_options_create(self): + options = 'add_argument("--disable-dev-shm-usage")' + sel_options = self.options.create('chrome', options) + self.results.append(sel_options.arguments) + + options = '%s;add_argument("--headless")' % options + sel_options = self.options.create('chrome', options) + self.results.append(sel_options.arguments) + + options = '%s;add_argument("--proxy-server=66.97.38.58:80")' % options + sel_options = self.options.create('chrome', options) + self.results.append(sel_options.arguments) + + options = '%s;binary_location("too", "many", "args")' % options + try: + self.options.create('chrome', options) + except Exception as error: + self.results.append(error.__str__()[:7]) + + chrome_options = webdriver.ChromeOptions() + chrome_options.add_argument('--disable-dev-shm-usage') + sel_options = self.options.create('chrome', chrome_options) + self.results.append(sel_options.arguments) + + sel_options = self.options.create('chrome', None) + self.results.append(sel_options) + + sel_options = self.options.create('chrome', 'None') + self.results.append(sel_options) + + verify_all('Selenium options', self.results, reporter=self.reporter) + + @unittest.skipIf(JYTHON, 'ApprovalTest does not work with Jython') + def test_create_with_android(self): + chrome_options = webdriver.ChromeOptions() + chrome_options.add_experimental_option('androidPackage', 'com.android.chrome') + sel_options = self.options.create('android', chrome_options) + self.results.append([sel_options.arguments, sel_options.experimental_options]) + verify_all('Selenium options with android', self.results, reporter=self.reporter) + + @unittest.skipIf(JYTHON, 'ApprovalTest does not work with Jython') + def test_get_options(self): + options = 'add_argument("--proxy-server=66.97.38.58:80")' + sel_options = self.options.create('chrome', options) + self.results.append(sel_options.arguments) + verify_all('Selenium options with string.', self.results, reporter=self.reporter) + + @unittest.skipIf(JYTHON, 'ApprovalTest does not work with Jython') + def test_importer(self): + self.results.append(self.options._import_options('firefox')) + self.results.append(self.options._import_options('headless_firefox')) + self.results.append(self.options._import_options('chrome')) + self.results.append(self.options._import_options('headless_chrome')) + self.results.append(self.options._import_options('ie')) + self.results.append(self.options._import_options('opera')) + self.results.append(self.options._import_options('edge')) + self.results.append(self.error_formatter(self.options._import_options, 'phantomjs')) + self.results.append(self.error_formatter(self.options._import_options, 'safari')) + self.results.append(self.error_formatter(self.options._import_options, 'htmlunit')) + self.results.append(self.error_formatter(self.options._import_options, 'htmlunit_with_js')) + self.results.append(self.options._import_options('android')) + self.results.append(self.error_formatter(self.options._import_options, 'iphone')) + verify_all('Selenium options import', self.results, reporter=self.reporter) + + def error_formatter(self, method, arg, full=False): + try: + return method(arg) + except Exception as error: + if full: + return '%s %s' % (arg, error) + return '%s %s' % (arg, error.__str__()[:15]) + + +class UsingSeleniumOptionsTests(unittest.TestCase): + + @classmethod + def setUpClass(cls): + curr_dir = os.path.dirname(os.path.abspath(__file__)) + cls.output_dir = os.path.abspath( + os.path.join(curr_dir, '..', '..', 'output_dir')) + cls.creator = WebDriverCreator(cls.output_dir) + + def tearDown(self): + unstub() + + def test_create_chrome_with_options(self): + options = mock() + expected_webdriver = mock() + when(webdriver).Chrome(service_log_path=None, options=options).thenReturn(expected_webdriver) + driver = self.creator.create_chrome({}, None, options=options) + self.assertEqual(driver, expected_webdriver) + + def test_create_chrome_with_options_and_remote_url(self): + url = 'http://localhost:4444/wd/hub' + caps = webdriver.DesiredCapabilities.CHROME.copy() + options = mock() + expected_webdriver = mock() + when(webdriver).Remote(command_executor=url, + desired_capabilities=caps, + browser_profile=None, + options=options).thenReturn(expected_webdriver) + driver = self.creator.create_chrome({}, url, options=options) + self.assertEqual(driver, expected_webdriver) + + def test_create_headless_chrome_with_options(self): + options = mock() + expected_webdriver = mock() + when(webdriver).Chrome(service_log_path=None, options=options).thenReturn(expected_webdriver) + driver = self.creator.create_headless_chrome({}, None, options=options) + self.assertEqual(driver, expected_webdriver) + + def test_create_firefox_with_options(self): + log_file = os.path.join(self.output_dir, 'geckodriver-1.log') + options = mock() + profile = mock() + expected_webdriver = mock() + when(webdriver).FirefoxProfile().thenReturn(profile) + when(webdriver).Firefox(options=options, firefox_profile=profile, + service_log_path=log_file).thenReturn(expected_webdriver) + driver = self.creator.create_firefox({}, None, None, options=options) + self.assertEqual(driver, expected_webdriver) + + def test_create_firefox_with_options_and_remote_url(self): + url = 'http://localhost:4444/wd/hub' + profile = mock() + when(webdriver).FirefoxProfile().thenReturn(profile) + caps = webdriver.DesiredCapabilities.FIREFOX.copy() + options = mock() + expected_webdriver = mock() + when(webdriver).Remote(command_executor=url, + desired_capabilities=caps, + browser_profile=profile, + options=options).thenReturn(expected_webdriver) + driver = self.creator.create_firefox({}, url, None, options=options) + self.assertEqual(driver, expected_webdriver) + + def test_create_headless_firefox_with_options(self): + log_file = os.path.join(self.output_dir, 'geckodriver-1.log') + options = mock() + profile = mock() + expected_webdriver = mock() + when(webdriver).FirefoxProfile().thenReturn(profile) + when(webdriver).Firefox(options=options, firefox_profile=profile, + service_log_path=log_file).thenReturn(expected_webdriver) + driver = self.creator.create_headless_firefox({}, None, None, options=options) + self.assertEqual(driver, expected_webdriver) + + def test_create_ie_with_options(self): + options = mock() + expected_webdriver = mock() + when(self.creator)._has_service_log_path(ANY).thenReturn(True) + when(self.creator)._has_options(ANY).thenReturn(True) + when(webdriver).Ie(service_log_path=None, options=options).thenReturn(expected_webdriver) + driver = self.creator.create_ie({}, None, options=options) + self.assertEqual(driver, expected_webdriver) + + def test_create_ie_with_options_and_remote_url(self): + url = 'http://localhost:4444/wd/hub' + caps = webdriver.DesiredCapabilities.INTERNETEXPLORER.copy() + options = mock() + expected_webdriver = mock() + when(webdriver).Remote(command_executor=url, + desired_capabilities=caps, + browser_profile=None, + options=options).thenReturn(expected_webdriver) + driver = self.creator.create_ie({}, url, options=options) + self.assertEqual(driver, expected_webdriver) + + def test_create_ie_with_options_and_log_path(self): + options = mock() + expected_webdriver = mock() + when(self.creator)._has_service_log_path(ANY).thenReturn(False) + when(self.creator)._has_options(ANY).thenReturn(True) + when(webdriver).Ie(options=options).thenReturn(expected_webdriver) + driver = self.creator.create_ie({}, None, options=options) + self.assertEqual(driver, expected_webdriver) + + def test_has_options(self): + self.assertTrue(self.creator._has_options(webdriver.Chrome)) + self.assertTrue(self.creator._has_options(webdriver.Firefox)) + self.assertTrue(self.creator._has_options(webdriver.Ie)) + self.assertFalse(self.creator._has_options(webdriver.Edge)) + self.assertTrue(self.creator._has_options(webdriver.Opera)) + self.assertFalse(self.creator._has_options(webdriver.Safari)) + + @unittest.skipIf('options' not in inspect.getargspec(webdriver.Edge.__init__), "Requires Selenium 4.0.") + def test_create_edge_with_options(self): + # TODO: This test requires Selenium 4.0 in Travis + options = mock() + expected_webdriver = mock() + when(self.creator)._has_service_log_path(ANY).thenReturn(True) + when(self.creator)._has_options(ANY).thenReturn(True) + when(webdriver).Edge(service_log_path=None, options=options).thenReturn(expected_webdriver) + driver = self.creator.create_edge({}, None, options=options) + self.assertEqual(driver, expected_webdriver) + + def test_create_opera_with_options(self): + options = mock() + expected_webdriver = mock() + when(webdriver).Opera(options=options, service_log_path=None).thenReturn(expected_webdriver) + driver = self.creator.create_opera({}, None, options=options) + self.assertEqual(driver, expected_webdriver) + + def test_create_opera_with_options_and_remote_url(self): + url = 'http://localhost:4444/wd/hub' + caps = webdriver.DesiredCapabilities.OPERA.copy() + options = mock() + expected_webdriver = mock() + when(webdriver).Remote(command_executor=url, + desired_capabilities=caps, + browser_profile=None, + options=options).thenReturn(expected_webdriver) + driver = self.creator.create_opera({}, url, options=options) + self.assertEqual(driver, expected_webdriver) + + def test_create_safari_no_options_support(self): + options = mock() + expected_webdriver = mock() + when(webdriver).Safari().thenReturn(expected_webdriver) + driver = self.creator.create_safari({}, None, options=options) + self.assertEqual(driver, expected_webdriver) + + def test_create_phantomjs_no_options_support(self): + options = mock() + expected_webdriver = mock() + when(webdriver).PhantomJS(service_log_path=None).thenReturn(expected_webdriver) + driver = self.creator.create_phantomjs({}, None, options=options) + self.assertEqual(driver, expected_webdriver) + + def test_create_htmlunit_no_options_support(self): + caps = webdriver.DesiredCapabilities.HTMLUNIT.copy() + options = mock() + expected_webdriver = mock() + when(webdriver).Remote(command_executor='None', + desired_capabilities=caps, + browser_profile=None, + options=options).thenReturn(expected_webdriver) + driver = self.creator.create_htmlunit({'desired_capabilities': caps}, None, options=options) + self.assertEqual(driver, expected_webdriver) + + def test_create_htmlunit_with_js_no_options_support(self): + caps = webdriver.DesiredCapabilities.HTMLUNITWITHJS.copy() + options = mock() + expected_webdriver = mock() + when(webdriver).Remote(command_executor='None', + desired_capabilities=caps, + browser_profile=None, + options=options).thenReturn(expected_webdriver) + driver = self.creator.create_htmlunit_with_js({}, None, options=options) + self.assertEqual(driver, expected_webdriver) + + def test_android_options_support(self): + caps = webdriver.DesiredCapabilities.ANDROID.copy() + options = mock() + expected_webdriver = mock() + when(webdriver).Remote(command_executor='None', + desired_capabilities=caps, + browser_profile=None, + options=options).thenReturn(expected_webdriver) + driver = self.creator.create_android({}, None, options=options) + self.assertEqual(driver, expected_webdriver) + + def test_iphone_options_support(self): + caps = webdriver.DesiredCapabilities.IPHONE.copy() + options = mock() + expected_webdriver = mock() + when(webdriver).Remote(command_executor='None', + desired_capabilities=caps, + browser_profile=None, + options=options).thenReturn(expected_webdriver) + driver = self.creator.create_iphone({}, None, options=options) + self.assertEqual(driver, expected_webdriver) + + def test_create_driver_chrome(self): + str_options = 'add_argument:--disable-dev-shm-usage' + options = mock() + expected_webdriver = mock() + when(self.creator.selenium_options).create('chrome', str_options).thenReturn(options) + when(webdriver).Chrome(service_log_path=None, options=options).thenReturn(expected_webdriver) + driver = self.creator.create_driver('Chrome', desired_capabilities={}, remote_url=None, + options=str_options) + self.assertEqual(driver, expected_webdriver) + + def test_create_driver_firefox(self): + log_file = os.path.join(self.output_dir, 'geckodriver-1.log') + str_options = 'add_argument:--disable-dev-shm-usage' + options = mock() + profile = mock() + when(webdriver).FirefoxProfile().thenReturn(profile) + expected_webdriver = mock() + when(self.creator.selenium_options).create('firefox', str_options).thenReturn(options) + when(webdriver).Firefox(options=options, firefox_profile=profile, + service_log_path=log_file).thenReturn(expected_webdriver) + driver = self.creator.create_driver('FireFox', desired_capabilities={}, remote_url=None, + options=str_options) + self.assertEqual(driver, expected_webdriver) diff --git a/utest/test/keywords/test_webdrivercreator.py b/utest/test/keywords/test_webdrivercreator.py index e2afbcbfb..7ae40e58a 100644 --- a/utest/test/keywords/test_webdrivercreator.py +++ b/utest/test/keywords/test_webdrivercreator.py @@ -17,14 +17,21 @@ def setUpClass(cls): def tearDown(self): unstub() + def test_normalise_browser_name(self): + browser = self.creator._normalise_browser_name('chrome') + self.assertEqual(browser, 'chrome') + + browser = self.creator._normalise_browser_name('ChrOmE') + self.assertEqual(browser, 'chrome') + + browser = self.creator._normalise_browser_name(' Ch rO mE ') + self.assertEqual(browser, 'chrome') + def test_get_creator_method(self): method = self.creator._get_creator_method('chrome') self.assertTrue(method) - method = self.creator._get_creator_method('Chrome') - self.assertTrue(method) - - method = self.creator._get_creator_method('Fire Fox') + method = self.creator._get_creator_method('firefox') self.assertTrue(method) with self.assertRaisesRegexp(ValueError, 'foobar is not a supported browser.'): @@ -298,6 +305,7 @@ def test_ie(self): expected_webdriver = mock() when(webdriver).Ie().thenReturn(expected_webdriver) when(self.creator)._has_service_log_path(ANY).thenReturn(False) + when(self.creator)._has_options(ANY).thenReturn(False) driver = self.creator.create_ie({}, None) self.assertEqual(driver, expected_webdriver) @@ -339,6 +347,7 @@ def test_edge(self): expected_webdriver = mock() when(webdriver).Edge(service_log_path=None).thenReturn(expected_webdriver) when(self.creator)._has_service_log_path(ANY).thenReturn(True) + when(self.creator)._has_options(ANY).thenReturn(False) driver = self.creator.create_edge({}, None) self.assertEqual(driver, expected_webdriver) @@ -374,7 +383,7 @@ def test_edge_no_browser_name(self): def test_opera(self): expected_webdriver = mock() - when(webdriver).Opera(service_log_path=None).thenReturn(expected_webdriver) + when(webdriver).Opera(options=None, service_log_path=None).thenReturn(expected_webdriver) driver = self.creator.create_opera({}, None) self.assertEqual(driver, expected_webdriver) @@ -591,6 +600,7 @@ def test_create_driver_firefox(self): def test_create_driver_ie(self): expected_webdriver = mock() when(self.creator)._has_service_log_path(ANY).thenReturn(False) + when(self.creator)._has_options(ANY).thenReturn(False) when(webdriver).Ie().thenReturn(expected_webdriver) for browser in ['ie', 'Internet Explorer']: driver = self.creator.create_driver(browser, None, None) diff --git a/utest/test/keywords/test_webdrivercreator_service_log_path.py b/utest/test/keywords/test_webdrivercreator_service_log_path.py index 9cd5e99ab..b6228c226 100644 --- a/utest/test/keywords/test_webdrivercreator_service_log_path.py +++ b/utest/test/keywords/test_webdrivercreator_service_log_path.py @@ -112,7 +112,8 @@ def test_create_ie_with_service_log_path_real_path(self): log_file = os.path.join(self.output_dir, 'ie-1.log') expected_webdriver = mock() when(self.creator)._has_service_log_path(ANY).thenReturn(True) - when(webdriver).Ie(service_log_path=log_file).thenReturn(expected_webdriver) + when(self.creator)._has_options(ANY).thenReturn(True) + when(webdriver).Ie(options=None, service_log_path=log_file).thenReturn(expected_webdriver) driver = self.creator.create_ie({}, None, service_log_path=log_file) self.assertEqual(driver, expected_webdriver) @@ -120,6 +121,7 @@ def test_create_ie_with_service_log_path_old_selenium(self): log_file = os.path.join(self.output_dir, 'ie-1.log') expected_webdriver = mock() when(self.creator)._has_service_log_path(ANY).thenReturn(False) + when(self.creator)._has_options(ANY).thenReturn(False) when(webdriver).Ie().thenReturn(expected_webdriver) driver = self.creator.create_ie({}, None, service_log_path=log_file) self.assertEqual(driver, expected_webdriver) @@ -134,6 +136,7 @@ def test_create_edge_with_service_log_path_real_path(self): log_file = os.path.join(self.output_dir, 'ie-1.log') expected_webdriver = mock() when(self.creator)._has_service_log_path(ANY).thenReturn(True) + when(self.creator)._has_options(ANY).thenReturn(False) when(webdriver).Edge(service_log_path=log_file).thenReturn(expected_webdriver) driver = self.creator.create_edge({}, None, service_log_path=log_file) self.assertEqual(driver, expected_webdriver) @@ -142,6 +145,7 @@ def test_create_edge_with_service_log_path_old_selenium(self): log_file = os.path.join(self.output_dir, 'ie-1.log') expected_webdriver = mock() when(self.creator)._has_service_log_path(ANY).thenReturn(False) + when(self.creator)._has_options(ANY).thenReturn(False) when(webdriver).Edge().thenReturn(expected_webdriver) driver = self.creator.create_edge({}, None, service_log_path=log_file) self.assertEqual(driver, expected_webdriver) @@ -149,10 +153,17 @@ def test_create_edge_with_service_log_path_old_selenium(self): def test_create_opera_with_service_log_path_real_path(self): log_file = os.path.join(self.output_dir, 'ie-1.log') expected_webdriver = mock() - when(webdriver).Opera(service_log_path=log_file).thenReturn(expected_webdriver) + when(webdriver).Opera(options=None, service_log_path=log_file).thenReturn(expected_webdriver) driver = self.creator.create_opera({}, None, service_log_path=log_file) self.assertEqual(driver, expected_webdriver) + def test_create_safari_no_support_for_service_log_path(self): + log_file = os.path.join(self.output_dir, 'ie-1.log') + expected_webdriver = mock() + when(webdriver).Safari().thenReturn(expected_webdriver) + driver = self.creator.create_safari({}, None, service_log_path=log_file) + self.assertEqual(driver, expected_webdriver) + def test_create_phantomjs_with_service_log_path_real_path(self): log_file = os.path.join(self.output_dir, 'ie-1.log') expected_webdriver = mock()