blob: ec04cf10c0250cd9dc49bf8aef684f5e6a28dcc1 [file] [log] [blame] [edit]
# 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 os
import platform
import socket
import subprocess
import time
from test.selenium.webdriver.common.network import get_lan_ip
from test.selenium.webdriver.common.webserver import SimpleWebServer
from urllib.request import urlopen
import pytest
from selenium import webdriver
from selenium.webdriver import DesiredCapabilities
drivers = (
"chrome",
"edge",
"firefox",
"ie",
"remote",
"safari",
"webkitgtk",
"chromiumedge",
"wpewebkit",
)
def pytest_addoption(parser):
parser.addoption(
"--driver",
action="append",
choices=drivers,
dest="drivers",
metavar="DRIVER",
help="driver to run tests against ({})".format(", ".join(drivers)),
)
parser.addoption("--browser-binary", action="store", dest="binary", help="location of the browser binary")
parser.addoption(
"--driver-binary", action="store", dest="executable", help="location of the service executable binary"
)
parser.addoption("--browser-args", action="store", dest="args", help="arguments to start the browser with")
parser.addoption("--headless", action="store", dest="headless", help="Allow tests to run in headless")
def pytest_ignore_collect(path, config):
drivers_opt = config.getoption("drivers")
_drivers = set(drivers).difference(drivers_opt or drivers)
if drivers_opt:
_drivers.add("unit")
parts = path.dirname.split(os.path.sep)
return len([d for d in _drivers if d.lower() in parts]) > 0
driver_instance = None
@pytest.fixture(scope="function")
def driver(request):
kwargs = {}
try:
driver_class = request.param.capitalize()
except AttributeError:
raise Exception("This test requires a --driver to be specified.")
# skip tests if not available on the platform
_platform = platform.system()
if driver_class == "Safari" and _platform != "Darwin":
pytest.skip("Safari tests can only run on an Apple OS")
if (driver_class == "Ie") and _platform != "Windows":
pytest.skip("IE and EdgeHTML Tests can only run on Windows")
if "WebKit" in driver_class and _platform != "Linux":
pytest.skip("Webkit tests can only run on Linux")
# conditionally mark tests as expected to fail based on driver
marker = request.node.get_closest_marker(f"xfail_{driver_class.lower()}")
if marker is not None:
if "run" in marker.kwargs:
if marker.kwargs["run"] is False:
pytest.skip()
yield
return
if "raises" in marker.kwargs:
marker.kwargs.pop("raises")
pytest.xfail(**marker.kwargs)
def fin():
global driver_instance
if driver_instance is not None:
driver_instance.quit()
driver_instance = None
request.addfinalizer(fin)
driver_path = request.config.option.executable
options = None
global driver_instance
if driver_instance is None:
if driver_class == "Firefox":
options = get_options(driver_class, request.config)
if driver_class == "Chrome":
options = get_options(driver_class, request.config)
if driver_class == "Remote":
capabilities = DesiredCapabilities.FIREFOX.copy()
kwargs.update({"desired_capabilities": capabilities})
options = get_options("Firefox", request.config)
if driver_class == "WebKitGTK":
options = get_options(driver_class, request.config)
if driver_class == "Edge":
options = get_options(driver_class, request.config)
if driver_class == "WPEWebKit":
options = get_options(driver_class, request.config)
if driver_path is not None:
kwargs["executable_path"] = driver_path
if options is not None:
kwargs["options"] = options
driver_instance = getattr(webdriver, driver_class)(**kwargs)
yield driver_instance
if request.node.get_closest_marker("no_driver_after_test"):
driver_instance = None
def get_options(driver_class, config):
browser_path = config.option.binary
browser_args = config.option.args
headless = bool(config.option.headless)
options = None
if driver_class == "ChromiumEdge":
options = getattr(webdriver, "EdgeOptions")()
if browser_path or browser_args:
if not options:
options = getattr(webdriver, f"{driver_class}Options")()
if driver_class == "WebKitGTK":
options.overlay_scrollbars_enabled = False
if browser_path is not None:
options.binary_location = browser_path
if browser_args is not None:
for arg in browser_args.split():
options.add_argument(arg)
if headless:
if not options:
options = getattr(webdriver, f"{driver_class}Options")()
if driver_class == "Chrome" or driver_class == "Edge":
options.add_argument("--headless=new")
if driver_class == "Firefox":
options.add_argument("-headless")
return options
@pytest.fixture(scope="session", autouse=True)
def stop_driver(request):
def fin():
global driver_instance
if driver_instance is not None:
driver_instance.quit()
driver_instance = None
request.addfinalizer(fin)
def pytest_exception_interact(node, call, report):
if report.failed:
global driver_instance
if driver_instance is not None:
driver_instance.quit()
driver_instance = None
@pytest.fixture
def pages(driver, webserver):
class Pages:
def url(self, name, localhost=False):
return webserver.where_is(name, localhost)
def load(self, name):
driver.get(self.url(name))
return Pages()
@pytest.fixture(autouse=True, scope="session")
def server(request):
drivers = request.config.getoption("drivers")
if drivers is None or "remote" not in drivers:
yield None
return
_host = "localhost"
_port = 4444
_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"java/src/org/openqa/selenium/grid/selenium_server_deploy.jar",
)
def wait_for_server(url, timeout):
start = time.time()
while time.time() - start < timeout:
try:
urlopen(url)
return 1
except OSError:
time.sleep(0.2)
return 0
_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
url = f"http://{_host}:{_port}/status"
try:
_socket.connect((_host, _port))
print(
"The remote driver server is already running or something else"
"is using port {}, continuing...".format(_port)
)
except Exception:
print("Starting the Selenium server")
process = subprocess.Popen(["java", "-jar", _path, "standalone", "--port", "4444"])
print(f"Selenium server running as process: {process.pid}")
assert wait_for_server(url, 10), f"Timed out waiting for Selenium server at {url}"
print("Selenium server is ready")
yield process
process.terminate()
process.wait()
print("Selenium server has been terminated")
@pytest.fixture(autouse=True, scope="session")
def webserver():
webserver = SimpleWebServer(host=get_lan_ip())
webserver.start()
yield webserver
webserver.stop()
@pytest.fixture
def edge_service():
from selenium.webdriver.edge.service import Service as EdgeService
return EdgeService