blob: d2e05a63ed92c33f389f694fc1d19eee0d64053b [file] [log] [blame]
Alexei Barantsev70795f32019-07-26 18:48:34 +03001# Licensed to the Software Freedom Conservancy (SFC) under one
2# or more contributor license agreements. See the NOTICE file
3# distributed with this work for additional information
4# regarding copyright ownership. The SFC licenses this file
5# to you under the Apache License, Version 2.0 (the
6# "License"); you may not use this file except in compliance
7# with the License. You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing,
12# software distributed under the License is distributed on an
13# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14# KIND, either express or implied. See the License for the
15# specific language governing permissions and limitations
16# under the License.
17
18import os
19import socket
20import subprocess
21import sys
22import time
23
24import pytest
25from _pytest.skipping import MarkEvaluator
26
27from selenium import webdriver
28from selenium.webdriver import DesiredCapabilities
29from test.selenium.webdriver.common.webserver import SimpleWebServer
30from test.selenium.webdriver.common.network import get_lan_ip
31
32if sys.version_info[0] == 3:
33 from urllib.request import urlopen
34else:
35 from urllib import urlopen
36
37drivers = (
38 'BlackBerry',
39 'Chrome',
40 'Edge',
41 'Firefox',
42 'Ie',
43 'Marionette',
44 'Remote',
45 'Safari',
46 'WebKitGTK',
Long Ly01465832019-08-01 19:46:39 -070047 'ChromiumEdge',
Alexei Barantsev70795f32019-07-26 18:48:34 +030048)
49
50
51def pytest_addoption(parser):
52 parser.addoption('--driver', action='append', choices=drivers, dest='drivers',
53 metavar='DRIVER',
54 help='driver to run tests against ({})'.format(', '.join(drivers)))
55 parser.addoption('--browser-binary', action='store', dest='binary',
56 help='location of the browser binary')
57 parser.addoption('--driver-binary', action='store', dest='executable',
58 help='location of the service executable binary')
59 parser.addoption('--browser-args', action='store', dest='args',
60 help='arguments to start the browser with')
61
62
63def pytest_ignore_collect(path, config):
64 drivers_opt = config.getoption('drivers')
65 _drivers = set(drivers).difference(drivers_opt or drivers)
66 if drivers_opt:
67 _drivers.add('unit')
68 parts = path.dirname.split(os.path.sep)
69 return len([d for d in _drivers if d.lower() in parts]) > 0
70
71
72driver_instance = None
73
74
75@pytest.fixture(scope='function')
76def driver(request):
77 kwargs = {}
78
79 try:
80 driver_class = request.param
81 except AttributeError:
82 raise Exception('This test requires a --driver to be specified.')
83
84 # conditionally mark tests as expected to fail based on driver
85 request.node._evalxfail = request.node._evalxfail or MarkEvaluator(
86 request.node, 'xfail_{0}'.format(driver_class.lower()))
87 if request.node._evalxfail.istrue():
88 def fin():
89 global driver_instance
90 if driver_instance is not None:
91 driver_instance.quit()
92 driver_instance = None
93 request.addfinalizer(fin)
94
95 # skip driver instantiation if xfail(run=False)
96 if not request.config.getoption('runxfail'):
97 if request.node._evalxfail.istrue():
98 if request.node._evalxfail.get('run') is False:
99 yield
100 return
101
102 driver_path = request.config.option.executable
103 options = None
104
105 global driver_instance
106 if driver_instance is None:
107 if driver_class == 'BlackBerry':
108 kwargs.update({'device_password': 'password'})
109 if driver_class == 'Firefox':
110 kwargs.update({'capabilities': {'marionette': False}})
111 options = get_options(driver_class, request.config)
112 if driver_class == 'Marionette':
113 driver_class = 'Firefox'
114 options = get_options(driver_class, request.config)
115 if driver_class == 'Remote':
116 capabilities = DesiredCapabilities.FIREFOX.copy()
117 kwargs.update({'desired_capabilities': capabilities})
118 options = get_options('Firefox', request.config)
119 if driver_class == 'WebKitGTK':
120 options = get_options(driver_class, request.config)
Long Ly01465832019-08-01 19:46:39 -0700121 if driver_class == 'ChromiumEdge':
Lucas Tierneyc401cb32019-08-01 21:49:11 -0500122 options = get_options(driver_class, request.config)
Alexei Barantsev70795f32019-07-26 18:48:34 +0300123 if driver_path is not None:
124 kwargs['executable_path'] = driver_path
125 if options is not None:
126 kwargs['options'] = options
127 driver_instance = getattr(webdriver, driver_class)(**kwargs)
128 yield driver_instance
129 if MarkEvaluator(request.node, 'no_driver_after_test').istrue():
130 driver_instance = None
131
132
133def get_options(driver_class, config):
134 browser_path = config.option.binary
135 browser_args = config.option.args
136 options = None
Long Ly01465832019-08-01 19:46:39 -0700137
AutomatedTestera23c0682020-03-10 11:42:11 +0000138 if driver_class == 'ChromiumEdge':
139 options = getattr(webdriver, 'EdgeOptions')()
140 options.use_chromium = True
141
Alexei Barantsev70795f32019-07-26 18:48:34 +0300142 if browser_path or browser_args:
AutomatedTestera23c0682020-03-10 11:42:11 +0000143 if not options:
Lucas Tierneyc401cb32019-08-01 21:49:11 -0500144 options = getattr(webdriver, '{}Options'.format(driver_class))()
Alexei Barantsev70795f32019-07-26 18:48:34 +0300145 if driver_class == 'WebKitGTK':
146 options.overlay_scrollbars_enabled = False
147 if browser_path is not None:
148 options.binary_location = browser_path
149 if browser_args is not None:
150 for arg in browser_args.split():
151 options.add_argument(arg)
152 return options
153
154
155@pytest.fixture(scope='session', autouse=True)
156def stop_driver(request):
157 def fin():
158 global driver_instance
159 if driver_instance is not None:
160 driver_instance.quit()
161 driver_instance = None
162 request.addfinalizer(fin)
163
164
165def pytest_exception_interact(node, call, report):
166 if report.failed:
167 global driver_instance
168 if driver_instance is not None:
169 driver_instance.quit()
170 driver_instance = None
171
172
173@pytest.fixture
174def pages(driver, webserver):
175 class Pages(object):
176 def url(self, name):
177 return webserver.where_is(name)
178
179 def load(self, name):
180 driver.get(self.url(name))
181 return Pages()
182
183
184@pytest.fixture(autouse=True, scope='session')
185def server(request):
186 drivers = request.config.getoption('drivers')
187 if drivers is None or 'Remote' not in drivers:
188 yield None
189 return
190
191 _host = 'localhost'
192 _port = 4444
Alexei Barantsevf8dd7042019-09-25 09:43:41 +0300193 _path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
Alexei Barantsev2c7b6f12020-04-01 19:30:35 +0300194 'java/server/src/org/openqa/selenium/grid/selenium_server_deploy.jar')
Alexei Barantsev70795f32019-07-26 18:48:34 +0300195
196 def wait_for_server(url, timeout):
197 start = time.time()
198 while time.time() - start < timeout:
199 try:
200 urlopen(url)
201 return 1
Alexei Barantsev803edde2019-09-25 10:58:19 +0300202 except IOError:
Alexei Barantsev70795f32019-07-26 18:48:34 +0300203 time.sleep(0.2)
204 return 0
205
206 _socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Alexei Barantsev803edde2019-09-25 10:58:19 +0300207 url = 'http://{}:{}/status'.format(_host, _port)
Alexei Barantsev70795f32019-07-26 18:48:34 +0300208 try:
209 _socket.connect((_host, _port))
210 print('The remote driver server is already running or something else'
211 'is using port {}, continuing...'.format(_port))
212 except Exception:
213 print('Starting the Selenium server')
Alexei Barantsevb85651b2019-09-24 23:19:37 +0300214 process = subprocess.Popen(['java', '-jar', _path, 'standalone', '--port', '4444'])
Alexei Barantsev70795f32019-07-26 18:48:34 +0300215 print('Selenium server running as process: {}'.format(process.pid))
216 assert wait_for_server(url, 10), 'Timed out waiting for Selenium server at {}'.format(url)
217 print('Selenium server is ready')
218 yield process
219 process.terminate()
220 process.wait()
221 print('Selenium server has been terminated')
222
223
224@pytest.fixture(autouse=True, scope='session')
225def webserver():
226 webserver = SimpleWebServer(host=get_lan_ip())
227 webserver.start()
228 yield webserver
229 webserver.stop()