Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[py] Use a capability to switch engines for Edge #8096

Merged
merged 7 commits into from
Mar 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions py/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,52 @@ py_test_suite(
],
)

py_test_suite(
name = "test-edge",
size = "large",
srcs = glob([
"test/selenium/webdriver/edge/**/*.py",
"test/selenium/webdriver/common/**/*.py",
"test/selenium/webdriver/support/**/*.py",
]),
args = [
"--instafail",
"--driver=Edge",
],
tags = [
"no-sandbox",
],
deps = [
":init-tree",
":selenium",
":webserver",
"//third_party/py:pytest",
],
)

py_test_suite(
name = "test-edge-chromium",
size = "large",
srcs = glob([
"test/selenium/webdriver/edge/**/*.py",
"test/selenium/webdriver/common/**/*.py",
"test/selenium/webdriver/support/**/*.py",
]),
args = [
"--instafail",
"--driver=ChromiumEdge",
],
tags = [
"no-sandbox",
],
deps = [
":init-tree",
":selenium",
":webserver",
"//third_party/py:pytest",
],
)

py_test_suite(
name = "test-firefox",
size = "large",
Expand Down
9 changes: 5 additions & 4 deletions py/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ def fin():
options = get_options(driver_class, request.config)
if driver_class == 'ChromiumEdge':
options = get_options(driver_class, request.config)
kwargs.update({'is_legacy': False})
if driver_path is not None:
kwargs['executable_path'] = driver_path
if options is not None:
Expand All @@ -136,10 +135,12 @@ def get_options(driver_class, config):
browser_args = config.option.args
options = None

if driver_class == 'ChromiumEdge':
options = getattr(webdriver, 'EdgeOptions')()
options.use_chromium = True

if browser_path or browser_args:
if driver_class == 'ChromiumEdge':
options = getattr(webdriver, 'EdgeOptions')(False)
else:
if not options:
options = getattr(webdriver, '{}Options'.format(driver_class))()
if driver_class == 'WebKitGTK':
options.overlay_scrollbars_enabled = False
Expand Down
7 changes: 6 additions & 1 deletion py/selenium/webdriver/chrome/webdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from selenium.webdriver.chromium.webdriver import ChromiumDriver
from .options import Options
from .service import Service
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities


DEFAULT_PORT = 0
Expand Down Expand Up @@ -49,6 +50,9 @@ def __init__(self, executable_path="chromedriver", port=DEFAULT_PORT,
- 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 chrome_options:
warnings.warn('use options instead of chrome_options',
DeprecationWarning, stacklevel=2)
Expand All @@ -57,7 +61,8 @@ def __init__(self, executable_path="chromedriver", port=DEFAULT_PORT,
if service is None:
service = Service(executable_path, port, service_args, service_log_path)

super(WebDriver, self).__init__(executable_path, port, options,
super(WebDriver, self).__init__(DesiredCapabilities.CHROME['browserName'], "goog",
port, options,
service_args, desired_capabilities,
service_log_path, service, keep_alive)

Expand Down
18 changes: 8 additions & 10 deletions py/selenium/webdriver/chromium/remote_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,15 @@


class ChromiumRemoteConnection(RemoteConnection):

browser_name = DesiredCapabilities.CHROME['browserName']

def __init__(self, remote_server_addr, keep_alive=True):
def __init__(self, remote_server_addr, vendor_prefix, browser_name, keep_alive=True):
RemoteConnection.__init__(self, remote_server_addr, keep_alive)
self.browser_name = browser_name
self._commands["launchApp"] = ('POST', '/session/$sessionId/chromium/launch_app')
self._commands["setNetworkConditions"] = ('POST', '/session/$sessionId/chromium/network_conditions')
self._commands["getNetworkConditions"] = ('GET', '/session/$sessionId/chromium/network_conditions')
self._commands['executeCdpCommand'] = ('POST', '/session/$sessionId/goog/cdp/execute')
self._commands['getSinks'] = ('GET', '/session/$sessionId/goog/cast/get_sinks')
self._commands['getIssueMessage'] = ('GET', '/session/$sessionId/goog/cast/get_issue_message')
self._commands['setSinkToUse'] = ('POST', '/session/$sessionId/goog/cast/set_sink_to_use')
self._commands['startTabMirroring'] = ('POST', '/session/$sessionId/goog/cast/start_tab_mirroring')
self._commands['stopCasting'] = ('POST', '/session/$sessionId/goog/cast/stop_casting')
self._commands['executeCdpCommand'] = ('POST', '/session/$sessionId/{}/cdp/execute'.format(vendor_prefix))
self._commands['getSinks'] = ('GET', '/session/$sessionId/{}/cast/get_sinks'.format(vendor_prefix))
self._commands['getIssueMessage'] = ('GET', '/session/$sessionId/{}/cast/get_issue_message'.format(vendor_prefix))
self._commands['setSinkToUse'] = ('POST', '/session/$sessionId/{}/cast/set_sink_to_use'.format(vendor_prefix))
self._commands['startTabMirroring'] = ('POST', '/session/$sessionId/{}/cast/start_tab_mirroring'.format(vendor_prefix))
self._commands['stopCasting'] = ('POST', '/session/$sessionId/{}/cast/stop_casting'.format(vendor_prefix))
11 changes: 5 additions & 6 deletions py/selenium/webdriver/chromium/webdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,17 @@ 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,
def __init__(self, browser_name, vendor_prefix,
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
browser_name - Browser name used when matching capabilities.
vendor_prefix - Company prefix to apply to vendor-specific WebDriver extension commands.
- 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
Expand All @@ -46,9 +47,6 @@ def __init__(self, executable_path="chromedriver", port=DEFAULT_PORT,
- 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)
Expand Down Expand Up @@ -81,6 +79,7 @@ def __init__(self, executable_path="chromedriver", port=DEFAULT_PORT,
self,
command_executor=ChromiumRemoteConnection(
remote_server_addr=self.service.service_url,
browser_name=browser_name, vendor_prefix=vendor_prefix,
keep_alive=keep_alive),
desired_capabilities=desired_capabilities)
except Exception:
Expand Down
52 changes: 21 additions & 31 deletions py/selenium/webdriver/edge/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,52 +22,42 @@
class Options(ChromiumOptions):
KEY = "ms:edgeOptions"

def __init__(self, is_legacy=True):
def __init__(self):
super(Options, self).__init__()
self._is_legacy = is_legacy
self._custom_browser_name = None

if is_legacy:
self._page_load_strategy = "normal"
self._use_chromium = False
self._use_webview = False

@property
def custom_browser_name(self):
return self._custom_browser_name
def use_chromium(self):
return self._use_chromium

@custom_browser_name.setter
def custom_browser_name(self, value):
self._custom_browser_name = value
@use_chromium.setter
def use_chromium(self, value):
self._use_chromium = bool(value)

@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")
def use_webview(self):
return self._use_webview

if value not in ['normal', 'eager', 'none']:
raise ValueError("Page Load Strategy should be 'normal', 'eager' or 'none'.")
self._page_load_strategy = value
@use_webview.setter
def use_webview(self, value):
self._use_webview = bool(value)

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_caps = super(Options, self).to_capabilities()
if self._custom_browser_name:
return_caps['browserName'] = self._custom_browser_name
return return_caps

caps = self._caps
caps['pageLoadStrategy'] = self._page_load_strategy

if self._use_chromium:
caps = super(Options, self).to_capabilities()
if self._use_webview:
caps['browserName'] = 'WebView2'
else:
caps['platform'] = 'windows'

caps['ms:edgeChromium'] = self._use_chromium
return caps

@property
Expand Down
13 changes: 5 additions & 8 deletions py/selenium/webdriver/edge/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
class Service(service.ChromiumService):

def __init__(self, executable_path, port=0, verbose=False, log_path=None,
is_legacy=True, service_args=None, env=None):
service_args=None, env=None):
"""
Creates a new instance of the EdgeDriver service.
EdgeDriver provides an interface for Microsoft WebDriver to use
Expand All @@ -32,19 +32,16 @@ def __init__(self, executable_path, port=0, verbose=False, log_path=None,
- 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.
Defaults to False.
- 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 []

if is_legacy:
if verbose:
self.service_args.append("--verbose")
if verbose:
self.service_args.append("--verbose")

service.ChromiumService.__init__(
self,
super(Service, self).__init__(
executable_path,
port,
service_args,
Expand Down
60 changes: 33 additions & 27 deletions py/selenium/webdriver/edge/webdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,52 +14,58 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.edge.service import Service
import warnings
from selenium.webdriver.chromium.webdriver import ChromiumDriver
from .options import Options
from .service import Service
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities


DEFAULT_PORT = 0
DEFAULT_SERVICE_LOG_PATH = None


class WebDriver(ChromiumDriver):
"""
Controls the Microsoft Edge driver and allows you to drive the browser.
You will need to download either the MicrosoftWebDriver (Legacy)
or MSEdgeDriver (Chromium) executable from
https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/
"""

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, is_legacy=True,
service_args=None):
def __init__(self, executable_path='MicrosoftWebDriver.exe', port=DEFAULT_PORT,
options=None, service_args=None,
capabilities=None, service_log_path=DEFAULT_SERVICE_LOG_PATH,
service=None, keep_alive=False, verbose=False):
"""
Creates a new instance of the edge driver.
Starts the service and then creates new instance of edge driver.

:Args:
- 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
- options - this takes an instance of EdgeOptions
- service_args - Deprecated: List of args to pass to the driver service
- 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 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.
- verbose - whether to set verbose logging in the service.
"""
if not is_legacy:
if executable_path != 'MicrosoftWebDriver.exe':
warnings.warn('executable_path has been deprecated, please pass in a Service object',
bwalderman marked this conversation as resolved.
Show resolved Hide resolved
DeprecationWarning, stacklevel=2)

if options is not None and options.use_chromium:
executable_path = "msedgedriver"

service = service or Service(executable_path,
port=port,
verbose=verbose,
log_path=service_log_path,
is_legacy=is_legacy)
if service is None:
service = Service(executable_path, port, service_args, service_log_path)

super(WebDriver, self).__init__(DesiredCapabilities.EDGE['browserName'], "ms",
port, options,
service_args, capabilities,
service_log_path, service, keep_alive)

super(WebDriver, self).__init__(
executable_path,
port,
options,
service_args,
DesiredCapabilities.EDGE,
service_log_path,
service,
keep_alive)
def create_options(self):
return Options()
21 changes: 15 additions & 6 deletions py/test/unit/selenium/webdriver/edge/edge_options_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ def test_raises_exception_with_invalid_page_load_strategy(options):

def test_set_page_load_strategy(options):
options.page_load_strategy = 'normal'
assert options._page_load_strategy == 'normal'
caps = options.to_capabilities()
assert caps['pageLoadStrategy'] == 'normal'


def test_get_page_load_strategy(options):
options._page_load_strategy = 'normal'
options._caps['pageLoadStrategy'] = 'normal'
assert options.page_load_strategy == 'normal'


Expand All @@ -58,8 +59,16 @@ def test_is_a_baseoptions(options):
assert isinstance(options, BaseOptions)


def test_custom_browser_name():
options = Options(is_legacy=False)
options.custom_browser_name = "testbrowsername"
def test_use_chromium():
options = Options()
options.use_chromium = True
caps = options.to_capabilities()
assert caps['ms:edgeChromium'] == True


def test_use_webview():
options = Options()
options.use_chromium = True
options.use_webview = True
caps = options.to_capabilities()
assert caps['browserName'] == "testbrowsername"
assert caps['browserName'] == "WebView2"