Skip to content

Commit

Permalink
[py] Add the ability to use Options classes on Safari
Browse files Browse the repository at this point in the history
  • Loading branch information
AutomatedTester committed Apr 22, 2021
1 parent 0b2ab18 commit 4153f72
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 6 deletions.
104 changes: 104 additions & 0 deletions py/selenium/webdriver/safari/options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# 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 typing import Union
import warnings
from selenium.common.exceptions import InvalidArgumentException
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.common.proxy import Proxy
from selenium.webdriver.common.options import ArgOptions


class Log(object):
def __init__(self):
self.level = None

def to_capabilities(self) -> dict:
if self.level:
return {"log": {"level": self.level}}
return {}


class Options(ArgOptions):
KEY = "webkit:safariOptions"

def __init__(self):
super(Options, self).__init__()
self._binary_location = None
self._preferences: dict = {}
self._proxy = None
self.log = Log()

@property
def binary_location(self):
"""
:Returns: The location of the browser binary otherwise an empty string
"""
return self._binary_location

@binary_location.setter
def binary_location(self, value):
"""
Allows you to set the browser binary to launch
:Args:
- value : path to the browser binary
"""
self._binary_location = value

@property
def accept_insecure_certs(self) -> bool:
return self._caps.get('acceptInsecureCerts')

@accept_insecure_certs.setter
def accept_insecure_certs(self, value: bool):
self._caps['acceptInsecureCerts'] = value

@property
def page_load_strategy(self) -> str:
return self._caps["pageLoadStrategy"]

@page_load_strategy.setter
def page_load_strategy(self, strategy: str):
if strategy in ["normal", "eager", "none"]:
self.set_capability("pageLoadStrategy", strategy)
else:
raise ValueError("Strategy can only be one of the following: normal, eager, none")

def to_capabilities(self) -> dict:
"""Marshals the options to an desired capabilities object.
"""
# This intentionally looks at the internal properties
# so if a binary or profile has _not_ been set,
# it will defer to geckodriver to find the system Firefox
# and generate a fresh profile.
caps = self._caps
opts = {}

if self._arguments:
opts["args"] = self._arguments
if self._binary_location:
opts["binary"] = self._binary_location
opts.update(self.log.to_capabilities())

if opts:
caps[Options.KEY] = opts

return caps

@property
def default_capabilities(self) -> dict:
return DesiredCapabilities.SAFARI.copy()
44 changes: 38 additions & 6 deletions py/selenium/webdriver/safari/webdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,34 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
from selenium.common.exceptions import WebDriverException

try:
import http.client as http_client
except ImportError:
import httplib as http_client

import warnings

from selenium.common.exceptions import WebDriverException
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
from .options import Options
from .service import Service
from .remote_connection import SafariRemoteConnection

DEFAULT_EXECUTABLE_PATH = "/usr/bin/safaridriver"
DEFAULT_SAFARI_CAPS = DesiredCapabilities.SAFARI.copy()


class WebDriver(RemoteWebDriver):
"""
Controls the SafariDriver and allows you to drive the browser.
"""

def __init__(self, port=0, executable_path="/usr/bin/safaridriver", reuse_service=False,
desired_capabilities=DesiredCapabilities.SAFARI, quiet=False,
keep_alive=True, service_args=None):
def __init__(self, port=0, executable_path=DEFAULT_EXECUTABLE_PATH, reuse_service=False,
desired_capabilities=DEFAULT_SAFARI_CAPS, quiet=False,
keep_alive=True, service_args=None, options: Options = None, service: Service = None):
"""
Creates a new Safari driver instance and launches or finds a running safaridriver service.
Expand All @@ -47,12 +53,38 @@ def __init__(self, port=0, executable_path="/usr/bin/safaridriver", reuse_servic
- desired_capabilities: Dictionary object with desired capabilities (Can be used to provide various Safari switches).
- quiet - If True, the driver's stdout and stderr is suppressed.
- keep_alive - Whether to configure SafariRemoteConnection to use
HTTP keep-alive. Defaults to False.
HTTP keep-alive. Defaults to True.
- service_args : List of args to pass to the safaridriver service
"""
if port == 0:
warnings.warn("port has been deprecated, please set it via the service class",
DeprecationWarning, stacklevel=2)

if executable_path != DEFAULT_EXECUTABLE_PATH:
warnings.warn("executable_path has been deprecated, please use the Options class to set it",
DeprecationWarning, stacklevel=2)
if not reuse_service:
warnings.warn("reuse_service has been deprecated, please use the Service class to set it",
DeprecationWarning, stacklevel=2)
if desired_capabilities != DEFAULT_SAFARI_CAPS:
warnings.warn("desired_capabilities has been deprecated, please use the Options class to set it",
DeprecationWarning, stacklevel=2)
if not quiet:
warnings.warn("quiet has been deprecated, please use the Service class to set it",
DeprecationWarning, stacklevel=2)
if not keep_alive:
warnings.warn("keep_alive has been deprecated, please use the Service class to set it",
DeprecationWarning, stacklevel=2)

if service_args:
warnings.warn("service_args has been deprecated, please use the Service class to set it",
DeprecationWarning, stacklevel=2)

self._reuse_service = reuse_service
self.service = Service(executable_path, port=port, quiet=quiet, service_args=service_args)
if service:
self.service = service
else:
self.service = Service(executable_path, port=port, quiet=quiet, service_args=service_args)
if not reuse_service:
self.service.start()

Expand Down
57 changes: 57 additions & 0 deletions py/test/unit/selenium/webdriver/safari/safari_options_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# 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 pytest

from selenium.webdriver.safari.options import Options


@pytest.fixture
def options():
return Options()


def test_set_binary_location(options):
options.binary_location = '/foo/bar'
assert options._binary_location == '/foo/bar'


def test_get_binary_location(options):
options._binary_location = '/foo/bar'
assert options.binary_location == '/foo/bar'


def test_creates_capabilities(options):
options._arguments = ['foo']
options._binary_location = '/bar'
caps = options.to_capabilities()
opts = caps.get(Options.KEY)
assert opts
assert 'foo' in opts['args']
assert opts['binary'] == '/bar'


def test_starts_with_default_capabilities(options):
from selenium.webdriver import DesiredCapabilities
caps = DesiredCapabilities.SAFARI.copy()
caps.update({"pageLoadStrategy": "normal"})
assert options._caps == caps


def test_is_a_baseoptions(options):
from selenium.webdriver.common.options import BaseOptions
assert isinstance(options, BaseOptions)

0 comments on commit 4153f72

Please sign in to comment.