@@ -80,6 +80,7 @@ def __init__(self, *args, **kwargs):
80
80
self .__device_width = None
81
81
self .__device_height = None
82
82
self .__device_pixel_ratio = None
83
+ self .__driver_browser_map = {}
83
84
# Requires self._* instead of self.__* for external class use
84
85
self ._language = "English"
85
86
self ._presentation_slides = {}
@@ -2134,8 +2135,10 @@ def get_new_driver(self, browser=None, headless=None, locale_code=None,
2134
2135
device_height = d_height ,
2135
2136
device_pixel_ratio = d_p_r )
2136
2137
self ._drivers_list .append (new_driver )
2138
+ self .__driver_browser_map [new_driver ] = browser_name
2137
2139
if switch_to :
2138
2140
self .driver = new_driver
2141
+ self .browser = browser_name
2139
2142
if self .headless :
2140
2143
# Make sure the invisible browser window is big enough
2141
2144
width = settings .HEADLESS_START_WIDTH
@@ -2209,11 +2212,15 @@ def switch_to_driver(self, driver):
2209
2212
""" Sets self.driver to the specified driver.
2210
2213
You may need this if using self.get_new_driver() in your code. """
2211
2214
self .driver = driver
2215
+ if self .driver in self .__driver_browser_map :
2216
+ self .browser = self .__driver_browser_map [self .driver ]
2212
2217
2213
2218
def switch_to_default_driver (self ):
2214
2219
""" Sets self.driver to the default/original driver. """
2215
2220
self .__check_scope ()
2216
2221
self .driver = self ._default_driver
2222
+ if self .driver in self .__driver_browser_map :
2223
+ self .browser = self .__driver_browser_map [self .driver ]
2217
2224
2218
2225
def save_screenshot (self , name , folder = None ):
2219
2226
""" The screenshot will be in PNG format. """
@@ -6497,7 +6504,7 @@ def check_window(self, name="default", level=0, baseline=False):
6497
6504
self.check_window(name="github_page", level=2)
6498
6505
self.check_window(name="wikipedia_page", level=3)
6499
6506
"""
6500
- self .__check_scope ()
6507
+ self .wait_for_ready_state_complete ()
6501
6508
if level == "0" :
6502
6509
level = 0
6503
6510
if level == "1" :
@@ -6510,11 +6517,10 @@ def check_window(self, name="default", level=0, baseline=False):
6510
6517
raise Exception ('Parameter "level" must be set to 0, 1, 2, or 3!' )
6511
6518
6512
6519
if self .demo_mode :
6513
- raise Exception (
6514
- "WARNING: Using Demo Mode will break layout tests "
6515
- "that use the check_window() method due to custom "
6516
- "HTML edits being made on the page!\n "
6517
- "Please rerun without using Demo Mode!" )
6520
+ message = (
6521
+ "WARNING: Using check_window() from Demo Mode may lead "
6522
+ "to unexpected results caused by Demo Mode HTML changes." )
6523
+ logging .info (message )
6518
6524
6519
6525
module = self .__class__ .__module__
6520
6526
if '.' in module and len (module .split ('.' )[- 1 ]) > 1 :
@@ -7263,15 +7269,20 @@ def setUp(self, masterqa_mode=False):
7263
7269
self .guest_mode = sb_config .guest_mode
7264
7270
self .devtools = sb_config .devtools
7265
7271
self .remote_debug = sb_config .remote_debug
7272
+ self ._multithreaded = sb_config ._multithreaded
7273
+ self ._reuse_session = sb_config .reuse_session
7274
+ self ._crumbs = sb_config .crumbs
7266
7275
self .dashboard = sb_config .dashboard
7267
7276
self ._dash_initialized = sb_config ._dashboard_initialized
7277
+ if self .dashboard and self ._multithreaded :
7278
+ import fasteners
7279
+ self .dash_lock = fasteners .InterProcessLock (
7280
+ constants .Dashboard .LOCKFILE )
7268
7281
self .swiftshader = sb_config .swiftshader
7269
7282
self .user_data_dir = sb_config .user_data_dir
7270
7283
self .extension_zip = sb_config .extension_zip
7271
7284
self .extension_dir = sb_config .extension_dir
7272
7285
self .maximize_option = sb_config .maximize_option
7273
- self ._reuse_session = sb_config .reuse_session
7274
- self ._crumbs = sb_config .crumbs
7275
7286
self .save_screenshot_after_test = sb_config .save_screenshot
7276
7287
self .visual_baseline = sb_config .visual_baseline
7277
7288
self .timeout_multiplier = sb_config .timeout_multiplier
@@ -7385,13 +7396,21 @@ def setUp(self, masterqa_mode=False):
7385
7396
7386
7397
# Dashboard pre-processing:
7387
7398
if self .dashboard :
7388
- sb_config ._sbase_detected = True
7389
- sb_config ._only_unittest = False
7390
- if not self ._dash_initialized :
7391
- sb_config ._dashboard_initialized = True
7399
+ if self ._multithreaded :
7400
+ with self .dash_lock :
7401
+ sb_config ._sbase_detected = True
7402
+ sb_config ._only_unittest = False
7403
+ if not self ._dash_initialized :
7404
+ sb_config ._dashboard_initialized = True
7405
+ self ._dash_initialized = True
7406
+ self .__process_dashboard (False , init = True )
7407
+ else :
7392
7408
sb_config ._sbase_detected = True
7393
- self ._dash_initialized = True
7394
- self .__process_dashboard (False , init = True )
7409
+ sb_config ._only_unittest = False
7410
+ if not self ._dash_initialized :
7411
+ sb_config ._dashboard_initialized = True
7412
+ self ._dash_initialized = True
7413
+ self .__process_dashboard (False , init = True )
7395
7414
7396
7415
has_url = False
7397
7416
if self ._reuse_session :
@@ -7690,6 +7709,23 @@ def __create_log_path_as_needed(self, test_logpath):
7690
7709
7691
7710
def __process_dashboard (self , has_exception , init = False ):
7692
7711
''' SeleniumBase Dashboard Processing '''
7712
+ existing_res = sb_config ._results # Used by multithreaded tests
7713
+ if self ._multithreaded :
7714
+ abs_path = os .path .abspath ('.' )
7715
+ dash_json_loc = constants .Dashboard .DASH_JSON
7716
+ dash_jsonpath = os .path .join (abs_path , dash_json_loc )
7717
+ if not init and os .path .exists (dash_jsonpath ):
7718
+ with open (dash_jsonpath , 'r' ) as f :
7719
+ dash_json = f .read ().strip ()
7720
+ dash_data , d_id , dash_runtimes , d_stats = json .loads (dash_json )
7721
+ num_passed , num_failed , num_skipped , num_untested = d_stats
7722
+ sb_config ._results = dash_data
7723
+ sb_config ._display_id = d_id
7724
+ sb_config ._duration = dash_runtimes
7725
+ sb_config .item_count_passed = num_passed
7726
+ sb_config .item_count_failed = num_failed
7727
+ sb_config .item_count_skipped = num_skipped
7728
+ sb_config .item_count_untested = num_untested
7693
7729
if len (sb_config ._extra_dash_entries ) > 0 :
7694
7730
# First take care of existing entries from non-SeleniumBase tests
7695
7731
for test_id in sb_config ._extra_dash_entries :
@@ -7735,6 +7771,11 @@ def __process_dashboard(self, has_exception, init=False):
7735
7771
sb_config .item_count_skipped += 1
7736
7772
sb_config .item_count_untested -= 1
7737
7773
sb_config ._results [test_id ] = "Skipped"
7774
+ elif self ._multithreaded and test_id in existing_res .keys () and (
7775
+ existing_res [test_id ] == "Skipped" ):
7776
+ sb_config .item_count_skipped += 1
7777
+ sb_config .item_count_untested -= 1
7778
+ sb_config ._results [test_id ] = "Skipped"
7738
7779
elif has_exception :
7739
7780
# pytest-rerunfailures may cause duplicate results
7740
7781
if test_id not in sb_config ._results .keys () or (
@@ -7772,6 +7813,14 @@ def __process_dashboard(self, has_exception, init=False):
7772
7813
if sb_config ._using_html_report :
7773
7814
# Add the pie chart to the pytest html report
7774
7815
sb_config ._saved_dashboard_pie = self .extract_chart ()
7816
+ if self ._multithreaded :
7817
+ abs_path = os .path .abspath ('.' )
7818
+ dash_pie = json .dumps (sb_config ._saved_dashboard_pie )
7819
+ dash_pie_loc = constants .Dashboard .DASH_PIE
7820
+ pie_path = os .path .join (abs_path , dash_pie_loc )
7821
+ pie_file = codecs .open (pie_path , "w+" , encoding = "utf-8" )
7822
+ pie_file .writelines (dash_pie )
7823
+ pie_file .close ()
7775
7824
head = (
7776
7825
'<head><meta charset="utf-8" />'
7777
7826
'<meta property="og:image" '
@@ -7881,6 +7930,17 @@ def __process_dashboard(self, has_exception, init=False):
7881
7930
out_file .writelines (the_html )
7882
7931
out_file .close ()
7883
7932
time .sleep (0.05 ) # Add time for dashboard server to process updates
7933
+ if self ._multithreaded :
7934
+ d_stats = (num_passed , num_failed , num_skipped , num_untested )
7935
+ _results = sb_config ._results
7936
+ _display_id = sb_config ._display_id
7937
+ _duration = sb_config ._duration
7938
+ dash_json = json .dumps ((_results , _display_id , _duration , d_stats ))
7939
+ dash_json_loc = constants .Dashboard .DASH_JSON
7940
+ dash_jsonpath = os .path .join (abs_path , dash_json_loc )
7941
+ dash_json_file = codecs .open (dash_jsonpath , "w+" , encoding = "utf-8" )
7942
+ dash_json_file .writelines (dash_json )
7943
+ dash_json_file .close ()
7884
7944
7885
7945
def has_exception (self ):
7886
7946
""" (This method should ONLY be used in custom tearDown() methods.)
@@ -8025,7 +8085,11 @@ def tearDown(self):
8025
8085
test_logpath , self .driver ,
8026
8086
self .__last_page_source )
8027
8087
if self .dashboard :
8028
- self .__process_dashboard (has_exception )
8088
+ if self ._multithreaded :
8089
+ with self .dash_lock :
8090
+ self .__process_dashboard (has_exception )
8091
+ else :
8092
+ self .__process_dashboard (has_exception )
8029
8093
# (Pytest) Finally close all open browser windows
8030
8094
self .__quit_all_drivers ()
8031
8095
if self .headless :
0 commit comments