Improve Python 3.11 compatibility

This commit is contained in:
Michael Mintz 2022-11-02 01:57:20 -04:00
parent 310b848842
commit 90a11a5519
5 changed files with 252 additions and 16 deletions

View File

@ -174,3 +174,40 @@ def get_side_by_side_png():
"MAAAAASUVORK5CYII="
)
return SIDE_BY_SIDE_PNG
def get_no_screenshot_png():
NO_SCREENSHOT = (
"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"
"=="
)
return NO_SCREENSHOT

View File

@ -75,6 +75,8 @@ def get_master_time():
def get_browser_version(driver):
if sys.version_info >= (3, 11) and hasattr(sb_config, "_browser_version"):
return sb_config._browser_version
driver_capabilities = driver.capabilities
if "version" in driver_capabilities:
browser_version = driver_capabilities["version"]
@ -84,6 +86,8 @@ def get_browser_version(driver):
def get_driver_name_and_version(driver, browser):
if hasattr(sb_config, "_driver_name_version"):
return sb_config._driver_name_version
if driver.capabilities["browserName"].lower() == "chrome":
cap_dict = driver.capabilities["chrome"]
return ("chromedriver", cap_dict["chromedriverVersion"].split(" ")[0])
@ -210,6 +214,10 @@ def log_test_failure_data(test, test_logpath, driver, browser, url=None):
the_stacks = traceback.format_list(
traceback.extract_tb(sys.last_traceback)
)
elif hasattr(sb_config, "_excinfo_tb"):
the_stacks = traceback.format_list(
traceback.extract_tb(sb_config._excinfo_tb)
)
else:
message = None
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):
if hasattr(sys, "last_value"):
last_value = sys.last_value
if last_value:
data_to_save.append("Exception: " + str(last_value))
data_to_save.append("Exception: %s" + str(last_value))
elif hasattr(sb_config, "_excinfo_value"):
data_to_save.append("Exception: %s" % sb_config._excinfo_value)
else:
data_to_save.append("Traceback: " + traceback_message)
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):
sb_config._report_time = the_time
sb_config._report_traceback = traceback_message
sb_config._report_exception = exc_message
try:
if not os.path.exists(test_logpath):
os.makedirs(test_logpath)
except Exception:
pass
log_file = codecs.open(basic_file_path, "w+", "utf-8")
log_file.writelines("\r\n".join(data_to_save))
log_file.close()
@ -323,6 +338,11 @@ def log_page_source(test_logpath, driver, source=None):
"unresponsive, or closed prematurely!</h4>"
)
)
try:
if not os.path.exists(test_logpath):
os.makedirs(test_logpath)
except Exception:
pass
html_file_path = os.path.join(test_logpath, html_file_name)
html_file = codecs.open(html_file_path, "w+", "utf-8")
html_file.write(page_source)

View File

@ -11388,6 +11388,11 @@ class BaseCase(unittest.TestCase):
raise VisualException(minified_exception)
def _process_visual_baseline_logs(self):
if sys.version_info < (3, 11):
return
self.__process_visual_baseline_logs()
def __process_visual_baseline_logs(self):
"""Save copies of baseline PNGs in "./latest_logs" during failures.
Also create a side_by_side.html file for visual comparisons."""
@ -13033,6 +13038,31 @@ class BaseCase(unittest.TestCase):
self.data_path = os.path.join(self.log_path, self.__get_test_id())
self.data_abspath = os.path.abspath(self.data_path)
# Add _test_logpath value to sb_config
test_id = self.__get_test_id()
test_logpath = os.path.join(self.log_path, test_id)
sb_config._test_logpath = test_logpath
# Add _process_dashboard_entry method to sb_config
sb_config._process_dashboard_entry = self._process_dashboard_entry
# Add _add_pytest_html_extra method to sb_config
sb_config._add_pytest_html_extra = self._add_pytest_html_extra
# Add _process_visual_baseline_logs method to sb_config
sb_config._process_v_baseline_logs = self._process_visual_baseline_logs
# Add _log_fail_data method to sb_config
sb_config._log_fail_data = self._log_fail_data
# Reset the last_page_screenshot variables
sb_config._last_page_screenshot = None
sb_config._last_page_screenshot_png = None
# Indictate to pytest reports that SeleniumBase is being used
sb_config._sbase_detected = True
sb_config._only_unittest = False
# Mobile Emulator device metrics: CSS Width, CSS Height, & Pixel-Ratio
if self.device_metrics:
metrics_string = self.device_metrics
@ -13075,15 +13105,11 @@ class BaseCase(unittest.TestCase):
if self.dashboard:
if self._multithreaded:
with self.dash_lock:
sb_config._sbase_detected = True
sb_config._only_unittest = False
if not self._dash_initialized:
sb_config._dashboard_initialized = True
self._dash_initialized = True
self.__process_dashboard(False, init=True)
else:
sb_config._sbase_detected = True
sb_config._only_unittest = False
if not self._dash_initialized:
sb_config._dashboard_initialized = True
self._dash_initialized = True
@ -13314,6 +13340,7 @@ class BaseCase(unittest.TestCase):
self.__last_page_source = (
constants.Warnings.PAGE_SOURCE_UNDEFINED
)
sb_config._last_page_source = self.__last_page_source
def __get_exception_info(self):
exc_message = None
@ -13368,6 +13395,11 @@ class BaseCase(unittest.TestCase):
data_payload.message = "Skipped: (no reason given)"
self.testcase_manager.update_testcase_data(data_payload)
def _add_pytest_html_extra(self):
if sys.version_info < (3, 11):
return
self.__add_pytest_html_extra()
def __add_pytest_html_extra(self):
if not self.__added_pytest_html_extra:
try:
@ -13381,7 +13413,7 @@ class BaseCase(unittest.TestCase):
extra_url["name"] = "URL"
extra_url["format"] = "url"
extra_url["format_type"] = "url"
extra_url["content"] = self.get_current_url()
extra_url["content"] = self.__last_page_url
extra_url["mime_type"] = None
extra_url["extension"] = None
extra_image = {}
@ -13465,8 +13497,12 @@ class BaseCase(unittest.TestCase):
return True
else:
return False
elif python3 and hasattr(self, "_outcome"):
if hasattr(self._outcome, "errors") and self._outcome.errors:
elif (
python3
and hasattr(self, "_outcome")
and hasattr(self._outcome, "errors")
):
if self._outcome.errors:
has_exception = True
else:
if python3:
@ -13592,6 +13628,18 @@ class BaseCase(unittest.TestCase):
except Exception:
pass # Only reachable during multi-threaded runs
def _process_dashboard_entry(self, has_exception, init=False):
if self._multithreaded:
import fasteners
self.dash_lock = fasteners.InterProcessLock(
constants.Dashboard.LOCKFILE
)
with self.dash_lock:
self.__process_dashboard(has_exception, init)
else:
self.__process_dashboard(has_exception, init)
def __process_dashboard(self, has_exception, init=False):
"""SeleniumBase Dashboard Processing"""
if self._multithreaded:
@ -13995,6 +14043,66 @@ class BaseCase(unittest.TestCase):
if self.is_pytest:
self.__add_pytest_html_extra()
def _log_fail_data(self):
if sys.version_info < (3, 11):
return
test_id = self.__get_test_id()
test_logpath = os.path.join(self.log_path, test_id)
log_helper.log_test_failure_data(
self,
test_logpath,
self.driver,
self.browser,
self.__last_page_url,
)
def _get_browser_version(self):
driver_capabilities = None
if hasattr(self, "driver") and hasattr(self.driver, "capabilities"):
driver_capabilities = self.driver.capabilities
elif hasattr(sb_config, "_browser_version"):
return sb_config._browser_version
else:
return "(Unknown Version)"
if "version" in driver_capabilities:
browser_version = driver_capabilities["version"]
else:
browser_version = driver_capabilities["browserVersion"]
return browser_version
def _get_driver_name_and_version(self):
if not hasattr(self.driver, "capabilities"):
if hasattr(sb_config, "_driver_name_version"):
return sb_config._driver_name_version
else:
return None
driver = self.driver
if driver.capabilities["browserName"].lower() == "chrome":
cap_dict = driver.capabilities["chrome"]
return (
"chromedriver", cap_dict["chromedriverVersion"].split(" ")[0]
)
elif driver.capabilities["browserName"].lower() == "msedge":
cap_dict = driver.capabilities["msedge"]
return (
"msedgedriver", cap_dict["msedgedriverVersion"].split(" ")[0]
)
elif driver.capabilities["browserName"].lower() == "opera":
cap_dict = driver.capabilities["opera"]
return (
"operadriver", cap_dict["operadriverVersion"].split(" ")[0]
)
elif driver.capabilities["browserName"].lower() == "firefox":
return (
"geckodriver", driver.capabilities["moz:geckodriverVersion"]
)
elif self.browser == "safari":
return ("safaridriver", self._get_browser_version())
elif self.browser == "ie":
return ("iedriver", self._get_browser_version())
else:
return None
def tearDown(self):
"""
Be careful if a subclass of BaseCase overrides setUp()
@ -14042,6 +14150,10 @@ class BaseCase(unittest.TestCase):
# *** Start tearDown() officially ***
self.__slow_mode_pause_if_active()
has_exception = self.__has_exception()
sb_config._has_exception = has_exception
sb_config._browser_version = self._get_browser_version()
sb_config._driver_name_version = self._get_driver_name_and_version()
if self.__overrided_default_timeouts:
# Reset default timeouts in case there are more tests
# These were changed in set_default_timeout()
@ -14091,6 +14203,11 @@ class BaseCase(unittest.TestCase):
)
self.__add_pytest_html_extra()
sb_config._has_logs = True
elif sys.version_info >= (3, 11) and not has_exception:
# Handle a bug on Python 3.11 where exceptions aren't seen
self.__set_last_page_screenshot()
self.__set_last_page_url()
self.__set_last_page_source()
if self.with_testing_base and has_exception:
test_logpath = os.path.join(self.log_path, test_id)
self.__create_log_path_as_needed(test_logpath)
@ -14303,8 +14420,10 @@ class BaseCase(unittest.TestCase):
# (Nosetests / Behave / Pure Python) Close all open browser windows
self.__quit_all_drivers()
# Resume tearDown() for all test runners, (Pytest / Nosetests / Behave)
if has_exception and self.__visual_baseline_copies:
self.__process_visual_baseline_logs()
if self.__visual_baseline_copies:
sb_config._visual_baseline_copies = True
if has_exception:
self.__process_visual_baseline_logs()
if deferred_exception:
# User forgot to call "self.process_deferred_asserts()" in test
raise deferred_exception

View File

@ -291,7 +291,7 @@ class Base(Plugin):
)
)
def add_fails_or_errors(self, test):
def add_fails_or_errors(self, test, err):
if self.report_on:
self.duration = str(
"%.2fs" % (float(time.time()) - float(self.start_time))
@ -307,10 +307,36 @@ class Base(Plugin):
test, self.test_count, self.duration
)
)
if sys.version_info >= (3, 11):
# Handle a bug on Python 3.11 where exceptions aren't seen
sb_config._browser_version = None
try:
test._BaseCase__set_last_page_screenshot()
test._BaseCase__set_last_page_url()
test._BaseCase__set_last_page_source()
sb_config._browser_version = test._get_browser_version()
test._log_fail_data()
except Exception:
pass
sb_config._excinfo_tb = err
log_path = None
if hasattr(sb_config, "_test_logpath"):
log_path = sb_config._test_logpath
if hasattr(sb_config, "_last_page_source"):
source = sb_config._last_page_source
if log_path and source:
log_helper.log_page_source(log_path, None, source)
last_page_screenshot_png = None
if hasattr(sb_config, "_last_page_screenshot_png"):
last_page_screenshot_png = sb_config._last_page_screenshot_png
if log_path and last_page_screenshot_png:
log_helper.log_screenshot(
log_path, None, last_page_screenshot_png
)
def addFailure(self, test, err, capt=None, tbinfo=None):
# self.__log_all_options_if_none_specified(test)
self.add_fails_or_errors(test)
self.add_fails_or_errors(test, err)
def addError(self, test, err, capt=None):
"""
@ -338,7 +364,7 @@ class Base(Plugin):
else:
# self.__log_all_options_if_none_specified(test)
pass
self.add_fails_or_errors(test)
self.add_fails_or_errors(test, err)
def handleError(self, test, err, capt=None):
"""

View File

@ -10,6 +10,7 @@ import sys
import time
from seleniumbase import config as sb_config
from seleniumbase.config import settings
from seleniumbase.core import log_helper
from seleniumbase.fixtures import constants
is_windows = False
@ -898,7 +899,7 @@ def pytest_addoption(parser):
action="store_true",
dest="use_auto_ext",
default=False,
help="""Using this enables Chrome's Automation Extension.
help="""(DEPRECATED) - Enable the automation extension.
It's not required, but some commands & advanced
features may need it.""",
)
@ -1682,7 +1683,6 @@ def pytest_collection_finish(session):
if "--co" in sys_argv or "--collect-only" in sys_argv:
return
if len(session.items) > 0 and not sb_config._multithreaded:
from seleniumbase.core import log_helper
from seleniumbase.core import download_helper
from seleniumbase.core import proxy_helper
@ -1829,7 +1829,6 @@ def pytest_terminal_summary(terminalreporter):
def _perform_pytest_unconfigure_():
from seleniumbase.core import log_helper
from seleniumbase.core import proxy_helper
proxy_helper.remove_proxy_zip_if_present()
@ -2106,6 +2105,41 @@ def pytest_runtest_makereport(item, call):
sb_config._d_t_log_path[test_id] = ""
if test_id not in sb_config._extra_dash_entries:
sb_config._extra_dash_entries.append(test_id)
elif (
sb_config._sbase_detected
and sys.version_info >= (3, 11)
and (report.outcome == "failed" or "AssertionError" in str(call))
and not sb_config._has_exception
):
# Handle a bug on Python 3.11 where exceptions aren't seen
log_path = ""
if hasattr(sb_config, "_test_logpath"):
log_path = sb_config._test_logpath
if sb_config.dashboard:
sb_config._process_dashboard_entry(True)
if hasattr(sb_config, "_add_pytest_html_extra"):
sb_config._add_pytest_html_extra()
if hasattr(sb_config, "_visual_baseline_copies"):
sb_config._process_v_baseline_logs()
sb_config._excinfo_value = call.excinfo.value
sb_config._excinfo_tb = call.excinfo.tb
if "pytest_plugin.BaseClass.base_method" not in log_path:
source = None
if hasattr(sb_config, "_last_page_source"):
source = sb_config._last_page_source
if log_path and source:
log_helper.log_page_source(log_path, None, source)
last_page_screenshot_png = None
if hasattr(sb_config, "_last_page_screenshot_png"):
last_page_screenshot_png = (
sb_config._last_page_screenshot_png
)
if log_path and last_page_screenshot_png:
log_helper.log_screenshot(
log_path, None, last_page_screenshot_png
)
if log_path:
sb_config._log_fail_data()
try:
extra_report = None
if hasattr(item, "_testcase"):