Skip to content

Commit 90a11a5

Browse files
committed
Improve Python 3.11 compatibility
1 parent 310b848 commit 90a11a5

File tree

5 files changed

+252
-16
lines changed

5 files changed

+252
-16
lines changed

seleniumbase/core/encoded_images.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,40 @@ def get_side_by_side_png():
174174
"MAAAAASUVORK5CYII="
175175
)
176176
return SIDE_BY_SIDE_PNG
177+
178+
179+
def get_no_screenshot_png():
180+
NO_SCREENSHOT = (
181+
"iVBORw0KGgoAAAANSUhEUgAAAIwAAAA8CAAAAACRYQ2XAAAF10lEQVRo3u3WeVATVxwH8"
182+
"AiGIDEkEEjEoCCpXGVsFI3EE5DBitd4Y6nngK1a79pRUYeq1FIto9JWO9ra4mCZoiCKR4"
183+
"tarMihYAEJJIpXNgmQmJBAIHe+/QORadC2M870mNn9a/f3e2/eZ/f33ttHwX/oopAYEkN"
184+
"iSAyJITEkhsSQGBJDYkgMiSExJIbEkBgSQ2JIDIn59zA6RbP5H8Los7bkWQHAdDyt+SXt"
185+
"avfEDA1OOiZ/nbFUn33R1TdqNZidMYrRlKDbAKCfwqzr2+PaCCpPGDqQmtDwGpjGIKGmb"
186+
"/RO4jd2J4wyikJZ1AagPZ59r0+HZwnuaU81rdenUFJeo1SSYJG2b7TIfYPDGTN24BCP47"
187+
"0Ys/LRk97XqOWKVABwk/OmDECHrInoBGwarV2jtALWZkJh7G5pkBNqBwCLqsOhJuSdAGB"
188+
"rJQiVHZAEj9NqCaIdAGCUP5TpAIchx+O9FosTRhiUzhop7cHc/2AYx1d0qqfElaxxLQCg"
189+
"O/SpCiidNciHt6gazQsTs8cKCDzYyucMXVpqA6wXZvtxBOkEUBO140sBxz+xDlCnh3K5E"
190+
"ZntkISMOjyW4zf9hgOoT/HncGN/MJu38Vy8hNXOmGE171PWGrsx8gS3Obs3D2MesnVnm8"
191+
"JoS660WgDYgV9DGMtS51Mny+WRnjxufOvjONr01JXM4GtAHtdvzbZJ/ZN0KGOxeYt3jKc"
192+
"s6LCl0d/enRpFOwhJmPugWamzXSfI8GDSgPlp64d6f2fNm08dk/bYGRPYJB3BKkB7PLse"
193+
"B11W6IGr/nxxd9aSNYjiEbZ83+VmoCOp/14zNHNpZxRCSqJEa9lJ3aiD9RA1yfRkxOCLg"
194+
"HKm22mUe3seM6NmOL9BPT6wDqiKmN0hDaPuMUAxmXkV21w3G4BCL4ECl+mbnVeTUhh4H1"
195+
"+7x7QY4tli/QT2bQDGpe65z9Om0r0iH9d+nhMvQcofLgVQtqeSGM2rBBRRgTWwOWTh/NY"
196+
"c6mqz3YYztFWOcla0Bmifyi1XTxiwX2EyN9TbJSEhUsC62qNQNX5IFQBTIq0QRfSN9r4Y"
197+
"KXQLXfa1TWWLiYDRrQAcabSs3lmvFWd/OJISIalkxeq6I0TkWy1ALY/9bkpyynIeW7zPR"
198+
"ZicnJwyq9/0zgpmkgnomMG55cjyceUvTi8zQRIs0gC2TR4FDcOi1ACwg3LklRhU83kXEt"
199+
"gNTbxoDQD77ucYx0+ZjwDALp5Ey7nBmGHowYxUAVW+jIkx0dExcQubUl2Gx0RHR8fG7TR"
200+
"VMJeZgY4ZnFJYSlIFTCp3u1EaLNJ2Y2p4E9sAYDsl69UYWwZ1ushHrOSHEwCMK3rKtIly"
201+
"FABg30Q9WsUWPQPw21f3iEiBChAHhorb9Xrt3WpLhstHOr1eryxvQnkP5paxUQV1xX4er"
202+
"/r+C8yDEAEBwLyYdv7VGLROdXXn1pvmuZ0CIA0PqO3OnugX3wwAzQnuZ5RCn2tA10pmvi"
203+
"JSoAI08Yx8ALWjEw1FA2I1AI7xs3ox5XWj1poA0wJWyYMeTH77NM8CADWDwwgU0Tf2mcB"
204+
"jAqUAUOJP4dzDOa/Q7xt/eYe65fl++zTaZW5OcXH2HGqMzHGAFpdf8rGnSCaPFKgAfEuP"
205+
"Olt3fppbJnTz3NaV3s0cHFL3AuNbphzne+ThoxOBo568+DJncZIe+WNj8UzqJzYUMybmq"
206+
"Z0w44PvA4Bll/sQMYwZXGbQIMYSRY/59jQ6neVFZ8y9B2g3env7McZchzxKqALQmcH1Cv"
207+
"TxXqcFGhPofgEDwwuBCt9kM2CY41+G/ADGG8M9A/IgjZisBWxbvQrQtc+HFcT1TFYDD0V"
208+
"uAeV/xHRdKujeppW5eTrAcvPA+vSCtt61pD63d8WqzMs6AOj8+Wjm6UdA58UiIwBYbny+"
209+
"9fDVTgBoydm562Q9AFVumR2wluSp4LhzaPWarCoH9IVXzICj6rQMMF0/sGF/kR6A4252r"
210+
"uqvzjNWp2eH+U/+kbYXd3b7S9IWy98YgDx2/k8wvwNEPGrBGochUwAAAABJRU5ErkJggg"
211+
"=="
212+
)
213+
return NO_SCREENSHOT

seleniumbase/core/log_helper.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ def get_master_time():
7575

7676

7777
def get_browser_version(driver):
78+
if sys.version_info >= (3, 11) and hasattr(sb_config, "_browser_version"):
79+
return sb_config._browser_version
7880
driver_capabilities = driver.capabilities
7981
if "version" in driver_capabilities:
8082
browser_version = driver_capabilities["version"]
@@ -84,6 +86,8 @@ def get_browser_version(driver):
8486

8587

8688
def get_driver_name_and_version(driver, browser):
89+
if hasattr(sb_config, "_driver_name_version"):
90+
return sb_config._driver_name_version
8791
if driver.capabilities["browserName"].lower() == "chrome":
8892
cap_dict = driver.capabilities["chrome"]
8993
return ("chromedriver", cap_dict["chromedriverVersion"].split(" ")[0])
@@ -210,6 +214,10 @@ def log_test_failure_data(test, test_logpath, driver, browser, url=None):
210214
the_stacks = traceback.format_list(
211215
traceback.extract_tb(sys.last_traceback)
212216
)
217+
elif hasattr(sb_config, "_excinfo_tb"):
218+
the_stacks = traceback.format_list(
219+
traceback.extract_tb(sb_config._excinfo_tb)
220+
)
213221
else:
214222
message = None
215223
if hasattr(test, "is_behave") and test.is_behave:
@@ -226,7 +234,9 @@ def log_test_failure_data(test, test_logpath, driver, browser, url=None):
226234
if hasattr(sys, "last_value"):
227235
last_value = sys.last_value
228236
if last_value:
229-
data_to_save.append("Exception: " + str(last_value))
237+
data_to_save.append("Exception: %s" + str(last_value))
238+
elif hasattr(sb_config, "_excinfo_value"):
239+
data_to_save.append("Exception: %s" % sb_config._excinfo_value)
230240
else:
231241
data_to_save.append("Traceback: " + traceback_message)
232242
if hasattr(test, "is_nosetest") and test.is_nosetest:
@@ -241,6 +251,11 @@ def log_test_failure_data(test, test_logpath, driver, browser, url=None):
241251
sb_config._report_time = the_time
242252
sb_config._report_traceback = traceback_message
243253
sb_config._report_exception = exc_message
254+
try:
255+
if not os.path.exists(test_logpath):
256+
os.makedirs(test_logpath)
257+
except Exception:
258+
pass
244259
log_file = codecs.open(basic_file_path, "w+", "utf-8")
245260
log_file.writelines("\r\n".join(data_to_save))
246261
log_file.close()
@@ -323,6 +338,11 @@ def log_page_source(test_logpath, driver, source=None):
323338
"unresponsive, or closed prematurely!</h4>"
324339
)
325340
)
341+
try:
342+
if not os.path.exists(test_logpath):
343+
os.makedirs(test_logpath)
344+
except Exception:
345+
pass
326346
html_file_path = os.path.join(test_logpath, html_file_name)
327347
html_file = codecs.open(html_file_path, "w+", "utf-8")
328348
html_file.write(page_source)

seleniumbase/fixtures/base_case.py

Lines changed: 128 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11388,6 +11388,11 @@ def __assert_eq(self, *args, **kwargs):
1138811388

1138911389
raise VisualException(minified_exception)
1139011390

11391+
def _process_visual_baseline_logs(self):
11392+
if sys.version_info < (3, 11):
11393+
return
11394+
self.__process_visual_baseline_logs()
11395+
1139111396
def __process_visual_baseline_logs(self):
1139211397
"""Save copies of baseline PNGs in "./latest_logs" during failures.
1139311398
Also create a side_by_side.html file for visual comparisons."""
@@ -13033,6 +13038,31 @@ def setUp(self, masterqa_mode=False):
1303313038
self.data_path = os.path.join(self.log_path, self.__get_test_id())
1303413039
self.data_abspath = os.path.abspath(self.data_path)
1303513040

13041+
# Add _test_logpath value to sb_config
13042+
test_id = self.__get_test_id()
13043+
test_logpath = os.path.join(self.log_path, test_id)
13044+
sb_config._test_logpath = test_logpath
13045+
13046+
# Add _process_dashboard_entry method to sb_config
13047+
sb_config._process_dashboard_entry = self._process_dashboard_entry
13048+
13049+
# Add _add_pytest_html_extra method to sb_config
13050+
sb_config._add_pytest_html_extra = self._add_pytest_html_extra
13051+
13052+
# Add _process_visual_baseline_logs method to sb_config
13053+
sb_config._process_v_baseline_logs = self._process_visual_baseline_logs
13054+
13055+
# Add _log_fail_data method to sb_config
13056+
sb_config._log_fail_data = self._log_fail_data
13057+
13058+
# Reset the last_page_screenshot variables
13059+
sb_config._last_page_screenshot = None
13060+
sb_config._last_page_screenshot_png = None
13061+
13062+
# Indictate to pytest reports that SeleniumBase is being used
13063+
sb_config._sbase_detected = True
13064+
sb_config._only_unittest = False
13065+
1303613066
# Mobile Emulator device metrics: CSS Width, CSS Height, & Pixel-Ratio
1303713067
if self.device_metrics:
1303813068
metrics_string = self.device_metrics
@@ -13075,15 +13105,11 @@ def setUp(self, masterqa_mode=False):
1307513105
if self.dashboard:
1307613106
if self._multithreaded:
1307713107
with self.dash_lock:
13078-
sb_config._sbase_detected = True
13079-
sb_config._only_unittest = False
1308013108
if not self._dash_initialized:
1308113109
sb_config._dashboard_initialized = True
1308213110
self._dash_initialized = True
1308313111
self.__process_dashboard(False, init=True)
1308413112
else:
13085-
sb_config._sbase_detected = True
13086-
sb_config._only_unittest = False
1308713113
if not self._dash_initialized:
1308813114
sb_config._dashboard_initialized = True
1308913115
self._dash_initialized = True
@@ -13314,6 +13340,7 @@ def __set_last_page_source(self):
1331413340
self.__last_page_source = (
1331513341
constants.Warnings.PAGE_SOURCE_UNDEFINED
1331613342
)
13343+
sb_config._last_page_source = self.__last_page_source
1331713344

1331813345
def __get_exception_info(self):
1331913346
exc_message = None
@@ -13368,6 +13395,11 @@ def __insert_test_result(self, state, err):
1336813395
data_payload.message = "Skipped: (no reason given)"
1336913396
self.testcase_manager.update_testcase_data(data_payload)
1337013397

13398+
def _add_pytest_html_extra(self):
13399+
if sys.version_info < (3, 11):
13400+
return
13401+
self.__add_pytest_html_extra()
13402+
1337113403
def __add_pytest_html_extra(self):
1337213404
if not self.__added_pytest_html_extra:
1337313405
try:
@@ -13381,7 +13413,7 @@ def __add_pytest_html_extra(self):
1338113413
extra_url["name"] = "URL"
1338213414
extra_url["format"] = "url"
1338313415
extra_url["format_type"] = "url"
13384-
extra_url["content"] = self.get_current_url()
13416+
extra_url["content"] = self.__last_page_url
1338513417
extra_url["mime_type"] = None
1338613418
extra_url["extension"] = None
1338713419
extra_image = {}
@@ -13465,8 +13497,12 @@ def __has_exception(self):
1346513497
return True
1346613498
else:
1346713499
return False
13468-
elif python3 and hasattr(self, "_outcome"):
13469-
if hasattr(self._outcome, "errors") and self._outcome.errors:
13500+
elif (
13501+
python3
13502+
and hasattr(self, "_outcome")
13503+
and hasattr(self._outcome, "errors")
13504+
):
13505+
if self._outcome.errors:
1347013506
has_exception = True
1347113507
else:
1347213508
if python3:
@@ -13592,6 +13628,18 @@ def __create_log_path_as_needed(self, test_logpath):
1359213628
except Exception:
1359313629
pass # Only reachable during multi-threaded runs
1359413630

13631+
def _process_dashboard_entry(self, has_exception, init=False):
13632+
if self._multithreaded:
13633+
import fasteners
13634+
13635+
self.dash_lock = fasteners.InterProcessLock(
13636+
constants.Dashboard.LOCKFILE
13637+
)
13638+
with self.dash_lock:
13639+
self.__process_dashboard(has_exception, init)
13640+
else:
13641+
self.__process_dashboard(has_exception, init)
13642+
1359513643
def __process_dashboard(self, has_exception, init=False):
1359613644
"""SeleniumBase Dashboard Processing"""
1359713645
if self._multithreaded:
@@ -13995,6 +14043,66 @@ def save_teardown_screenshot(self):
1399514043
if self.is_pytest:
1399614044
self.__add_pytest_html_extra()
1399714045

14046+
def _log_fail_data(self):
14047+
if sys.version_info < (3, 11):
14048+
return
14049+
test_id = self.__get_test_id()
14050+
test_logpath = os.path.join(self.log_path, test_id)
14051+
log_helper.log_test_failure_data(
14052+
self,
14053+
test_logpath,
14054+
self.driver,
14055+
self.browser,
14056+
self.__last_page_url,
14057+
)
14058+
14059+
def _get_browser_version(self):
14060+
driver_capabilities = None
14061+
if hasattr(self, "driver") and hasattr(self.driver, "capabilities"):
14062+
driver_capabilities = self.driver.capabilities
14063+
elif hasattr(sb_config, "_browser_version"):
14064+
return sb_config._browser_version
14065+
else:
14066+
return "(Unknown Version)"
14067+
if "version" in driver_capabilities:
14068+
browser_version = driver_capabilities["version"]
14069+
else:
14070+
browser_version = driver_capabilities["browserVersion"]
14071+
return browser_version
14072+
14073+
def _get_driver_name_and_version(self):
14074+
if not hasattr(self.driver, "capabilities"):
14075+
if hasattr(sb_config, "_driver_name_version"):
14076+
return sb_config._driver_name_version
14077+
else:
14078+
return None
14079+
driver = self.driver
14080+
if driver.capabilities["browserName"].lower() == "chrome":
14081+
cap_dict = driver.capabilities["chrome"]
14082+
return (
14083+
"chromedriver", cap_dict["chromedriverVersion"].split(" ")[0]
14084+
)
14085+
elif driver.capabilities["browserName"].lower() == "msedge":
14086+
cap_dict = driver.capabilities["msedge"]
14087+
return (
14088+
"msedgedriver", cap_dict["msedgedriverVersion"].split(" ")[0]
14089+
)
14090+
elif driver.capabilities["browserName"].lower() == "opera":
14091+
cap_dict = driver.capabilities["opera"]
14092+
return (
14093+
"operadriver", cap_dict["operadriverVersion"].split(" ")[0]
14094+
)
14095+
elif driver.capabilities["browserName"].lower() == "firefox":
14096+
return (
14097+
"geckodriver", driver.capabilities["moz:geckodriverVersion"]
14098+
)
14099+
elif self.browser == "safari":
14100+
return ("safaridriver", self._get_browser_version())
14101+
elif self.browser == "ie":
14102+
return ("iedriver", self._get_browser_version())
14103+
else:
14104+
return None
14105+
1399814106
def tearDown(self):
1399914107
"""
1400014108
Be careful if a subclass of BaseCase overrides setUp()
@@ -14042,6 +14150,10 @@ def tearDown(self):
1404214150
# *** Start tearDown() officially ***
1404314151
self.__slow_mode_pause_if_active()
1404414152
has_exception = self.__has_exception()
14153+
sb_config._has_exception = has_exception
14154+
sb_config._browser_version = self._get_browser_version()
14155+
sb_config._driver_name_version = self._get_driver_name_and_version()
14156+
1404514157
if self.__overrided_default_timeouts:
1404614158
# Reset default timeouts in case there are more tests
1404714159
# These were changed in set_default_timeout()
@@ -14091,6 +14203,11 @@ def tearDown(self):
1409114203
)
1409214204
self.__add_pytest_html_extra()
1409314205
sb_config._has_logs = True
14206+
elif sys.version_info >= (3, 11) and not has_exception:
14207+
# Handle a bug on Python 3.11 where exceptions aren't seen
14208+
self.__set_last_page_screenshot()
14209+
self.__set_last_page_url()
14210+
self.__set_last_page_source()
1409414211
if self.with_testing_base and has_exception:
1409514212
test_logpath = os.path.join(self.log_path, test_id)
1409614213
self.__create_log_path_as_needed(test_logpath)
@@ -14303,8 +14420,10 @@ def tearDown(self):
1430314420
# (Nosetests / Behave / Pure Python) Close all open browser windows
1430414421
self.__quit_all_drivers()
1430514422
# Resume tearDown() for all test runners, (Pytest / Nosetests / Behave)
14306-
if has_exception and self.__visual_baseline_copies:
14307-
self.__process_visual_baseline_logs()
14423+
if self.__visual_baseline_copies:
14424+
sb_config._visual_baseline_copies = True
14425+
if has_exception:
14426+
self.__process_visual_baseline_logs()
1430814427
if deferred_exception:
1430914428
# User forgot to call "self.process_deferred_asserts()" in test
1431014429
raise deferred_exception

seleniumbase/plugins/base_plugin.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ def addSuccess(self, test, capt):
291291
)
292292
)
293293

294-
def add_fails_or_errors(self, test):
294+
def add_fails_or_errors(self, test, err):
295295
if self.report_on:
296296
self.duration = str(
297297
"%.2fs" % (float(time.time()) - float(self.start_time))
@@ -307,10 +307,36 @@ def add_fails_or_errors(self, test):
307307
test, self.test_count, self.duration
308308
)
309309
)
310+
if sys.version_info >= (3, 11):
311+
# Handle a bug on Python 3.11 where exceptions aren't seen
312+
sb_config._browser_version = None
313+
try:
314+
test._BaseCase__set_last_page_screenshot()
315+
test._BaseCase__set_last_page_url()
316+
test._BaseCase__set_last_page_source()
317+
sb_config._browser_version = test._get_browser_version()
318+
test._log_fail_data()
319+
except Exception:
320+
pass
321+
sb_config._excinfo_tb = err
322+
log_path = None
323+
if hasattr(sb_config, "_test_logpath"):
324+
log_path = sb_config._test_logpath
325+
if hasattr(sb_config, "_last_page_source"):
326+
source = sb_config._last_page_source
327+
if log_path and source:
328+
log_helper.log_page_source(log_path, None, source)
329+
last_page_screenshot_png = None
330+
if hasattr(sb_config, "_last_page_screenshot_png"):
331+
last_page_screenshot_png = sb_config._last_page_screenshot_png
332+
if log_path and last_page_screenshot_png:
333+
log_helper.log_screenshot(
334+
log_path, None, last_page_screenshot_png
335+
)
310336

311337
def addFailure(self, test, err, capt=None, tbinfo=None):
312338
# self.__log_all_options_if_none_specified(test)
313-
self.add_fails_or_errors(test)
339+
self.add_fails_or_errors(test, err)
314340

315341
def addError(self, test, err, capt=None):
316342
"""
@@ -338,7 +364,7 @@ def addError(self, test, err, capt=None):
338364
else:
339365
# self.__log_all_options_if_none_specified(test)
340366
pass
341-
self.add_fails_or_errors(test)
367+
self.add_fails_or_errors(test, err)
342368

343369
def handleError(self, test, err, capt=None):
344370
"""

0 commit comments

Comments
 (0)