[py] Add support for the new MicrosoftEdge (#7459)
Extracting Chromium from Chrome module. Have both Chrome and Edge
inherited from Chromium module. Add is_legacy parameter to launch
the new MicrosoftEdge.
Cr-Mirrored-From: https://chromium.googlesource.com/external/github.com/SeleniumHQ/selenium
Cr-Mirrored-Commit: 139ca6cddf95feb4446931c75f2bb7ce9ff83496
diff --git a/MANIFEST.in b/MANIFEST.in
index a911439..04207c9 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -4,6 +4,7 @@
recursive-include selenium/webdriver/common/actions *.py
recursive-include selenium/webdriver/common/html5 *.py
recursive-include selenium/common *.py
+recursive-include selenium/webdriver/chromium *.py
recursive-include selenium/webdriver/chrome *.py
recursive-include selenium/webdriver/opera *.py
recursive-include selenium/webdriver/phantomjs *.py
diff --git a/build.desc b/build.desc
index 78e8563..ee7c116 100644
--- a/build.desc
+++ b/build.desc
@@ -43,6 +43,11 @@
browsers = [ "edge"])
py_test(
+ name = "chromium_edge_test",
+ deps = [":test_edge"],
+ browsers = ["chromiumedge"])
+
+py_test(
name = "remote_firefox_test",
deps = [ ":test_remote_firefox" ],
browsers = [ "remote_firefox" ])
diff --git a/conftest.py b/conftest.py
index 7ba634a..f1b90e7 100644
--- a/conftest.py
+++ b/conftest.py
@@ -44,6 +44,7 @@
'Remote',
'Safari',
'WebKitGTK',
+ 'ChromiumEdge',
)
@@ -117,6 +118,9 @@
options = get_options('Firefox', request.config)
if driver_class == 'WebKitGTK':
options = get_options(driver_class, request.config)
+ if driver_class == 'ChromiumEdge':
+ options = get_options('Edge', request.config)
+ kwargs.update({'is_legacy': False})
if driver_path is not None:
kwargs['executable_path'] = driver_path
if options is not None:
@@ -131,6 +135,10 @@
browser_path = config.option.binary
browser_args = config.option.args
options = None
+
+ if driver_class == 'Edge':
+ return getattr(webdriver, '{}Options'.format(driver_class))(False)
+
if browser_path or browser_args:
options = getattr(webdriver, '{}Options'.format(driver_class))()
if driver_class == 'WebKitGTK':
diff --git a/selenium/webdriver/__init__.py b/selenium/webdriver/__init__.py
index b0f1791..77dca82 100644
--- a/selenium/webdriver/__init__.py
+++ b/selenium/webdriver/__init__.py
@@ -23,6 +23,8 @@
from .ie.webdriver import WebDriver as Ie # noqa
from .ie.options import Options as IeOptions # noqa
from .edge.webdriver import WebDriver as Edge # noqa
+from .edge.webdriver import WebDriver as ChromiumEdge # noqa
+from .edge.options import Options as EdgeOptions #noqa
from .opera.webdriver import WebDriver as Opera # noqa
from .safari.webdriver import WebDriver as Safari # noqa
from .blackberry.webdriver import WebDriver as BlackBerry # noqa
diff --git a/selenium/webdriver/chrome/options.py b/selenium/webdriver/chrome/options.py
index c5ec418..5325aa3 100644
--- a/selenium/webdriver/chrome/options.py
+++ b/selenium/webdriver/chrome/options.py
@@ -19,159 +19,19 @@
import os
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
-from selenium.webdriver.common.options import ArgOptions
+from selenium.webdriver.chromium.options import ChromiumOptions
-class Options(ArgOptions):
+class Options(ChromiumOptions):
KEY = "goog:chromeOptions"
- def __init__(self):
- super(Options, self).__init__()
- self._binary_location = ''
- self._extension_files = []
- self._extensions = []
- self._experimental_options = {}
- self._debugger_address = None
-
- @property
- def binary_location(self):
- """
- :Returns: The location of the binary, otherwise an empty string
- """
- return self._binary_location
-
- @binary_location.setter
- def binary_location(self, value):
- """
- Allows you to set where the chromium binary lives
-
- :Args:
- - value: path to the Chromium binary
- """
- self._binary_location = value
-
- @property
- def debugger_address(self):
- """
- :Returns: The address of the remote devtools instance
- """
- return self._debugger_address
-
- @debugger_address.setter
- def debugger_address(self, value):
- """
- Allows you to set the address of the remote devtools instance
- that the ChromeDriver instance will try to connect to during an
- active wait.
-
- :Args:
- - value: address of remote devtools instance if any (hostname[:port])
- """
- self._debugger_address = value
-
- @property
- def extensions(self):
- """
- :Returns: A list of encoded extensions that will be loaded into chrome
- """
- encoded_extensions = []
- for ext in self._extension_files:
- file_ = open(ext, 'rb')
- # Should not use base64.encodestring() which inserts newlines every
- # 76 characters (per RFC 1521). Chromedriver has to remove those
- # unnecessary newlines before decoding, causing performance hit.
- encoded_extensions.append(base64.b64encode(file_.read()).decode('UTF-8'))
-
- file_.close()
- return encoded_extensions + self._extensions
-
- def add_extension(self, extension):
- """
- Adds the path to the extension to a list that will be used to extract it
- to the ChromeDriver
-
- :Args:
- - extension: path to the \\*.crx file
- """
- if extension:
- extension_to_add = os.path.abspath(os.path.expanduser(extension))
- if os.path.exists(extension_to_add):
- self._extension_files.append(extension_to_add)
- else:
- raise IOError("Path to the extension doesn't exist")
- else:
- raise ValueError("argument can not be null")
-
- def add_encoded_extension(self, extension):
- """
- Adds Base64 encoded string with extension data to a list that will be used to extract it
- to the ChromeDriver
-
- :Args:
- - extension: Base64 encoded string with extension data
- """
- if extension:
- self._extensions.append(extension)
- else:
- raise ValueError("argument can not be null")
-
- @property
- def experimental_options(self):
- """
- :Returns: A dictionary of experimental options for chrome
- """
- return self._experimental_options
-
- def add_experimental_option(self, name, value):
- """
- Adds an experimental option which is passed to chrome.
-
- :Args:
- name: The experimental option name.
- value: The option value.
- """
- self._experimental_options[name] = value
-
- @property
- def headless(self):
- """
- :Returns: True if the headless argument is set, else False
- """
- return '--headless' in self._arguments
-
- @headless.setter
- def headless(self, value):
- """
- Sets the headless argument
-
- :Args:
- value: boolean value indicating to set the headless option
- """
- args = {'--headless'}
- if value is True:
- self._arguments.extend(args)
- else:
- self._arguments = list(set(self._arguments) - args)
-
- def to_capabilities(self):
- """
- Creates a capabilities with all the options that have been set
-
- :Returns: A dictionary with everything
- """
- caps = self._caps
- chrome_options = self.experimental_options.copy()
- chrome_options["extensions"] = self.extensions
- if self.binary_location:
- chrome_options["binary"] = self.binary_location
- chrome_options["args"] = self.arguments
- if self.debugger_address:
- chrome_options["debuggerAddress"] = self.debugger_address
-
- caps[self.KEY] = chrome_options
-
- return caps
-
@property
def default_capabilities(self):
return DesiredCapabilities.CHROME.copy()
+
+ def to_capabilities(self):
+ """
+ Creates a capabilities with all the options that have been set and
+ :Returns: A dictionary with everything
+ """
+ return super(Options, self).to_capabilities(self.KEY)
diff --git a/selenium/webdriver/chrome/service.py b/selenium/webdriver/chrome/service.py
index 5a67b2c..786c538 100644
--- a/selenium/webdriver/chrome/service.py
+++ b/selenium/webdriver/chrome/service.py
@@ -15,10 +15,10 @@
# specific language governing permissions and limitations
# under the License.
-from selenium.webdriver.common import service
+from selenium.webdriver.chromium import service
-class Service(service.Service):
+class Service(service.ChromiumService):
"""
Object that manages the starting and stopping of the ChromeDriver
"""
@@ -27,19 +27,16 @@
log_path=None, env=None):
"""
Creates a new instance of the Service
-
:Args:
- executable_path : Path to the ChromeDriver
- port : Port the service is running on
- service_args : List of args to pass to the chromedriver service
- log_path : Path for the chromedriver service to log to"""
- self.service_args = service_args or []
- if log_path:
- self.service_args.append('--log-path=%s' % log_path)
-
- service.Service.__init__(self, executable_path, port=port, env=env,
- start_error_message="Please see https://sites.google.com/a/chromium.org/chromedriver/home")
-
- def command_line_args(self):
- return ["--port=%d" % self.port] + self.service_args
+ super(Service, self).__init__(
+ executable_path,
+ port,
+ service_args,
+ log_path,
+ env,
+ "Please see https://sites.google.com/a/chromium.org/chromedriver/home")
diff --git a/selenium/webdriver/chrome/webdriver.py b/selenium/webdriver/chrome/webdriver.py
index 97d12d1..3b3617d 100644
--- a/selenium/webdriver/chrome/webdriver.py
+++ b/selenium/webdriver/chrome/webdriver.py
@@ -15,20 +15,18 @@
# specific language governing permissions and limitations
# under the License.
import warnings
-from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
-from .remote_connection import ChromeRemoteConnection
-from .service import Service
+from selenium.webdriver.chromium.webdriver import ChromiumDriver
from .options import Options
+from .service import Service
DEFAULT_PORT = 0
DEFAULT_SERVICE_LOG_PATH = None
-class WebDriver(RemoteWebDriver):
+class WebDriver(ChromiumDriver):
"""
Controls the ChromeDriver and allows you to drive the browser.
-
You will need to download the ChromeDriver executable from
http://chromedriver.storage.googleapis.com/index.html
"""
@@ -39,9 +37,7 @@
chrome_options=None, service=None, keep_alive=True):
"""
Creates a new instance of the chrome driver.
-
Starts the service and then creates new instance of chrome driver.
-
:Args:
- executable_path - Deprecated: path to the executable. If the default is used it assumes the executable is in the $PATH
- port - Deprecated: port you would like the service to run, if left as 0, a free port will be found.
@@ -52,172 +48,16 @@
- service_log_path - Deprecated: Where to log information from the driver.
- keep_alive - Whether to configure ChromeRemoteConnection to use HTTP keep-alive.
"""
- if executable_path != 'chromedriver':
- warnings.warn('executable_path has been deprecated, please pass in a Service object',
- DeprecationWarning, stacklevel=2)
- if desired_capabilities is not None:
- warnings.warn('desired_capabilities has been deprecated, please pass in a Service object',
- DeprecationWarning, stacklevel=2)
- if port != DEFAULT_PORT:
- warnings.warn('port has been deprecated, please pass in a Service object',
- DeprecationWarning, stacklevel=2)
- self.port = port
- if service_log_path != DEFAULT_SERVICE_LOG_PATH:
- warnings.warn('service_log_path has been deprecated, please pass in a Service object',
- DeprecationWarning, stacklevel=2)
-
if chrome_options:
warnings.warn('use options instead of chrome_options',
DeprecationWarning, stacklevel=2)
options = chrome_options
- if options is None:
- # desired_capabilities stays as passed in
- if desired_capabilities is None:
- desired_capabilities = self.create_options().to_capabilities()
- else:
- if desired_capabilities is None:
- desired_capabilities = options.to_capabilities()
- else:
- desired_capabilities.update(options.to_capabilities())
+ if service is None:
+ service = Service(executable_path, port, service_args, service_log_path)
- if service:
- self.service = service
- else:
- self.service = Service(
- executable_path,
- port=port,
- service_args=service_args,
- log_path=service_log_path)
- self.service.start()
-
- try:
- RemoteWebDriver.__init__(
- self,
- command_executor=ChromeRemoteConnection(
- remote_server_addr=self.service.service_url,
- keep_alive=keep_alive),
- desired_capabilities=desired_capabilities)
- except Exception:
- self.quit()
- raise
- self._is_remote = False
-
- def launch_app(self, id):
- """Launches Chrome app specified by id."""
- return self.execute("launchApp", {'id': id})
-
- def get_network_conditions(self):
- """
- Gets Chrome network emulation settings.
-
- :Returns:
- A dict. For example:
-
- {'latency': 4, 'download_throughput': 2, 'upload_throughput': 2,
- 'offline': False}
-
- """
- return self.execute("getNetworkConditions")['value']
-
- def set_network_conditions(self, **network_conditions):
- """
- Sets Chrome network emulation settings.
-
- :Args:
- - network_conditions: A dict with conditions specification.
-
- :Usage:
- ::
-
- driver.set_network_conditions(
- offline=False,
- latency=5, # additional latency (ms)
- download_throughput=500 * 1024, # maximal throughput
- upload_throughput=500 * 1024) # maximal throughput
-
- Note: 'throughput' can be used to set both (for download and upload).
- """
- self.execute("setNetworkConditions", {
- 'network_conditions': network_conditions
- })
-
- def execute_cdp_cmd(self, cmd, cmd_args):
- """
- Execute Chrome Devtools Protocol command and get returned result
-
- The command and command args should follow chrome devtools protocol domains/commands, refer to link
- https://chromedevtools.github.io/devtools-protocol/
-
- :Args:
- - cmd: A str, command name
- - cmd_args: A dict, command args. empty dict {} if there is no command args
-
- :Usage:
- ::
-
- driver.execute_cdp_cmd('Network.getResponseBody', {'requestId': requestId})
-
- :Returns:
- A dict, empty dict {} if there is no result to return.
- For example to getResponseBody:
-
- {'base64Encoded': False, 'body': 'response body string'}
-
- """
- return self.execute("executeCdpCommand", {'cmd': cmd, 'params': cmd_args})['value']
-
- def get_sinks(self):
- """
- :Returns: A list of sinks avaliable for Cast.
- """
- return self.execute('getSinks')['value']
-
- def get_issue_message(self):
- """
- :Returns: An error message when there is any issue in a Cast session.
- """
- return self.execute('getIssueMessage')['value']
-
- def set_sink_to_use(self, sink_name):
- """
- Sets a specific sink, using its name, as a Cast session receiver target.
-
- :Args:
- - sink_name: Name of the sink to use as the target.
- """
- return self.execute('setSinkToUse', {'sinkName': sink_name})
-
- def start_tab_mirroring(self, sink_name):
- """
- Starts a tab mirroring session on a specific receiver target.
-
- :Args:
- - sink_name: Name of the sink to use as the target.
- """
- return self.execute('startTabMirroring', {'sinkName': sink_name})
-
- def stop_casting(self, sink_name):
- """
- Stops the existing Cast session on a specific receiver target.
-
- :Args:
- - sink_name: Name of the sink to stop the Cast session.
- """
- return self.execute('stopCasting', {'sinkName': sink_name})
-
- def quit(self):
- """
- Closes the browser and shuts down the ChromeDriver executable
- that is started when starting the ChromeDriver
- """
- try:
- RemoteWebDriver.quit(self)
- except Exception:
- # We don't care about the message because something probably has gone wrong
- pass
- finally:
- self.service.stop()
+ super(WebDriver, self).__init__(executable_path, port, options, service_args, desired_capabilities, service_log_path,
+ service, keep_alive)
def create_options(self):
return Options()
diff --git a/selenium/webdriver/chromium/__init__.py b/selenium/webdriver/chromium/__init__.py
new file mode 100644
index 0000000..3f22a88
--- /dev/null
+++ b/selenium/webdriver/chromium/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Software Freedom Conservancy (SFC) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The SFC licenses this file
+# to you 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.
\ No newline at end of file
diff --git a/selenium/webdriver/chromium/options.py b/selenium/webdriver/chromium/options.py
new file mode 100644
index 0000000..66fcfd3
--- /dev/null
+++ b/selenium/webdriver/chromium/options.py
@@ -0,0 +1,169 @@
+# Licensed to the Software Freedom Conservancy (SFC) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The SFC licenses this file
+# to you 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.
+
+import base64
+import os
+
+from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
+from selenium.webdriver.common.options import ArgOptions
+
+
+class ChromiumOptions(ArgOptions):
+
+ def __init__(self):
+ super(ChromiumOptions, self).__init__()
+ self._binary_location = ''
+ self._extension_files = []
+ self._extensions = []
+ self._experimental_options = {}
+ self._debugger_address = None
+
+ @property
+ def binary_location(self):
+ """
+ :Returns: The location of the binary, otherwise an empty string
+ """
+ return self._binary_location
+
+ @binary_location.setter
+ def binary_location(self, value):
+ """
+ Allows you to set where the chromium binary lives
+ :Args:
+ - value: path to the Chromium binary
+ """
+ self._binary_location = value
+
+ @property
+ def debugger_address(self):
+ """
+ :Returns: The address of the remote devtools instance
+ """
+ return self._debugger_address
+
+ @debugger_address.setter
+ def debugger_address(self, value):
+ """
+ Allows you to set the address of the remote devtools instance
+ that the ChromeDriver instance will try to connect to during an
+ active wait.
+ :Args:
+ - value: address of remote devtools instance if any (hostname[:port])
+ """
+ self._debugger_address = value
+
+ @property
+ def extensions(self):
+ """
+ :Returns: A list of encoded extensions that will be loaded
+ """
+ encoded_extensions = []
+ for ext in self._extension_files:
+ file_ = open(ext, 'rb')
+ # Should not use base64.encodestring() which inserts newlines every
+ # 76 characters (per RFC 1521). Chromedriver has to remove those
+ # unnecessary newlines before decoding, causing performance hit.
+ encoded_extensions.append(base64.b64encode(file_.read()).decode('UTF-8'))
+
+ file_.close()
+ return encoded_extensions + self._extensions
+
+ def add_extension(self, extension):
+ """
+ Adds the path to the extension to a list that will be used to extract it
+ to the ChromeDriver
+ :Args:
+ - extension: path to the \\*.crx file
+ """
+ if extension:
+ extension_to_add = os.path.abspath(os.path.expanduser(extension))
+ if os.path.exists(extension_to_add):
+ self._extension_files.append(extension_to_add)
+ else:
+ raise IOError("Path to the extension doesn't exist")
+ else:
+ raise ValueError("argument can not be null")
+
+ def add_encoded_extension(self, extension):
+ """
+ Adds Base64 encoded string with extension data to a list that will be used to extract it
+ to the ChromeDriver
+ :Args:
+ - extension: Base64 encoded string with extension data
+ """
+ if extension:
+ self._extensions.append(extension)
+ else:
+ raise ValueError("argument can not be null")
+
+ @property
+ def experimental_options(self):
+ """
+ :Returns: A dictionary of experimental options for chromium
+ """
+ return self._experimental_options
+
+ def add_experimental_option(self, name, value):
+ """
+ Adds an experimental option which is passed to chromium.
+ :Args:
+ name: The experimental option name.
+ value: The option value.
+ """
+ self._experimental_options[name] = value
+
+ @property
+ def headless(self):
+ """
+ :Returns: True if the headless argument is set, else False
+ """
+ return '--headless' in self._arguments
+
+ @headless.setter
+ def headless(self, value):
+ """
+ Sets the headless argument
+ :Args:
+ value: boolean value indicating to set the headless option
+ """
+ args = {'--headless'}
+ if value is True:
+ self._arguments.extend(args)
+ else:
+ self._arguments = list(set(self._arguments) - args)
+
+ def to_capabilities(self, capabilityKey):
+ """
+ Creates a capabilities with all the options that have been set
+ :Returns: A dictionary with everything
+ """
+ caps = self._caps
+ chrome_options = self.experimental_options.copy()
+ chrome_options["extensions"] = self.extensions
+ if self.binary_location:
+ chrome_options["binary"] = self.binary_location
+ chrome_options["args"] = self.arguments
+ if self.debugger_address:
+ chrome_options["debuggerAddress"] = self.debugger_address
+
+ caps[capabilityKey] = chrome_options
+
+ return caps
+
+ @property
+ def default_capabilities(self):
+ pass
diff --git a/selenium/webdriver/chrome/remote_connection.py b/selenium/webdriver/chromium/remote_connection.py
similarity index 97%
rename from selenium/webdriver/chrome/remote_connection.py
rename to selenium/webdriver/chromium/remote_connection.py
index d8408a0..71770b0 100644
--- a/selenium/webdriver/chrome/remote_connection.py
+++ b/selenium/webdriver/chromium/remote_connection.py
@@ -18,7 +18,7 @@
from selenium.webdriver.remote.remote_connection import RemoteConnection
-class ChromeRemoteConnection(RemoteConnection):
+class ChromiumRemoteConnection(RemoteConnection):
def __init__(self, remote_server_addr, keep_alive=True):
RemoteConnection.__init__(self, remote_server_addr, keep_alive)
diff --git a/selenium/webdriver/chromium/service.py b/selenium/webdriver/chromium/service.py
new file mode 100644
index 0000000..719dc13
--- /dev/null
+++ b/selenium/webdriver/chromium/service.py
@@ -0,0 +1,46 @@
+# Licensed to the Software Freedom Conservancy (SFC) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The SFC licenses this file
+# to you 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 selenium.webdriver.common import service
+
+
+class ChromiumService(service.Service):
+ """
+ Object that manages the starting and stopping the WebDriver instance of the ChromiumDriver
+ """
+
+ def __init__(self, executable_path, port=0, service_args=None,
+ log_path=None, env=None, start_error_message=None):
+ """
+ Creates a new instance of the Service
+ :Args:
+ - executable_path : Path to the WebDriver executable
+ - port : Port the service is running on
+ - service_args : List of args to pass to the WebDriver service
+ - log_path : Path for the WebDriver service to log to"""
+
+ self.service_args = service_args or []
+ if log_path:
+ self.service_args.append('--log-path=%s' % log_path)
+
+ if start_error_message is None:
+ raise AttributeError("start_error_message should not be empty")
+
+ service.Service.__init__(self, executable_path, port=port, env=env, start_error_message=start_error_message)
+
+ def command_line_args(self):
+ return ["--port=%d" % self.port] + self.service_args
diff --git a/selenium/webdriver/chromium/webdriver.py b/selenium/webdriver/chromium/webdriver.py
new file mode 100644
index 0000000..434f457
--- /dev/null
+++ b/selenium/webdriver/chromium/webdriver.py
@@ -0,0 +1,192 @@
+# Licensed to the Software Freedom Conservancy (SFC) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The SFC licenses this file
+# to you 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.
+import warnings
+from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
+from selenium.webdriver.chromium.remote_connection import ChromiumRemoteConnection
+from .service import ChromiumService
+from .options import ChromiumOptions
+
+
+DEFAULT_PORT = 0
+DEFAULT_SERVICE_LOG_PATH = None
+
+
+class ChromiumDriver(RemoteWebDriver):
+ """
+ Controls the WebDriver instance of ChromiumDriver and allows you to drive the browser.
+ """
+
+ def __init__(self, executable_path="chromedriver", port=DEFAULT_PORT,
+ options=None, service_args=None,
+ desired_capabilities=None, service_log_path=DEFAULT_SERVICE_LOG_PATH,
+ service=None, keep_alive=True):
+ """
+ Creates a new WebDriver instance of the ChromiumDriver.
+ Starts the service and then creates new WebDriver instance of ChromiumDriver.
+ :Args:
+ - executable_path - Deprecated: path to the executable. If the default is used it assumes the executable is in the $PATH
+ - port - Deprecated: port you would like the service to run, if left as 0, a free port will be found.
+ - options - this takes an instance of ChromiumOptions
+ - service_args - Deprecated: List of args to pass to the driver service
+ - desired_capabilities - Deprecated: Dictionary object with non-browser specific
+ capabilities only, such as "proxy" or "loggingPref".
+ - service_log_path - Deprecated: Where to log information from the driver.
+ - keep_alive - Whether to configure ChromiumRemoteConnection to use HTTP keep-alive.
+ """
+ if executable_path != 'chromedriver':
+ warnings.warn('executable_path has been deprecated, please pass in a Service object',
+ DeprecationWarning, stacklevel=2)
+ if desired_capabilities is not None:
+ warnings.warn('desired_capabilities has been deprecated, please pass in a Service object',
+ DeprecationWarning, stacklevel=2)
+ if port != DEFAULT_PORT:
+ warnings.warn('port has been deprecated, please pass in a Service object',
+ DeprecationWarning, stacklevel=2)
+ self.port = port
+ if service_log_path != DEFAULT_SERVICE_LOG_PATH:
+ warnings.warn('service_log_path has been deprecated, please pass in a Service object',
+ DeprecationWarning, stacklevel=2)
+
+ if options is None:
+ # desired_capabilities stays as passed in
+ if desired_capabilities is None:
+ desired_capabilities = self.create_options().to_capabilities()
+ else:
+ if desired_capabilities is None:
+ desired_capabilities = options.to_capabilities()
+ else:
+ desired_capabilities.update(options.to_capabilities())
+
+ if service is None:
+ raise AttributeError('service cannot be None')
+
+ self.service = service
+ self.service.start()
+
+ try:
+ RemoteWebDriver.__init__(
+ self,
+ command_executor=ChromiumRemoteConnection(
+ remote_server_addr=self.service.service_url,
+ keep_alive=keep_alive),
+ desired_capabilities=desired_capabilities)
+ except Exception:
+ self.quit()
+ raise
+ self._is_remote = False
+
+ def launch_app(self, id):
+ """Launches Chromium app specified by id."""
+ return self.execute("launchApp", {'id': id})
+
+ def get_network_conditions(self):
+ """
+ Gets Chromium network emulation settings.
+ :Returns:
+ A dict. For example:
+ {'latency': 4, 'download_throughput': 2, 'upload_throughput': 2,
+ 'offline': False}
+ """
+ return self.execute("getNetworkConditions")['value']
+
+ def set_network_conditions(self, **network_conditions):
+ """
+ Sets Chromium network emulation settings.
+ :Args:
+ - network_conditions: A dict with conditions specification.
+ :Usage:
+ ::
+ driver.set_network_conditions(
+ offline=False,
+ latency=5, # additional latency (ms)
+ download_throughput=500 * 1024, # maximal throughput
+ upload_throughput=500 * 1024) # maximal throughput
+ Note: 'throughput' can be used to set both (for download and upload).
+ """
+ self.execute("setNetworkConditions", {
+ 'network_conditions': network_conditions
+ })
+
+ def execute_cdp_cmd(self, cmd, cmd_args):
+ """
+ Execute Chrome Devtools Protocol command and get returned result
+ The command and command args should follow chrome devtools protocol domains/commands, refer to link
+ https://chromedevtools.github.io/devtools-protocol/
+ :Args:
+ - cmd: A str, command name
+ - cmd_args: A dict, command args. empty dict {} if there is no command args
+ :Usage:
+ ::
+ driver.execute_cdp_cmd('Network.getResponseBody', {'requestId': requestId})
+ :Returns:
+ A dict, empty dict {} if there is no result to return.
+ For example to getResponseBody:
+ {'base64Encoded': False, 'body': 'response body string'}
+ """
+ return self.execute("executeCdpCommand", {'cmd': cmd, 'params': cmd_args})['value']
+
+ def get_sinks(self):
+ """
+ :Returns: A list of sinks avaliable for Cast.
+ """
+ return self.execute('getSinks')['value']
+
+ def get_issue_message(self):
+ """
+ :Returns: An error message when there is any issue in a Cast session.
+ """
+ return self.execute('getIssueMessage')['value']
+
+ def set_sink_to_use(self, sink_name):
+ """
+ Sets a specific sink, using its name, as a Cast session receiver target.
+ :Args:
+ - sink_name: Name of the sink to use as the target.
+ """
+ return self.execute('setSinkToUse', {'sinkName': sink_name})
+
+ def start_tab_mirroring(self, sink_name):
+ """
+ Starts a tab mirroring session on a specific receiver target.
+ :Args:
+ - sink_name: Name of the sink to use as the target.
+ """
+ return self.execute('startTabMirroring', {'sinkName': sink_name})
+
+ def stop_casting(self, sink_name):
+ """
+ Stops the existing Cast session on a specific receiver target.
+ :Args:
+ - sink_name: Name of the sink to stop the Cast session.
+ """
+ return self.execute('stopCasting', {'sinkName': sink_name})
+
+ def quit(self):
+ """
+ Closes the browser and shuts down the ChromiumDriver executable
+ that is started when starting the ChromiumDriver
+ """
+ try:
+ RemoteWebDriver.quit(self)
+ except Exception:
+ # We don't care about the message because something probably has gone wrong
+ pass
+ finally:
+ self.service.stop()
+
+ def create_options(self):
+ pass
diff --git a/selenium/webdriver/edge/options.py b/selenium/webdriver/edge/options.py
index 2fa5b03..93a215c 100644
--- a/selenium/webdriver/edge/options.py
+++ b/selenium/webdriver/edge/options.py
@@ -16,21 +16,31 @@
# under the License.
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
-from selenium.webdriver.common.options import BaseOptions
+from selenium.webdriver.chromium.options import ChromiumOptions
-class Options(BaseOptions):
+class Options(ChromiumOptions):
+ KEY = "goog:chromeOptions"
- def __init__(self):
+ def __init__(self, is_legacy=True):
super(Options, self).__init__()
- self._page_load_strategy = "normal"
+ self._is_legacy = is_legacy
+
+ if is_legacy:
+ self._page_load_strategy = "normal"
@property
def page_load_strategy(self):
+ if not self._is_legacy:
+ raise AttributeError("Page Load Strategy only exists in Legacy Mode")
+
return self._page_load_strategy
@page_load_strategy.setter
def page_load_strategy(self, value):
+ if not self._is_legacy:
+ raise AttributeError("Page Load Strategy only exists in Legacy Mode")
+
if value not in ['normal', 'eager', 'none']:
raise ValueError("Page Load Strategy should be 'normal', 'eager' or 'none'.")
self._page_load_strategy = value
@@ -38,9 +48,11 @@
def to_capabilities(self):
"""
Creates a capabilities with all the options that have been set and
-
:Returns: A dictionary with everything
"""
+ if not self._is_legacy:
+ return super(Options, self).to_capabilities(self.KEY)
+
caps = self._caps
caps['pageLoadStrategy'] = self._page_load_strategy
diff --git a/selenium/webdriver/edge/service.py b/selenium/webdriver/edge/service.py
index 4f2bf87..fbf2df9 100644
--- a/selenium/webdriver/edge/service.py
+++ b/selenium/webdriver/edge/service.py
@@ -15,43 +15,40 @@
# specific language governing permissions and limitations
# under the License.
-from selenium.webdriver.common import service
+from selenium.webdriver.chromium import service
-class Service(service.Service):
+class Service(service.ChromiumService):
- def __init__(self, executable_path, port=0, verbose=False, log_path=None):
+ def __init__(self, executable_path, port=0, verbose=False, log_path=None, is_legacy=True, service_args=None, env=None):
"""
Creates a new instance of the EdgeDriver service.
-
EdgeDriver provides an interface for Microsoft WebDriver to use
with Microsoft Edge.
-
- :param executable_path: Path to the Microsoft WebDriver binary.
- :param port: Run the remote service on a specified port.
- Defaults to 0, which binds to a random open port of the
- system's choosing.
- :verbose: Whether to make the webdriver more verbose (passes the
- --verbose option to the binary). Defaults to False.
- :param log_path: Optional path for the webdriver binary to log to.
- Defaults to None which disables logging.
-
+ :Args:
+ - executable_path : Path to the Microsoft WebDriver binary.
+ - port : Run the remote service on a specified port. Defaults to 0, which binds to a random open port
+ of the system's choosing.
+ - verbose : Whether to make the webdriver more verbose (passes the --verbose option to the binary).
+ Defaults to False. Should be only used for legacy mode.
+ - log_path : Optional path for the webdriver binary to log to. Defaults to None which disables logging.
+ - is_legacy : Whether to use MicrosoftWebDriver.exe (legacy) or MSEdgeDriver.exe (chromium-based). Defaults to True.
+ - service_args : List of args to pass to the WebDriver service.
"""
+ self.service_args = service_args or []
- self.service_args = []
- if verbose:
- self.service_args.append("--verbose")
+ if is_legacy:
+ if verbose:
+ self.service_args.append("--verbose")
- params = {
- "executable": executable_path,
- "port": port,
- "start_error_message": "Please download from https://go.microsoft.com/fwlink/?LinkId=619687"
- }
+ if log_path:
+ params["log_file"] = open(log_path, "a+")
- if log_path:
- params["log_file"] = open(log_path, "a+")
-
- service.Service.__init__(self, **params)
-
- def command_line_args(self):
- return ["--port=%d" % self.port] + self.service_args
+ service.ChromiumService.__init__(
+ self,
+ executable_path,
+ port,
+ service_args,
+ log_path,
+ env,
+ "Please download from https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/")
diff --git a/selenium/webdriver/edge/webdriver.py b/selenium/webdriver/edge/webdriver.py
index d74241b..657f9ee 100644
--- a/selenium/webdriver/edge/webdriver.py
+++ b/selenium/webdriver/edge/webdriver.py
@@ -14,78 +14,49 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-import warnings
-
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.edge.service import Service
-from selenium.webdriver.remote.remote_connection import RemoteConnection
-from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
+from selenium.webdriver.chromium.webdriver import ChromiumDriver
+
DEFAULT_PORT = 0
DEFAULT_SERVICE_LOG_PATH = None
-class WebDriver(RemoteWebDriver):
+class WebDriver(ChromiumDriver):
def __init__(self, executable_path='MicrosoftWebDriver.exe',
capabilities=None, port=DEFAULT_PORT, verbose=False,
service_log_path=None, log_path=DEFAULT_SERVICE_LOG_PATH,
- service=None, options=None, keep_alive=False):
+ service=None, options=None, keep_alive=False, is_legacy=True,
+ service_args=None):
"""
- Creates a new instance of the chrome driver.
-
- Starts the service and then creates new instance of chrome driver.
-
+ Creates a new instance of the edge driver.
+ Starts the service and then creates new instance of edge driver.
:Args:
- - executable_path - path to the executable. If the default is used it assumes the executable is in the $PATH
- - capabilities - Dictionary object with non-browser specific
- capabilities only, such as "proxy" or "loggingPref".
- - port - port you would like the service to run, if left as 0, a free port will be found.
- - verbose - whether to set verbose logging in the service
- - service_log_path - Where to log information from the driver.
+ - executable_path - Deprecated: path to the executable. If the default is used it assumes the executable is in the $PATH
+ - capabilities - Dictionary object with non-browser specific capabilities only, such as "proxy" or "loggingPref".
+ Only available in Legacy mode
+ - port - Deprecated: port you would like the service to run, if left as 0, a free port will be found.
+ - verbose - whether to set verbose logging in the service. Only available in Legacy Mode
+ - service_log_path - Deprecated: Where to log information from the driver.
- keep_alive - Whether to configure EdgeRemoteConnection to use HTTP keep-alive.
+ - service_args - Deprecated: List of args to pass to the driver service
+ - is_legacy: Whether to use MicrosoftWebDriver.exe (legacy) or MSEdgeDriver.exe (chromium-based). Defaults to True.
"""
- if port != DEFAULT_PORT:
- warnings.warn('port has been deprecated, please pass in a Service object',
- DeprecationWarning, stacklevel=2)
- self.port = port
+ if not is_legacy:
+ executable_path = "msedgedriver"
- if service_log_path != DEFAULT_SERVICE_LOG_PATH:
- warnings.warn('service_log_path has been deprecated, please pass in a Service object',
- DeprecationWarning, stacklevel=2)
- if capabilities is not None:
- warnings.warn('capabilities has been deprecated, please pass in a Service object',
- DeprecationWarning, stacklevel=2)
- if service_log_path != DEFAULT_SERVICE_LOG_PATH:
- warnings.warn('service_log_path has been deprecated, please pass in a Service object',
- DeprecationWarning, stacklevel=2)
- if verbose:
- warnings.warn('verbose has been deprecated, please pass in a Service object',
- DeprecationWarning, stacklevel=2)
+ service = service or Service(executable_path,
+ port=port, verbose=verbose,
+ log_path=service_log_path, is_legacy=is_legacy)
- if service:
- self.service = service
- else:
- self.service = Service(executable_path, port=self.port, verbose=verbose,
- log_path=service_log_path)
- self.service.start()
-
- if capabilities is None:
- capabilities = DesiredCapabilities.EDGE
-
- RemoteWebDriver.__init__(
- self,
- command_executor=RemoteConnection(self.service.service_url,
- keep_alive=keep_alive),
- desired_capabilities=capabilities)
- self._is_remote = False
-
- @property
- def edge_service(self):
- warnings.warn("'edge_service' has been deprecated, please use 'service'",
- DeprecationWarning, stacklevel=2)
- return self.service
-
- def quit(self):
- RemoteWebDriver.quit(self)
- self.service.stop()
+ super(WebDriver, self).__init__(
+ executable_path,
+ port,
+ options,
+ service_args,
+ DesiredCapabilities.EDGE,
+ service_log_path,
+ service,
+ keep_alive)
diff --git a/setup.py b/setup.py
index 12a3850..85ebdff 100755
--- a/setup.py
+++ b/setup.py
@@ -54,6 +54,7 @@
'selenium.common',
'selenium.webdriver',
'selenium.webdriver.android',
+ 'selenium.webdriver.chromium',
'selenium.webdriver.chrome',
'selenium.webdriver.common',
'selenium.webdriver.common.html5',
diff --git a/test/selenium/webdriver/common/alerts_tests.py b/test/selenium/webdriver/common/alerts_tests.py
index 7ed1a12..ca72ec7 100644
--- a/test/selenium/webdriver/common/alerts_tests.py
+++ b/test/selenium/webdriver/common/alerts_tests.py
@@ -142,6 +142,10 @@
condition=sys.platform == 'darwin',
reason='https://bugs.chromium.org/p/chromedriver/issues/detail?id=26',
run=False)
[email protected]_chromiumedge(
+ condition=sys.platform == 'darwin',
+ reason='https://bugs.chromium.org/p/chromedriver/issues/detail?id=26',
+ run=False)
def testAlertShouldNotAllowAdditionalCommandsIfDimissed(driver, pages):
pages.load("alerts.html")
driver.find_element(By.ID, "alert").click()
@@ -246,6 +250,7 @@
@pytest.mark.xfail_firefox(reason='Non W3C conformant')
@pytest.mark.xfail_chrome(reason='Non W3C conformant')
[email protected]_chromiumedge(reason='Non W3C conformant')
def testShouldHandleAlertOnPageBeforeUnload(driver, pages):
pages.load("pageWithOnBeforeUnloadMessage.html")
@@ -287,6 +292,8 @@
@pytest.mark.xfail_chrome(
reason='https://bugs.chromium.org/p/chromedriver/issues/detail?id=1537')
[email protected]_chromiumedge(
+ reason='https://bugs.chromium.org/p/chromedriver/issues/detail?id=1537')
@pytest.mark.xfail_marionette(
reason='https://bugzilla.mozilla.org/show_bug.cgi?id=1279211')
@pytest.mark.xfail_remote(
diff --git a/test/selenium/webdriver/common/api_example_tests.py b/test/selenium/webdriver/common/api_example_tests.py
index 679c9be..7a45f4a 100644
--- a/test/selenium/webdriver/common/api_example_tests.py
+++ b/test/selenium/webdriver/common/api_example_tests.py
@@ -286,6 +286,7 @@
@pytest.mark.xfail_chrome(raises=WebDriverException)
[email protected]_chromiumedge(raises=WebDriverException)
@pytest.mark.xfail_marionette(raises=WebDriverException)
def testGetLogTypes(driver, pages):
pages.load("blank.html")
@@ -293,6 +294,7 @@
@pytest.mark.xfail_chrome(raises=WebDriverException)
[email protected]_chromiumedge(raises=WebDriverException)
@pytest.mark.xfail_marionette(raises=WebDriverException)
def testGetLog(driver, pages):
pages.load("blank.html")
diff --git a/test/selenium/webdriver/common/appcache_tests.py b/test/selenium/webdriver/common/appcache_tests.py
index 946a33b..33f147c 100644
--- a/test/selenium/webdriver/common/appcache_tests.py
+++ b/test/selenium/webdriver/common/appcache_tests.py
@@ -22,6 +22,7 @@
@pytest.mark.xfail_chrome
[email protected]_chromiumedge
@pytest.mark.xfail_marionette(raises=WebDriverException)
@pytest.mark.xfail_remote
def testWeCanGetTheStatusOfTheAppCache(driver, pages):
diff --git a/test/selenium/webdriver/common/click_scrolling_tests.py b/test/selenium/webdriver/common/click_scrolling_tests.py
index bc8adb8..f3744f0 100644
--- a/test/selenium/webdriver/common/click_scrolling_tests.py
+++ b/test/selenium/webdriver/common/click_scrolling_tests.py
@@ -87,6 +87,8 @@
@pytest.mark.xfail_chrome(
reason='https://bugs.chromium.org/p/chromedriver/issues/detail?id=1542')
[email protected]_chromiumedge(
+ reason='https://bugs.chromium.org/p/chromedriver/issues/detail?id=1542')
@pytest.mark.xfail_marionette
@pytest.mark.xfail_remote
def testShouldNotScrollIfAlreadyScrolledAndElementIsInView(driver, pages):
diff --git a/test/selenium/webdriver/common/driver_element_finding_tests.py b/test/selenium/webdriver/common/driver_element_finding_tests.py
index a1402f9..7b02881 100755
--- a/test/selenium/webdriver/common/driver_element_finding_tests.py
+++ b/test/selenium/webdriver/common/driver_element_finding_tests.py
@@ -337,6 +337,7 @@
@pytest.mark.xfail_chrome(raises=InvalidSelectorException)
[email protected]_chromiumedge(raises=InvalidSelectorException)
@pytest.mark.xfail_firefox(raises=InvalidSelectorException)
@pytest.mark.xfail_remote(raises=InvalidSelectorException)
@pytest.mark.xfail_marionette(raises=WebDriverException)
diff --git a/test/selenium/webdriver/common/frame_switching_tests.py b/test/selenium/webdriver/common/frame_switching_tests.py
index 4c7c9a6..05e368f 100644
--- a/test/selenium/webdriver/common/frame_switching_tests.py
+++ b/test/selenium/webdriver/common/frame_switching_tests.py
@@ -367,6 +367,7 @@
@pytest.mark.xfail_chrome(raises=NoSuchElementException)
[email protected]_chromiumedge(raises=NoSuchElementException)
@pytest.mark.xfail_marionette(raises=WebDriverException,
reason='https://github.com/mozilla/geckodriver/issues/614')
@pytest.mark.xfail_remote(raises=WebDriverException,
diff --git a/test/selenium/webdriver/common/page_load_timeout_tests.py b/test/selenium/webdriver/common/page_load_timeout_tests.py
index 19c83aa..b74d9fd 100644
--- a/test/selenium/webdriver/common/page_load_timeout_tests.py
+++ b/test/selenium/webdriver/common/page_load_timeout_tests.py
@@ -33,6 +33,7 @@
@pytest.mark.xfail_chrome
[email protected]_chromiumedge
def testClickShouldTimeout(driver, pages):
pages.load("simpleTest.html")
driver.set_page_load_timeout(0.01)
diff --git a/test/selenium/webdriver/common/page_loading_tests.py b/test/selenium/webdriver/common/page_loading_tests.py
index 2f19280..8cb321a 100644
--- a/test/selenium/webdriver/common/page_loading_tests.py
+++ b/test/selenium/webdriver/common/page_loading_tests.py
@@ -119,6 +119,7 @@
@pytest.mark.xfail_marionette(run=False)
@pytest.mark.xfail_remote(run=False)
@pytest.mark.xfail_chrome(run=False)
[email protected]_chromiumedge(run=False)
def testShouldNotHangifDocumentOpenCallIsNeverFollowedByDocumentCloseCall(driver, pages):
pages.load("document_write_in_onload.html")
driver.find_element(By.XPATH, "//body")
diff --git a/test/selenium/webdriver/common/position_and_size_tests.py b/test/selenium/webdriver/common/position_and_size_tests.py
index 0725241..2285880 100644
--- a/test/selenium/webdriver/common/position_and_size_tests.py
+++ b/test/selenium/webdriver/common/position_and_size_tests.py
@@ -57,6 +57,7 @@
@pytest.mark.xfail_chrome
[email protected]_chromiumedge
@pytest.mark.xfail_marionette
@pytest.mark.xfail_remote
def testShouldGetCoordinatesOfAnElementInAFrame(driver, pages):
@@ -68,6 +69,7 @@
@pytest.mark.xfail_chrome
[email protected]_chromiumedge
@pytest.mark.xfail_marionette
@pytest.mark.xfail_remote
def testShouldGetCoordinatesOfAnElementInANestedFrame(driver, pages):
diff --git a/test/selenium/webdriver/common/select_class_tests.py b/test/selenium/webdriver/common/select_class_tests.py
index 99fbca3..a38293b 100644
--- a/test/selenium/webdriver/common/select_class_tests.py
+++ b/test/selenium/webdriver/common/select_class_tests.py
@@ -43,6 +43,7 @@
@pytest.mark.xfail_chrome
[email protected]_chromiumedge
@pytest.mark.xfail_firefox
@pytest.mark.xfail_remote
@pytest.mark.xfail_marionette(reason='https://bugzilla.mozilla.org/show_bug.cgi?id=1429403')
@@ -69,6 +70,7 @@
@pytest.mark.xfail_chrome
[email protected]_chromiumedge
@pytest.mark.xfail_firefox
@pytest.mark.xfail_remote
@pytest.mark.xfail_marionette(reason='https://bugzilla.mozilla.org/show_bug.cgi?id=1429403')
@@ -97,6 +99,8 @@
@pytest.mark.xfail_chrome(
reason='https://bugs.chromium.org/p/chromedriver/issues/detail?id=822')
[email protected]_chromiumedge(
+ reason='https://bugs.chromium.org/p/chromedriver/issues/detail?id=822')
def testSelectByVisibleTextShouldNormalizeSpaces(driver, pages):
pages.load("formPage.html")
@@ -109,6 +113,7 @@
@pytest.mark.xfail_chrome
[email protected]_chromiumedge
@pytest.mark.xfail_firefox
@pytest.mark.xfail_remote
@pytest.mark.xfail_marionette(reason='https://bugzilla.mozilla.org/show_bug.cgi?id=1429403')
diff --git a/test/selenium/webdriver/common/window_switching_tests.py b/test/selenium/webdriver/common/window_switching_tests.py
index 8ce87f6..52eff32 100644
--- a/test/selenium/webdriver/common/window_switching_tests.py
+++ b/test/selenium/webdriver/common/window_switching_tests.py
@@ -193,6 +193,7 @@
@pytest.mark.xfail_ie
@pytest.mark.xfail_chrome
[email protected]_chromiumedge
def testShouldBeAbleToCreateANewWindow(driver, pages):
original_handle = driver.current_window_handle
diff --git a/test/selenium/webdriver/common/window_tests.py b/test/selenium/webdriver/common/window_tests.py
index 11eeb2a..65afeee 100644
--- a/test/selenium/webdriver/common/window_tests.py
+++ b/test/selenium/webdriver/common/window_tests.py
@@ -23,6 +23,7 @@
@pytest.mark.xfail_ie
@pytest.mark.xfail_chrome(reason="Fails on Travis")
[email protected]_chromiumedge(reason="Fails on Travis")
@pytest.mark.xfail_marionette(reason="Fails on Travis")
@pytest.mark.xfail_firefox(reason="Fails on Travis")
@pytest.mark.xfail_remote(reason="Fails on Travis")
@@ -119,6 +120,8 @@
@pytest.mark.xfail_chrome(raises=WebDriverException,
reason='Fullscreen command not implemented')
[email protected]_chromiumedge(raises=WebDriverException,
+ reason='Fullscreen command not implemented')
@pytest.mark.xfail_firefox(raises=WebDriverException,
reason='Fullscreen command not implemented')
@pytest.mark.xfail_safari(raises=WebDriverException,
@@ -145,6 +148,8 @@
@pytest.mark.xfail_chrome(raises=WebDriverException,
reason='Minimize command not implemented')
[email protected]_chromiumedge(raises=WebDriverException,
+ reason='Minimize command not implemented')
@pytest.mark.xfail_firefox(raises=WebDriverException,
reason='Minimize command not implemented')
@pytest.mark.xfail_safari(raises=WebDriverException,
diff --git a/tox.ini b/tox.ini
index 3d9b4c4..afa5d9f 100644
--- a/tox.ini
+++ b/tox.ini
@@ -12,12 +12,13 @@
py{27,36}-marionette: py.test --driver=Marionette {posargs}
py{27,36}-remote: py.test --driver=Remote {posargs}
py{27,36}-safari: py.test --driver=Safari {posargs}
+ py{27,36}-chromiumedge: py.test --driver=ChromiumEdge {posargs}
install_command = pip install -v --no-index --find-links=../third_party/py {opts} {packages}
deps =
pytest==3.0.3
pytest-instafail==0.3.0
pytest-mock==1.5.0
- py{27,33,34,35,36}-{unit,chrome,firefox,marionette}: pytest-xdist==1.15
+ py{27,33,34,35,36}-{unit,chrome,firefox,marionette,chromiumedge}: pytest-xdist==1.15
urllib3==1.23
[testenv:docs]