@@ -67,6 +67,10 @@ def __init__(self, *args, **kwargs):
67
67
self .driver = None
68
68
self .environment = None
69
69
self .env = None # Add a shortened version of self.environment
70
+ self .__called_setup = False
71
+ self .__called_teardown = False
72
+ self .__start_time_ms = None
73
+ self .__passed_then_skipped = False
70
74
self .__last_url_of_deferred_assert = "data:,"
71
75
self .__last_page_load_url = "data:,"
72
76
self .__last_page_screenshot = None
@@ -3748,6 +3752,12 @@ def skip(self, reason=""):
3748
3752
self .__check_scope ()
3749
3753
if self .dashboard or (self .is_pytest and self .with_db_reporting ):
3750
3754
test_id = self .__get_test_id_2 ()
3755
+ if (
3756
+ test_id in sb_config ._results .keys ()
3757
+ and sb_config ._results [test_id ] == "Passed"
3758
+ ):
3759
+ # Duplicate tearDown() called where test already passed
3760
+ self .__passed_then_skipped = True
3751
3761
sb_config ._results [test_id ] = "Skipped"
3752
3762
self .__skip_reason = reason
3753
3763
elif reason and not self .is_pytest :
@@ -7336,6 +7346,11 @@ def setUp(self, masterqa_mode=False):
7336
7346
You'll need to add the following line to the subclass setUp() method:
7337
7347
super(SubClassOfBaseCase, self).setUp()
7338
7348
"""
7349
+ if not hasattr (self , "_using_sb_fixture" ) and self .__called_setup :
7350
+ # This test already called setUp()
7351
+ return
7352
+ self .__called_setup = True
7353
+ self .__called_teardown = False
7339
7354
self .masterqa_mode = masterqa_mode
7340
7355
self .is_pytest = None
7341
7356
try :
@@ -7642,6 +7657,9 @@ def setUp(self, masterqa_mode=False):
7642
7657
# Although the pytest clock starts before setUp() begins,
7643
7658
# the time-limit clock starts at the end of the setUp() method.
7644
7659
sb_config .start_time_ms = int (time .time () * 1000.0 )
7660
+ if not self .__start_time_ms :
7661
+ # Call this once in case of multiple setUp() calls in the same test
7662
+ self .__start_time_ms = sb_config .start_time_ms
7645
7663
7646
7664
def __set_last_page_screenshot (self ):
7647
7665
""" self.__last_page_screenshot is only for pytest html report logs
@@ -7906,7 +7924,7 @@ def __process_dashboard(self, has_exception, init=False):
7906
7924
test_id = self .__get_test_id_2 ()
7907
7925
dud = "seleniumbase/plugins/pytest_plugin.py::BaseClass::base_method"
7908
7926
if not init :
7909
- duration_ms = int (time .time () * 1000 ) - sb_config . start_time_ms
7927
+ duration_ms = int (time .time () * 1000 ) - self . __start_time_ms
7910
7928
duration = float (duration_ms ) / 1000.0
7911
7929
sb_config ._duration [test_id ] = duration
7912
7930
if test_id not in sb_config ._display_id .keys ():
@@ -7927,27 +7945,53 @@ def __process_dashboard(self, has_exception, init=False):
7927
7945
sb_config ._results .pop (alt_test_id )
7928
7946
if test_id in sb_config ._results .keys () and (
7929
7947
sb_config ._results [test_id ] == "Skipped" ):
7948
+ if self .__passed_then_skipped :
7949
+ # Multiple calls of setUp() and tearDown() in the same test
7950
+ sb_config .item_count_passed -= 1
7951
+ sb_config .item_count_untested += 1
7952
+ self .__passed_then_skipped = False
7953
+ sb_config ._results [test_id ] = "Skipped"
7930
7954
sb_config .item_count_skipped += 1
7931
7955
sb_config .item_count_untested -= 1
7932
- sb_config ._results [test_id ] = "Skipped"
7933
7956
elif self ._multithreaded and test_id in existing_res .keys () and (
7934
7957
existing_res [test_id ] == "Skipped" ):
7958
+ sb_config ._results [test_id ] = "Skipped"
7935
7959
sb_config .item_count_skipped += 1
7936
7960
sb_config .item_count_untested -= 1
7937
- sb_config ._results [test_id ] = "Skipped"
7938
7961
elif has_exception :
7939
- # pytest-rerunfailures may cause duplicate results
7940
- if test_id not in sb_config ._results .keys () or (
7941
- (not sb_config ._results [test_id ] == "Failed" )):
7962
+ if test_id not in sb_config ._results .keys ():
7942
7963
sb_config ._results [test_id ] = "Failed"
7943
7964
sb_config .item_count_failed += 1
7944
7965
sb_config .item_count_untested -= 1
7966
+ elif not sb_config ._results [test_id ] == "Failed" :
7967
+ # tearDown() was called more than once in the test
7968
+ if sb_config ._results [test_id ] == "Passed" :
7969
+ # Passed earlier, but last run failed
7970
+ sb_config ._results [test_id ] = "Failed"
7971
+ sb_config .item_count_failed += 1
7972
+ sb_config .item_count_passed -= 1
7973
+ else :
7974
+ sb_config ._results [test_id ] = "Failed"
7975
+ sb_config .item_count_failed += 1
7976
+ sb_config .item_count_untested -= 1
7977
+ else :
7978
+ # pytest-rerunfailures caused a duplicate failure
7979
+ sb_config ._results [test_id ] = "Failed"
7945
7980
else :
7946
- if test_id in sb_config ._results .keys () and (
7947
- sb_config ._results [test_id ] == "Failed" ):
7948
- # Possibly pytest-rerunfailures reran the test
7981
+ if (
7982
+ test_id in sb_config ._results .keys ()
7983
+ and sb_config ._results [test_id ] == "Failed"
7984
+ ):
7985
+ # pytest-rerunfailures reran a test that failed
7949
7986
sb_config .item_count_failed -= 1
7950
7987
sb_config .item_count_untested += 1
7988
+ elif (
7989
+ test_id in sb_config ._results .keys ()
7990
+ and sb_config ._results [test_id ] == "Passed"
7991
+ ):
7992
+ # tearDown() was called more than once in the test
7993
+ sb_config .item_count_passed -= 1
7994
+ sb_config .item_count_untested += 1
7951
7995
sb_config ._results [test_id ] = "Passed"
7952
7996
sb_config .item_count_passed += 1
7953
7997
sb_config .item_count_untested -= 1
@@ -8143,17 +8187,23 @@ def tearDown(self):
8143
8187
You'll need to add the following line to the subclass's tearDown():
8144
8188
super(SubClassOfBaseCase, self).tearDown()
8145
8189
"""
8190
+ if not hasattr (self , "_using_sb_fixture" ) and self .__called_teardown :
8191
+ # This test already called tearDown()
8192
+ return
8193
+ self .__called_teardown = True
8194
+ self .__called_setup = False
8146
8195
try :
8147
8196
is_pytest = self .is_pytest # This fails if overriding setUp()
8148
8197
if is_pytest :
8149
8198
with_selenium = self .with_selenium
8150
8199
except Exception :
8151
- sub_class_name = str (
8152
- self .__class__ .__bases__ [0 ]).split ('.' )[- 1 ].split ("'" )[0 ]
8153
- sub_file_name = str (self .__class__ .__bases__ [0 ]).split ('.' )[- 2 ]
8200
+ sub_class_name = (
8201
+ str (self .__class__ .__bases__ [0 ]).split ("." )[- 1 ].split ("'" )[0 ]
8202
+ )
8203
+ sub_file_name = str (self .__class__ .__bases__ [0 ]).split ("." )[- 2 ]
8154
8204
sub_file_name = sub_file_name + ".py"
8155
- class_name = str (self .__class__ ).split ('.' )[- 1 ].split ("'" )[0 ]
8156
- file_name = str (self .__class__ ).split ('.' )[- 2 ] + ".py"
8205
+ class_name = str (self .__class__ ).split ("." )[- 1 ].split ("'" )[0 ]
8206
+ file_name = str (self .__class__ ).split ("." )[- 2 ] + ".py"
8157
8207
class_name_used = sub_class_name
8158
8208
file_name_used = sub_file_name
8159
8209
if sub_class_name == "BaseCase" :
@@ -8187,7 +8237,8 @@ def tearDown(self):
8187
8237
print (
8188
8238
"\n When using self.deferred_assert_*() methods in your tests, "
8189
8239
"remember to call self.process_deferred_asserts() afterwards. "
8190
- "Now calling in tearDown()...\n Failures Detected:" )
8240
+ "Now calling in tearDown()...\n Failures Detected:"
8241
+ )
8191
8242
if not has_exception :
8192
8243
self .process_deferred_asserts ()
8193
8244
else :
@@ -8200,8 +8251,11 @@ def tearDown(self):
8200
8251
if has_exception :
8201
8252
self .__add_pytest_html_extra ()
8202
8253
sb_config ._has_exception = True
8203
- if self .with_testing_base and not has_exception and (
8204
- self .save_screenshot_after_test ):
8254
+ if (
8255
+ self .with_testing_base
8256
+ and not has_exception
8257
+ and self .save_screenshot_after_test
8258
+ ):
8205
8259
test_logpath = self .log_path + "/" + test_id
8206
8260
self .__create_log_path_as_needed (test_logpath )
8207
8261
if not self .__last_page_screenshot_png :
@@ -8211,7 +8265,8 @@ def tearDown(self):
8211
8265
log_helper .log_screenshot (
8212
8266
test_logpath ,
8213
8267
self .driver ,
8214
- self .__last_page_screenshot_png )
8268
+ self .__last_page_screenshot_png
8269
+ )
8215
8270
self .__add_pytest_html_extra ()
8216
8271
if self .with_testing_base and has_exception :
8217
8272
test_logpath = self .log_path + "/" + test_id
@@ -8227,12 +8282,17 @@ def tearDown(self):
8227
8282
log_helper .log_screenshot (
8228
8283
test_logpath ,
8229
8284
self .driver ,
8230
- self .__last_page_screenshot_png )
8285
+ self .__last_page_screenshot_png
8286
+ )
8231
8287
log_helper .log_test_failure_data (
8232
- self , test_logpath , self .driver , self .browser ,
8233
- self .__last_page_url )
8288
+ self , test_logpath ,
8289
+ self .driver ,
8290
+ self .browser ,
8291
+ self .__last_page_url
8292
+ )
8234
8293
log_helper .log_page_source (
8235
- test_logpath , self .driver , self .__last_page_source )
8294
+ test_logpath , self .driver , self .__last_page_source
8295
+ )
8236
8296
else :
8237
8297
if self .with_screen_shots :
8238
8298
if not self .__last_page_screenshot_png :
@@ -8242,15 +8302,22 @@ def tearDown(self):
8242
8302
log_helper .log_screenshot (
8243
8303
test_logpath ,
8244
8304
self .driver ,
8245
- self .__last_page_screenshot_png )
8305
+ self .__last_page_screenshot_png
8306
+ )
8246
8307
if self .with_basic_test_info :
8247
8308
log_helper .log_test_failure_data (
8248
- self , test_logpath , self .driver , self .browser ,
8249
- self .__last_page_url )
8309
+ self ,
8310
+ test_logpath ,
8311
+ self .driver ,
8312
+ self .browser ,
8313
+ self .__last_page_url
8314
+ )
8250
8315
if self .with_page_source :
8251
8316
log_helper .log_page_source (
8252
- test_logpath , self .driver ,
8253
- self .__last_page_source )
8317
+ test_logpath ,
8318
+ self .driver ,
8319
+ self .__last_page_source
8320
+ )
8254
8321
if self .dashboard :
8255
8322
if self ._multithreaded :
8256
8323
with self .dash_lock :
@@ -8274,15 +8341,19 @@ def tearDown(self):
8274
8341
else :
8275
8342
test_id = self .__get_test_id_2 ()
8276
8343
if test_id in sb_config ._results .keys () and (
8277
- sb_config ._results [test_id ] == "Skipped" ):
8344
+ sb_config ._results [test_id ] == "Skipped"
8345
+ ):
8278
8346
self .__insert_test_result (
8279
- constants .State .SKIPPED , False )
8347
+ constants .State .SKIPPED , False
8348
+ )
8280
8349
else :
8281
8350
self .__insert_test_result (
8282
- constants .State .PASSED , False )
8351
+ constants .State .PASSED , False
8352
+ )
8283
8353
runtime = int (time .time () * 1000 ) - self .execution_start_time
8284
8354
self .testcase_manager .update_execution_data (
8285
- self .execution_guid , runtime )
8355
+ self .execution_guid , runtime
8356
+ )
8286
8357
if self .with_s3_logging and has_exception :
8287
8358
""" If enabled, upload logs to S3 during test exceptions. """
8288
8359
import uuid
@@ -8292,22 +8363,28 @@ def tearDown(self):
8292
8363
path = "%s/%s" % (self .log_path , test_id )
8293
8364
uploaded_files = []
8294
8365
for logfile in os .listdir (path ):
8295
- logfile_name = "%s/%s/%s" % (guid ,
8296
- test_id ,
8297
- logfile .split (path )[- 1 ])
8298
- s3_bucket .upload_file (logfile_name ,
8299
- "%s/%s" % (path , logfile ))
8366
+ logfile_name = "%s/%s/%s" % (
8367
+ guid ,
8368
+ test_id ,
8369
+ logfile .split (path )[- 1 ]
8370
+ )
8371
+ s3_bucket .upload_file (
8372
+ logfile_name , "%s/%s" % (path , logfile )
8373
+ )
8300
8374
uploaded_files .append (logfile_name )
8301
8375
s3_bucket .save_uploaded_file_names (uploaded_files )
8302
8376
index_file = s3_bucket .upload_index_file (test_id , guid )
8303
8377
print ("\n \n *** Log files uploaded: ***\n %s\n " % index_file )
8304
8378
logging .info (
8305
- "\n \n *** Log files uploaded: ***\n %s\n " % index_file )
8379
+ "\n \n *** Log files uploaded: ***\n %s\n " % index_file
8380
+ )
8306
8381
if self .with_db_reporting :
8307
8382
from seleniumbase .core .testcase_manager import (
8308
- TestcaseDataPayload )
8383
+ TestcaseDataPayload
8384
+ )
8309
8385
from seleniumbase .core .testcase_manager import (
8310
- TestcaseManager )
8386
+ TestcaseManager
8387
+ )
8311
8388
self .testcase_manager = TestcaseManager (self .database_env )
8312
8389
data_payload = TestcaseDataPayload ()
8313
8390
data_payload .guid = self .testcase_guid
@@ -8320,8 +8397,12 @@ def tearDown(self):
8320
8397
test_logpath = self .log_path + "/" + test_id
8321
8398
self .__create_log_path_as_needed (test_logpath )
8322
8399
log_helper .log_test_failure_data (
8323
- self , test_logpath , self .driver , self .browser ,
8324
- self .__last_page_url )
8400
+ self ,
8401
+ test_logpath ,
8402
+ self .driver ,
8403
+ self .browser ,
8404
+ self .__last_page_url
8405
+ )
8325
8406
if len (self ._drivers_list ) > 0 :
8326
8407
if not self .__last_page_screenshot_png :
8327
8408
self .__set_last_page_screenshot ()
@@ -8330,9 +8411,11 @@ def tearDown(self):
8330
8411
log_helper .log_screenshot (
8331
8412
test_logpath ,
8332
8413
self .driver ,
8333
- self .__last_page_screenshot_png )
8414
+ self .__last_page_screenshot_png
8415
+ )
8334
8416
log_helper .log_page_source (
8335
- test_logpath , self .driver , self .__last_page_source )
8417
+ test_logpath , self .driver , self .__last_page_source
8418
+ )
8336
8419
elif self .save_screenshot_after_test :
8337
8420
test_id = self .__get_test_id ()
8338
8421
test_logpath = self .log_path + "/" + test_id
@@ -8342,9 +8425,8 @@ def tearDown(self):
8342
8425
self .__set_last_page_url ()
8343
8426
self .__set_last_page_source ()
8344
8427
log_helper .log_screenshot (
8345
- test_logpath ,
8346
- self .driver ,
8347
- self .__last_page_screenshot_png )
8428
+ test_logpath , self .driver , self .__last_page_screenshot_png
8429
+ )
8348
8430
if self .report_on :
8349
8431
self ._last_page_screenshot = self .__last_page_screenshot_png
8350
8432
try :
0 commit comments