@@ -100,23 +100,122 @@ def pytest_ignore_collect(path, config):
100100driver_instance = None
101101
102102
103+ class Driver :
104+ def __init__ (self , driver_class , request ):
105+ self ._driver_class = driver_class
106+ self ._request = request
107+ self ._driver = None
108+ self ._options = None
109+ self ._platform = None
110+ self ._service = None
111+ self .kwargs = {}
112+
113+ @property
114+ def exe_platform (self ):
115+ self ._platform = platform
116+ return self ._platform
117+
118+ @property
119+ def browser_path (self ):
120+ return self ._request .config .option .binary
121+
122+ @property
123+ def browser_args (self ):
124+ return self ._request .config .option .args
125+
126+ @property
127+ def driver_path (self ):
128+ return self ._request .config .option .executable
129+
130+ @property
131+ def headless (self ):
132+ return bool (self ._request .config .option .headless )
133+
134+ @property
135+ def bidi (self ):
136+ return bool (self ._request .config .option .bidi )
137+
138+ @property
139+ def options (self ):
140+ if self .browser_path or self .browser_args :
141+ if not self ._options :
142+ if self ._driver_class == "Remote" :
143+ self ._options = getattr (webdriver , "FirefoxOptions" )() or webdriver .FirefoxOptions ()
144+ self ._options .set_capability ("moz:firefoxOptions" , {})
145+ self ._options .enable_downloads = True
146+ elif self ._driver_class .lower () == "webkitgtk" :
147+ self ._driver_class = "WebKitGTK"
148+ self ._options = getattr (webdriver , f"{ self ._driver_class } Options" )()
149+ self ._options .overlay_scrollbars_enabled = False
150+ elif self ._driver_class .lower () == "wpewebkit" :
151+ self ._driver_class = "WPEWebKit"
152+ self ._options = getattr (webdriver , f"{ self ._driver_class } Options" )()
153+ else :
154+ self ._options = getattr (webdriver , f"{ self ._driver_class } Options" )()
155+
156+ if self .browser_path is not None :
157+ self ._options .binary_location = self .browser_path .strip ("'" )
158+ if self .browser_args is not None :
159+ for arg in self .browser_args .split ():
160+ self ._options .add_argument (arg )
161+
162+ if self .headless :
163+ if self ._driver_class == "Chrome" or self ._driver_class == "Edge" :
164+ self ._options .add_argument ("--headless=new" )
165+ if self ._driver_class == "Firefox" :
166+ self ._options .add_argument ("-headless" )
167+
168+ if self .bidi :
169+ self ._options .web_socket_url = True
170+ self ._options .unhandled_prompt_behavior = "ignore"
171+
172+ if self .driver_path :
173+ self .service = self .driver_path
174+ return self ._options
175+
176+ @property
177+ def service (self ):
178+ executable = self .driver_path
179+ if executable :
180+ module = getattr (webdriver , self ._driver_class .lower ())
181+ self ._service = module .service .Service (executable_path = executable )
182+ return self ._service
183+
184+ @property
185+ def driver (self ):
186+ if not self ._driver :
187+ return self ._initialize_driver ()
188+
189+ def _initialize_driver (self ):
190+ self .kwargs ["options" ] = self .options
191+ self .kwargs ["service" ] = self .service
192+ self ._driver = getattr (webdriver , self ._driver_class )(** self .kwargs )
193+ return self ._driver
194+
195+ @property
196+ def stop_driver (self ):
197+ def fin ():
198+ if self ._driver is not None :
199+ self ._driver .quit ()
200+
201+ return fin
202+
203+
103204@pytest .fixture (scope = "function" )
104205def driver (request ):
105- kwargs = {}
106-
107- # browser can be changed with `--driver=firefox` as an argument or to addopts in pytest.ini
206+ global driver_instance
108207 driver_class = getattr (request , "param" , "Chrome" ).capitalize ()
208+ selenium_driver = Driver (driver_class , request )
209+ driver_instance = selenium_driver .driver
109210
110211 # skip tests if not available on the platform
111- _platform = platform .system ()
112- if driver_class == "Safari" and _platform != "Darwin" :
212+ if driver_class == "Safari" and driver_instance .exe_platform != "Darwin" :
113213 pytest .skip ("Safari tests can only run on an Apple OS" )
114- if ( driver_class == "Ie" ) and _platform != "Windows" :
214+ if driver_class == "Ie" and driver_instance . exe_platform != "Windows" :
115215 pytest .skip ("IE and EdgeHTML Tests can only run on Windows" )
116- if "WebKit" in driver_class and _platform != "Linux" :
216+ if "WebKit" in driver_class and driver_instance . exe_platform != "Linux" :
117217 pytest .skip ("Webkit tests can only run on Linux" )
118-
119- # conditionally mark tests as expected to fail based on driver
218+ # conditionally mark tests as expected to fail based on driver
120219 marker = request .node .get_closest_marker (f"xfail_{ driver_class .lower ()} " )
121220
122221 if marker is not None :
@@ -129,108 +228,10 @@ def driver(request):
129228 marker .kwargs .pop ("raises" )
130229 pytest .xfail (** marker .kwargs )
131230
132- def fin ():
133- global driver_instance
134- if driver_instance is not None :
135- driver_instance .quit ()
136- driver_instance = None
137-
138- request .addfinalizer (fin )
139-
140- driver_path = request .config .option .executable
141- options = None
231+ request .addfinalizer (selenium_driver .stop_driver )
142232
143- global driver_instance
144- if driver_instance is None :
145- if driver_class == "Firefox" :
146- options = get_options (driver_class , request .config )
147- if driver_class == "Chrome" :
148- options = get_options (driver_class , request .config )
149- if driver_class == "Remote" :
150- options = get_options ("Firefox" , request .config ) or webdriver .FirefoxOptions ()
151- options .set_capability ("moz:firefoxOptions" , {})
152- options .enable_downloads = True
153- if driver_class .lower () == "webkitgtk" :
154- driver_class = "WebKitGTK"
155- options = get_options (driver_class , request .config )
156- if driver_class == "Edge" :
157- options = get_options (driver_class , request .config )
158- if driver_class .lower () == "wpewebkit" :
159- driver_class = "WPEWebKit"
160- options = get_options (driver_class , request .config )
161- if driver_path is not None :
162- kwargs ["service" ] = get_service (driver_class , driver_path )
163- if options is not None :
164- kwargs ["options" ] = options
165-
166- driver_instance = getattr (webdriver , driver_class )(** kwargs )
167233 yield driver_instance
168234
169- # Close the browser after BiDi tests. Those make event subscriptions
170- # and doesn't seems to be stable enough, causing the flakiness of the
171- # subsequent tests.
172- # Remove this when BiDi implementation and API is stable.
173- if bool (request .config .option .bidi ):
174-
175- def fin ():
176- global driver_instance
177- if driver_instance is not None :
178- driver_instance .quit ()
179- driver_instance = None
180-
181- request .addfinalizer (fin )
182-
183- if request .node .get_closest_marker ("no_driver_after_test" ):
184- driver_instance = None
185-
186-
187- def get_options (driver_class , config ):
188- browser_path = config .option .binary
189- browser_args = config .option .args
190- headless = bool (config .option .headless )
191- bidi = bool (config .option .bidi )
192- options = None
193-
194- if browser_path or browser_args :
195- if not options :
196- options = getattr (webdriver , f"{ driver_class } Options" )()
197- if driver_class == "WebKitGTK" :
198- options .overlay_scrollbars_enabled = False
199- if browser_path is not None :
200- options .binary_location = browser_path .strip ("'" )
201- if browser_args is not None :
202- for arg in browser_args .split ():
203- options .add_argument (arg )
204-
205- if headless :
206- if not options :
207- options = getattr (webdriver , f"{ driver_class } Options" )()
208-
209- if driver_class == "Chrome" or driver_class == "Edge" :
210- options .add_argument ("--headless=new" )
211- if driver_class == "Firefox" :
212- options .add_argument ("-headless" )
213-
214- if bidi :
215- if not options :
216- options = getattr (webdriver , f"{ driver_class } Options" )()
217-
218- options .web_socket_url = True
219- options .unhandled_prompt_behavior = "ignore"
220-
221- return options
222-
223-
224- def get_service (driver_class , executable ):
225- # Let the default behaviour be used if we don't set the driver executable
226- if not executable :
227- return None
228-
229- module = getattr (webdriver , driver_class .lower ())
230- service = module .service .Service (executable_path = executable )
231-
232- return service
233-
234235
235236@pytest .fixture (scope = "session" , autouse = True )
236237def stop_driver (request ):
@@ -348,8 +349,8 @@ def clean_service(request):
348349 driver_class = request .config .option .drivers [0 ].capitalize ()
349350 except AttributeError :
350351 raise Exception ("This test requires a --driver to be specified." )
351-
352- yield get_service ( driver_class , request . config . option . executable )
352+ selenium_driver = Driver ( driver_class , request )
353+ yield selenium_driver . service
353354
354355
355356@pytest .fixture (scope = "function" )
@@ -358,7 +359,6 @@ def clean_driver(request):
358359 driver_class = request .config .option .drivers [0 ].capitalize ()
359360 except AttributeError :
360361 raise Exception ("This test requires a --driver to be specified." )
361-
362362 driver_reference = getattr (webdriver , driver_class )
363363 yield driver_reference
364364
0 commit comments