seleniumbase 4.44.2__py3-none-any.whl → 4.45.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. seleniumbase/__version__.py +1 -1
  2. seleniumbase/behave/behave_sb.py +14 -0
  3. seleniumbase/console_scripts/run.py +1 -0
  4. seleniumbase/console_scripts/sb_install.py +142 -11
  5. seleniumbase/console_scripts/sb_mkdir.py +76 -0
  6. seleniumbase/console_scripts/sb_mkrec.py +25 -0
  7. seleniumbase/console_scripts/sb_recorder.py +40 -3
  8. seleniumbase/core/browser_launcher.py +279 -117
  9. seleniumbase/core/detect_b_ver.py +8 -6
  10. seleniumbase/core/log_helper.py +11 -16
  11. seleniumbase/core/mysql.py +1 -1
  12. seleniumbase/core/report_helper.py +3 -7
  13. seleniumbase/core/sb_cdp.py +275 -78
  14. seleniumbase/core/sb_driver.py +36 -5
  15. seleniumbase/core/session_helper.py +2 -4
  16. seleniumbase/drivers/chromium_drivers/__init__.py +0 -0
  17. seleniumbase/fixtures/base_case.py +251 -201
  18. seleniumbase/fixtures/constants.py +1 -0
  19. seleniumbase/fixtures/js_utils.py +52 -14
  20. seleniumbase/fixtures/page_actions.py +18 -7
  21. seleniumbase/fixtures/page_utils.py +4 -2
  22. seleniumbase/fixtures/shared_utils.py +2 -4
  23. seleniumbase/masterqa/master_qa.py +16 -2
  24. seleniumbase/plugins/base_plugin.py +8 -0
  25. seleniumbase/plugins/driver_manager.py +15 -5
  26. seleniumbase/plugins/pytest_plugin.py +43 -57
  27. seleniumbase/plugins/sb_manager.py +23 -19
  28. seleniumbase/plugins/selenium_plugin.py +20 -13
  29. seleniumbase/undetected/__init__.py +11 -10
  30. seleniumbase/undetected/cdp.py +1 -12
  31. seleniumbase/undetected/cdp_driver/browser.py +330 -128
  32. seleniumbase/undetected/cdp_driver/cdp_util.py +48 -14
  33. seleniumbase/undetected/cdp_driver/config.py +78 -11
  34. seleniumbase/undetected/cdp_driver/connection.py +15 -43
  35. seleniumbase/undetected/cdp_driver/element.py +37 -22
  36. seleniumbase/undetected/cdp_driver/tab.py +414 -39
  37. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/METADATA +140 -152
  38. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/RECORD +42 -41
  39. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/licenses/LICENSE +1 -1
  40. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/WHEEL +0 -0
  41. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/entry_points.txt +0 -0
  42. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/top_level.txt +0 -0
@@ -221,11 +221,28 @@ class BaseCase(unittest.TestCase):
221
221
  [sys.executable, "-m", "pytest", file, "-s", *all_args]
222
222
  )
223
223
 
224
- def open(self, url):
224
+ def open(self, url, **kwargs):
225
225
  """Navigates the current browser window to the specified page."""
226
226
  self.__check_scope()
227
227
  if self.__is_cdp_swap_needed():
228
- self.cdp.open(url)
228
+ self.cdp.open(url, **kwargs)
229
+ return
230
+ elif (
231
+ getattr(self.driver, "_is_using_uc", None)
232
+ # and getattr(self.driver, "_is_using_auth", None)
233
+ and not getattr(self.driver, "_is_using_cdp", None)
234
+ ):
235
+ # Auth in UC Mode requires CDP Mode
236
+ # (and now we're always forcing it)
237
+ logging.info("open() in UC Mode now always activates CDP Mode.")
238
+ self.activate_cdp_mode(url, **kwargs)
239
+ return
240
+ elif (
241
+ getattr(self.driver, "_is_using_uc", None)
242
+ and getattr(self.driver, "_is_using_cdp", None)
243
+ ):
244
+ self.disconnect()
245
+ self.cdp.open(url, **kwargs)
229
246
  return
230
247
  self._check_browser()
231
248
  if self.__needs_minimum_wait():
@@ -1293,8 +1310,11 @@ class BaseCase(unittest.TestCase):
1293
1310
  self.__check_scope()
1294
1311
  return self.execute_script("return window.location.origin;")
1295
1312
 
1296
- def get_page_source(self):
1297
- if self.__is_cdp_swap_needed():
1313
+ def get_html(self, *args, **kwargs):
1314
+ return self.get_page_source(*args, **kwargs)
1315
+
1316
+ def get_page_source(self, *args, **kwargs):
1317
+ if self.__is_cdp_swap_needed(*args, **kwargs):
1298
1318
  return self.cdp.get_page_source()
1299
1319
  self.wait_for_ready_state_complete()
1300
1320
  if self.__needs_minimum_wait:
@@ -1333,7 +1353,7 @@ class BaseCase(unittest.TestCase):
1333
1353
  if self.__is_cdp_swap_needed():
1334
1354
  self.cdp.go_back()
1335
1355
  return
1336
- if hasattr(self, "recorder_mode") and self.recorder_mode:
1356
+ if getattr(self, "recorder_mode", None):
1337
1357
  self.save_recorded_actions()
1338
1358
  pre_action_url = None
1339
1359
  with suppress(Exception):
@@ -1361,7 +1381,7 @@ class BaseCase(unittest.TestCase):
1361
1381
  if self.__is_cdp_swap_needed():
1362
1382
  self.cdp.go_forward()
1363
1383
  return
1364
- if hasattr(self, "recorder_mode") and self.recorder_mode:
1384
+ if getattr(self, "recorder_mode", None):
1365
1385
  self.save_recorded_actions()
1366
1386
  self.__last_page_load_url = None
1367
1387
  self.driver.forward()
@@ -1617,14 +1637,14 @@ class BaseCase(unittest.TestCase):
1617
1637
  def click_link_text(self, link_text, timeout=None):
1618
1638
  """This method clicks link text on a page."""
1619
1639
  self.__check_scope()
1620
- if self.__is_cdp_swap_needed():
1621
- self.cdp.find_element(link_text, timeout=timeout).click()
1622
- return
1623
- self.__skip_if_esc()
1624
1640
  if not timeout:
1625
1641
  timeout = settings.SMALL_TIMEOUT
1626
1642
  if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
1627
1643
  timeout = self.__get_new_timeout(timeout)
1644
+ if self.__is_cdp_swap_needed():
1645
+ self.cdp.find_element(link_text, timeout=timeout).click()
1646
+ return
1647
+ self.__skip_if_esc()
1628
1648
  link_text = self.__get_type_checked_text(link_text)
1629
1649
  if self.__is_cdp_swap_needed():
1630
1650
  self.cdp.click_link(link_text)
@@ -2697,7 +2717,7 @@ class BaseCase(unittest.TestCase):
2697
2717
  original_by = by
2698
2718
  selector, by = self.__recalculate_selector(selector, by)
2699
2719
  if self.__is_cdp_swap_needed():
2700
- self.cdp.gui_hover_element(selector)
2720
+ self.cdp.hover_element(selector)
2701
2721
  return
2702
2722
  self.wait_for_element_visible(
2703
2723
  original_selector, by=original_by, timeout=timeout
@@ -2742,7 +2762,7 @@ class BaseCase(unittest.TestCase):
2742
2762
  click_selector, click_by
2743
2763
  )
2744
2764
  if self.__is_cdp_swap_needed():
2745
- self.cdp.gui_hover_and_click(hover_selector, click_selector)
2765
+ self.cdp.hover_and_click(hover_selector, click_selector)
2746
2766
  return
2747
2767
  dropdown_element = self.wait_for_element_visible(
2748
2768
  original_selector, by=original_by, timeout=timeout
@@ -3426,6 +3446,46 @@ class BaseCase(unittest.TestCase):
3426
3446
  file_path = os.path.join(abs_path, html_file)
3427
3447
  self.open("file://" + file_path)
3428
3448
 
3449
+ def evaluate(self, expression):
3450
+ """Run a JavaScript expression and return the result."""
3451
+ self.__check_scope()
3452
+ if self.__is_cdp_swap_needed():
3453
+ return self.cdp.evaluate(expression)
3454
+ self._check_browser()
3455
+ original_expression = expression
3456
+ expression = expression.strip()
3457
+ exp_list = expression.split("\n")
3458
+ if exp_list and exp_list[-1].strip().startswith("return "):
3459
+ expression = (
3460
+ "\n".join(exp_list[0:-1]) + "\n"
3461
+ + exp_list[-1].strip()[len("return "):]
3462
+ ).strip()
3463
+ evaluation = self.driver.execute_cdp_cmd(
3464
+ "Runtime.evaluate",
3465
+ {
3466
+ "expression": expression
3467
+ },
3468
+ )
3469
+ if "value" in evaluation["result"]:
3470
+ return evaluation["result"]["value"]
3471
+ elif evaluation["result"]["type"] == "undefined":
3472
+ return None
3473
+ elif "exceptionDetails" in evaluation:
3474
+ raise Exception(evaluation["result"]["description"], expression)
3475
+ elif evaluation["result"]["type"] == "object":
3476
+ if "return " not in original_expression:
3477
+ expression = "return " + original_expression.strip()
3478
+ # Need to use execute_script() to return a WebDriver object.
3479
+ # If this causes duplicate evaluation, don't use evaluate().
3480
+ return self.execute_script(expression)
3481
+ elif evaluation["result"]["type"] == "function":
3482
+ return {} # This is what sb.cdp.evaluate returns
3483
+ elif "description" in evaluation["result"]:
3484
+ # At this point, the description is the exception
3485
+ raise Exception(evaluation["result"]["description"], expression)
3486
+ else: # Possibly an unhandled case if reached
3487
+ return None
3488
+
3429
3489
  def execute_script(self, script, *args, **kwargs):
3430
3490
  self.__check_scope()
3431
3491
  if self.__is_cdp_swap_needed():
@@ -3940,10 +4000,23 @@ class BaseCase(unittest.TestCase):
3940
4000
  Reverts self.set_content_to_frame()."""
3941
4001
  self.set_content_to_default(nested=True)
3942
4002
 
3943
- def open_new_window(self, switch_to=True):
4003
+ def open_new_window(self, switch_to=True, **kwargs):
3944
4004
  """Opens a new browser tab/window and switches to it by default."""
4005
+ url = None
4006
+ if self.__looks_like_a_page_url(str(switch_to)):
4007
+ # Different API for CDP Mode: First arg is a `url`.
4008
+ # (Also, don't break backwards compat for reg mode)
4009
+ url = switch_to
4010
+ switch_to = True
3945
4011
  if self.__is_cdp_swap_needed():
3946
- self.cdp.open_new_tab(switch_to=switch_to)
4012
+ self.cdp.open_new_tab(url=url, switch_to=switch_to, **kwargs)
4013
+ return
4014
+ elif (
4015
+ getattr(self.driver, "_is_using_uc", None)
4016
+ and getattr(self.driver, "_is_using_cdp", None)
4017
+ ):
4018
+ self.disconnect()
4019
+ self.cdp.open_new_tab(url=url, switch_to=switch_to, **kwargs)
3947
4020
  return
3948
4021
  self.wait_for_ready_state_complete()
3949
4022
  if switch_to:
@@ -4694,7 +4767,7 @@ class BaseCase(unittest.TestCase):
4694
4767
  if not os.path.exists(file_path):
4695
4768
  os.makedirs(file_path)
4696
4769
  cookies_file_path = os.path.join(file_path, name)
4697
- cookies_file = open(cookies_file_path, "w+", encoding="utf-8")
4770
+ cookies_file = open(cookies_file_path, mode="w+", encoding="utf-8")
4698
4771
  cookies_file.writelines(json_cookies)
4699
4772
  cookies_file.close()
4700
4773
 
@@ -4853,7 +4926,7 @@ class BaseCase(unittest.TestCase):
4853
4926
  self.driver.add_cookie(cookie)
4854
4927
 
4855
4928
  def __set_esc_skip(self):
4856
- if hasattr(self, "esc_end") and self.esc_end:
4929
+ if getattr(self, "esc_end", None):
4857
4930
  script = (
4858
4931
  """document.onkeydown = function(evt) {
4859
4932
  evt = evt || window.event;
@@ -4871,7 +4944,7 @@ class BaseCase(unittest.TestCase):
4871
4944
  self.execute_script(script)
4872
4945
 
4873
4946
  def __skip_if_esc(self):
4874
- if hasattr(self, "esc_end") and self.esc_end:
4947
+ if getattr(self, "esc_end", None):
4875
4948
  if self.execute_script("return document.sb_esc_end;") == "yes":
4876
4949
  self.skip()
4877
4950
 
@@ -4893,8 +4966,7 @@ class BaseCase(unittest.TestCase):
4893
4966
  self.__disable_beforeunload_as_needed()
4894
4967
  if (
4895
4968
  self.page_load_strategy == "none"
4896
- and hasattr(settings, "SKIP_JS_WAITS")
4897
- and settings.SKIP_JS_WAITS
4969
+ and getattr(settings, "SKIP_JS_WAITS", None)
4898
4970
  ):
4899
4971
  time.sleep(0.01)
4900
4972
  if self.undetectable:
@@ -4915,10 +4987,7 @@ class BaseCase(unittest.TestCase):
4915
4987
 
4916
4988
  def sleep(self, seconds):
4917
4989
  self.__check_scope()
4918
- if (
4919
- not hasattr(sb_config, "time_limit")
4920
- or (hasattr(sb_config, "time_limit") and not sb_config.time_limit)
4921
- ):
4990
+ if not getattr(sb_config, "time_limit", None):
4922
4991
  time.sleep(seconds)
4923
4992
  elif seconds < 0.4:
4924
4993
  shared_utils.check_if_time_limit_exceeded()
@@ -4933,11 +5002,7 @@ class BaseCase(unittest.TestCase):
4933
5002
  if now_ms >= stop_ms:
4934
5003
  break
4935
5004
  time.sleep(0.2)
4936
- if (
4937
- self.recorder_mode
4938
- and hasattr(sb_config, "record_sleep")
4939
- and sb_config.record_sleep
4940
- ):
5005
+ if self.recorder_mode and getattr(sb_config, "record_sleep", None):
4941
5006
  time_stamp = self.execute_script("return Date.now();")
4942
5007
  origin = self.get_origin()
4943
5008
  action = ["sleep", seconds, origin, time_stamp]
@@ -4995,14 +5060,14 @@ class BaseCase(unittest.TestCase):
4995
5060
 
4996
5061
  def activate_cdp_mode(self, url=None, **kwargs):
4997
5062
  """Activate CDP Mode with the URL and kwargs."""
4998
- if hasattr(self.driver, "_is_using_uc") and self.driver._is_using_uc:
5063
+ if getattr(self.driver, "_is_using_uc", None):
4999
5064
  if self.__is_cdp_swap_needed():
5000
5065
  return # CDP Mode is already active
5001
5066
  if not self.is_connected():
5002
5067
  self.driver.connect()
5003
5068
  current_url = self.get_current_url()
5004
5069
  if not current_url.startswith(("about", "data", "chrome")):
5005
- self.open("about:blank")
5070
+ self.driver.get("about:blank")
5006
5071
  self.driver.uc_open_with_cdp_mode(url, **kwargs)
5007
5072
  else:
5008
5073
  self.get_new_driver(undetectable=True)
@@ -5010,11 +5075,17 @@ class BaseCase(unittest.TestCase):
5010
5075
  self.cdp = self.driver.cdp
5011
5076
  if hasattr(self.cdp, "solve_captcha"):
5012
5077
  self.solve_captcha = self.cdp.solve_captcha
5078
+ if hasattr(self.cdp, "click_captcha"):
5079
+ self.click_captcha = self.cdp.click_captcha
5080
+ if hasattr(self.cdp, "find_element_by_text"):
5081
+ self.find_element_by_text = self.cdp.find_element_by_text
5082
+ if getattr(self.driver, "_is_using_auth", None):
5083
+ with suppress(Exception):
5084
+ self.cdp.loop.run_until_complete(self.cdp.page.wait(0.25))
5013
5085
  self.undetectable = True
5014
5086
 
5015
5087
  def activate_recorder(self):
5016
- """Activate Recorder Mode on the current tab/window.
5017
- For persistent Recorder Mode, use the extension instead."""
5088
+ """Activate Recorder Mode on the newest tab / window."""
5018
5089
  from seleniumbase.js_code.recorder_js import recorder_js
5019
5090
 
5020
5091
  if not self.is_chromium():
@@ -5027,7 +5098,7 @@ class BaseCase(unittest.TestCase):
5027
5098
  "The %s Recorder is for Chromium only!\n"
5028
5099
  " (Supported browsers: Chrome and Edge)" % sc
5029
5100
  )
5030
- url = self.driver.current_url
5101
+ url = self.get_current_url()
5031
5102
  if url.startswith(("data:", "about:", "chrome:", "edge:")):
5032
5103
  message = (
5033
5104
  "The URL in Recorder-Mode cannot start with: "
@@ -5035,8 +5106,9 @@ class BaseCase(unittest.TestCase):
5035
5106
  )
5036
5107
  print("\n" + message)
5037
5108
  return
5038
- if self.recorder_ext:
5109
+ if self.recorder_ext and not self.undetectable:
5039
5110
  return # The Recorder extension is already active
5111
+ self.switch_to_newest_tab()
5040
5112
  with suppress(Exception):
5041
5113
  recorder_on = self.get_session_storage_item("recorder_activated")
5042
5114
  if not recorder_on == "yes":
@@ -5659,14 +5731,13 @@ class BaseCase(unittest.TestCase):
5659
5731
  methodname = self._testMethodName
5660
5732
  context_filename = None
5661
5733
  if (
5662
- hasattr(sb_config, "is_context_manager")
5663
- and sb_config.is_context_manager
5734
+ getattr(sb_config, "is_context_manager", None)
5664
5735
  and (filename == "base_case.py" or methodname == "runTest")
5665
5736
  ):
5666
5737
  import traceback
5667
5738
  stack_base = traceback.format_stack()[0].split(os.sep)[-1]
5668
5739
  test_base = stack_base.split(", in ")[0]
5669
- if hasattr(self, "cm_filename") and self.cm_filename:
5740
+ if getattr(self, "cm_filename", None):
5670
5741
  filename = self.cm_filename
5671
5742
  else:
5672
5743
  filename = test_base.split('"')[0]
@@ -5683,7 +5754,7 @@ class BaseCase(unittest.TestCase):
5683
5754
  classname = "MyTestClass"
5684
5755
  methodname = methodname.replace("[", "__").replace("]", "")
5685
5756
  methodname = re.sub(r"[\W]", "_", methodname)
5686
- if hasattr(self, "is_behave") and self.is_behave:
5757
+ if getattr(self, "is_behave", None):
5687
5758
  classname = sb_config.behave_feature.name
5688
5759
  classname = classname.replace("/", " ").replace(" & ", " ")
5689
5760
  classname = re.sub(r"[^\w" + r"_ " + r"]", "", classname)
@@ -5751,7 +5822,7 @@ class BaseCase(unittest.TestCase):
5751
5822
  extra_file_name = "__init__.py"
5752
5823
  extra_file_path = os.path.join(recordings_folder, extra_file_name)
5753
5824
  if not os.path.exists(extra_file_path):
5754
- out_file = open(extra_file_path, "w+", "utf-8")
5825
+ out_file = open(extra_file_path, mode="w+", encoding="utf-8")
5755
5826
  out_file.writelines("\r\n".join(data))
5756
5827
  out_file.close()
5757
5828
  sys.stdout.write("\nCreated recordings%s__init__.py" % os.sep)
@@ -5799,7 +5870,7 @@ class BaseCase(unittest.TestCase):
5799
5870
  extra_file_name = "pytest.ini"
5800
5871
  extra_file_path = os.path.join(recordings_folder, extra_file_name)
5801
5872
  if not os.path.exists(extra_file_path):
5802
- out_file = open(extra_file_path, "w+", "utf-8")
5873
+ out_file = open(extra_file_path, mode="w+", encoding="utf-8")
5803
5874
  out_file.writelines("\r\n".join(data))
5804
5875
  out_file.close()
5805
5876
  sys.stdout.write("\nCreated recordings%spytest.ini" % os.sep)
@@ -5820,7 +5891,7 @@ class BaseCase(unittest.TestCase):
5820
5891
  extra_file_name = "setup.cfg"
5821
5892
  extra_file_path = os.path.join(recordings_folder, extra_file_name)
5822
5893
  if not os.path.exists(extra_file_path):
5823
- out_file = open(extra_file_path, "w+", "utf-8")
5894
+ out_file = open(extra_file_path, mode="w+", encoding="utf-8")
5824
5895
  out_file.writelines("\r\n".join(data))
5825
5896
  out_file.close()
5826
5897
  sys.stdout.write("\nCreated recordings%ssetup.cfg" % os.sep)
@@ -5831,14 +5902,14 @@ class BaseCase(unittest.TestCase):
5831
5902
  test_id = sb_config._test_id
5832
5903
  file_name = test_id.split("::")[0].split("/")[-1].split("\\")[-1]
5833
5904
  file_name = file_name.split(".py")[0] + "_rec.py"
5834
- if hasattr(self, "is_behave") and self.is_behave:
5905
+ if getattr(self, "is_behave", None):
5835
5906
  file_name = sb_config.behave_scenario.filename.replace(".", "_")
5836
5907
  file_name = file_name.split("/")[-1].split("\\")[-1] + "_rec.py"
5837
5908
  file_name = file_name
5838
5909
  elif context_filename:
5839
5910
  file_name = context_filename
5840
5911
  file_path = os.path.join(recordings_folder, file_name)
5841
- out_file = open(file_path, "w+", "utf-8")
5912
+ out_file = open(file_path, mode="w+", encoding="utf-8")
5842
5913
  out_file.writelines("\r\n".join(data))
5843
5914
  out_file.close()
5844
5915
  rec_message = ">>> RECORDING SAVED as: "
@@ -5850,7 +5921,7 @@ class BaseCase(unittest.TestCase):
5850
5921
  if terminal_size > 30 and star_len > terminal_size:
5851
5922
  star_len = terminal_size
5852
5923
  spc = "\n\n"
5853
- if hasattr(self, "rec_print") and self.rec_print:
5924
+ if getattr(self, "rec_print", None):
5854
5925
  spc = ""
5855
5926
  sys.stdout.write("\nCreated recordings%s%s" % (os.sep, file_name))
5856
5927
  print()
@@ -5871,7 +5942,7 @@ class BaseCase(unittest.TestCase):
5871
5942
  rec_message = rec_message.replace(">>>", c2 + ">>>" + cr)
5872
5943
  print("%s%s%s%s%s\n%s" % (spc, rec_message, c1, file_path, cr, stars))
5873
5944
 
5874
- if hasattr(self, "rec_behave") and self.rec_behave:
5945
+ if getattr(self, "rec_behave", None):
5875
5946
  # Also generate necessary behave-gherkin files.
5876
5947
  self.__process_recorded_behave_actions(srt_actions, colorama)
5877
5948
 
@@ -5882,7 +5953,7 @@ class BaseCase(unittest.TestCase):
5882
5953
  filename = self.__get_filename()
5883
5954
  feature_class = None
5884
5955
  scenario_test = None
5885
- if hasattr(self, "is_behave") and self.is_behave:
5956
+ if getattr(self, "is_behave", None):
5886
5957
  feature_class = sb_config.behave_feature.name
5887
5958
  scenario_test = sb_config.behave_scenario.name
5888
5959
  else:
@@ -5936,11 +6007,11 @@ class BaseCase(unittest.TestCase):
5936
6007
  os.makedirs(steps_folder)
5937
6008
 
5938
6009
  file_name = filename.split(".")[0]
5939
- if hasattr(self, "is_behave") and self.is_behave:
6010
+ if getattr(self, "is_behave", None):
5940
6011
  file_name = sb_config.behave_scenario.filename.replace(".", "_")
5941
6012
  file_name = file_name.split("/")[-1].split("\\")[-1] + "_rec.feature"
5942
6013
  file_path = os.path.join(features_folder, file_name)
5943
- out_file = open(file_path, "w+", "utf-8")
6014
+ out_file = open(file_path, mode="w+", encoding="utf-8")
5944
6015
  out_file.writelines("\r\n".join(data))
5945
6016
  out_file.close()
5946
6017
 
@@ -5953,7 +6024,7 @@ class BaseCase(unittest.TestCase):
5953
6024
  if terminal_size > 30 and star_len > terminal_size:
5954
6025
  star_len = terminal_size
5955
6026
  spc = "\n"
5956
- if hasattr(self, "rec_print") and self.rec_print:
6027
+ if getattr(self, "rec_print", None):
5957
6028
  spc = ""
5958
6029
  print()
5959
6030
  if " " not in file_path:
@@ -5978,7 +6049,7 @@ class BaseCase(unittest.TestCase):
5978
6049
  file_name = "__init__.py"
5979
6050
  file_path = os.path.join(features_folder, file_name)
5980
6051
  if not os.path.exists(file_path):
5981
- out_file = open(file_path, "w+", "utf-8")
6052
+ out_file = open(file_path, mode="w+", encoding="utf-8")
5982
6053
  out_file.writelines("\r\n".join(data))
5983
6054
  out_file.close()
5984
6055
  print("Created recordings/features/__init__.py")
@@ -5991,7 +6062,7 @@ class BaseCase(unittest.TestCase):
5991
6062
  file_name = "behave.ini"
5992
6063
  file_path = os.path.join(features_folder, file_name)
5993
6064
  if not os.path.exists(file_path):
5994
- out_file = open(file_path, "w+", "utf-8")
6065
+ out_file = open(file_path, mode="w+", encoding="utf-8")
5995
6066
  out_file.writelines("\r\n".join(data))
5996
6067
  out_file.close()
5997
6068
  print("Created recordings/features/behave.ini")
@@ -6030,7 +6101,7 @@ class BaseCase(unittest.TestCase):
6030
6101
  file_name = "environment.py"
6031
6102
  file_path = os.path.join(features_folder, file_name)
6032
6103
  if not os.path.exists(file_path):
6033
- out_file = open(file_path, "w+", "utf-8")
6104
+ out_file = open(file_path, mode="w+", encoding="utf-8")
6034
6105
  out_file.writelines("\r\n".join(data))
6035
6106
  out_file.close()
6036
6107
  print("Created recordings/features/environment.py")
@@ -6040,7 +6111,7 @@ class BaseCase(unittest.TestCase):
6040
6111
  file_name = "__init__.py"
6041
6112
  file_path = os.path.join(steps_folder, file_name)
6042
6113
  if not os.path.exists(file_path):
6043
- out_file = open(file_path, "w+", "utf-8")
6114
+ out_file = open(file_path, mode="w+", encoding="utf-8")
6044
6115
  out_file.writelines("\r\n".join(data))
6045
6116
  out_file.close()
6046
6117
  print("Created recordings/features/steps/__init__.py")
@@ -6051,7 +6122,7 @@ class BaseCase(unittest.TestCase):
6051
6122
  file_name = "imported.py"
6052
6123
  file_path = os.path.join(steps_folder, file_name)
6053
6124
  if not os.path.exists(file_path):
6054
- out_file = open(file_path, "w+", "utf-8")
6125
+ out_file = open(file_path, mode="w+", encoding="utf-8")
6055
6126
  out_file.writelines("\r\n".join(data))
6056
6127
  out_file.close()
6057
6128
  print("Created recordings/features/steps/imported.py")
@@ -6278,7 +6349,12 @@ class BaseCase(unittest.TestCase):
6278
6349
  scroll - the option to scroll to the element first (Default: True)
6279
6350
  timeout - the time to wait for the element to appear """
6280
6351
  self.__check_scope()
6281
- if not self.__is_cdp_swap_needed():
6352
+ if self.__is_cdp_swap_needed():
6353
+ if page_utils.is_xpath_selector(selector):
6354
+ if "contains(" in selector:
6355
+ self.cdp.highlight(selector)
6356
+ return
6357
+ else:
6282
6358
  self._check_browser()
6283
6359
  self.__skip_if_esc()
6284
6360
  if isinstance(selector, WebElement):
@@ -6323,6 +6399,7 @@ class BaseCase(unittest.TestCase):
6323
6399
  By default, "html" will be used as the CSS Selector target.
6324
6400
  You can specify how many times in-a-row the action happens."""
6325
6401
  self.__check_scope()
6402
+ self._check_browser()
6326
6403
  if times < 1:
6327
6404
  return
6328
6405
  element = self.wait_for_element_present(selector)
@@ -6345,6 +6422,7 @@ class BaseCase(unittest.TestCase):
6345
6422
  By default, "html" will be used as the CSS Selector target.
6346
6423
  You can specify how many times in-a-row the action happens."""
6347
6424
  self.__check_scope()
6425
+ self._check_browser()
6348
6426
  if times < 1:
6349
6427
  return
6350
6428
  element = self.wait_for_element_present(selector)
@@ -6367,6 +6445,7 @@ class BaseCase(unittest.TestCase):
6367
6445
  By default, "html" will be used as the CSS Selector target.
6368
6446
  You can specify how many times in-a-row the action happens."""
6369
6447
  self.__check_scope()
6448
+ self._check_browser()
6370
6449
  if times < 1:
6371
6450
  return
6372
6451
  element = self.wait_for_element_present(selector)
@@ -6389,6 +6468,7 @@ class BaseCase(unittest.TestCase):
6389
6468
  By default, "html" will be used as the CSS Selector target.
6390
6469
  You can specify how many times in-a-row the action happens."""
6391
6470
  self.__check_scope()
6471
+ self._check_browser()
6392
6472
  if times < 1:
6393
6473
  return
6394
6474
  element = self.wait_for_element_present(selector)
@@ -7297,21 +7377,6 @@ class BaseCase(unittest.TestCase):
7297
7377
  with pip_find_lock:
7298
7378
  with suppress(Exception):
7299
7379
  shared_utils.make_writable(constants.PipInstall.FINDLOCK)
7300
- if sys.version_info < (3, 9):
7301
- # Fix bug in newer cryptography for Python 3.7 and 3.8:
7302
- # "pyo3_runtime.PanicException: Python API call failed"
7303
- try:
7304
- import cryptography
7305
- if cryptography.__version__ != "39.0.2":
7306
- del cryptography # To get newer ver
7307
- shared_utils.pip_install(
7308
- "cryptography", version="39.0.2"
7309
- )
7310
- import cryptography
7311
- except Exception:
7312
- shared_utils.pip_install(
7313
- "cryptography", version="39.0.2"
7314
- )
7315
7380
  try:
7316
7381
  from pdfminer.high_level import extract_text
7317
7382
  except Exception:
@@ -7603,7 +7668,11 @@ class BaseCase(unittest.TestCase):
7603
7668
  destination_folder = constants.Files.DOWNLOADS_FOLDER
7604
7669
  if not os.path.exists(destination_folder):
7605
7670
  os.makedirs(destination_folder)
7606
- page_utils._download_file_to(file_url, destination_folder)
7671
+ agent = self.get_user_agent()
7672
+ headers = {"user-agent": agent}
7673
+ page_utils._download_file_to(
7674
+ file_url, destination_folder, headers=headers
7675
+ )
7607
7676
  if self.recorder_mode and self.__current_url_is_recordable():
7608
7677
  if self.get_session_storage_item("pause_recorder") == "no":
7609
7678
  time_stamp = self.execute_script("return Date.now();")
@@ -7627,8 +7696,10 @@ class BaseCase(unittest.TestCase):
7627
7696
  destination_folder = constants.Files.DOWNLOADS_FOLDER
7628
7697
  if not os.path.exists(destination_folder):
7629
7698
  os.makedirs(destination_folder)
7699
+ agent = self.get_user_agent()
7700
+ headers = {"user-agent": agent}
7630
7701
  page_utils._download_file_to(
7631
- file_url, destination_folder, new_file_name
7702
+ file_url, destination_folder, new_file_name, headers=headers
7632
7703
  )
7633
7704
 
7634
7705
  def save_data_as(self, data, file_name, destination_folder=None):
@@ -8740,6 +8811,9 @@ class BaseCase(unittest.TestCase):
8740
8811
  if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
8741
8812
  timeout = self.__get_new_timeout(timeout)
8742
8813
  selector, by = self.__recalculate_selector(selector, by)
8814
+ if self.__is_cdp_swap_needed():
8815
+ self.cdp.set_value(selector, text)
8816
+ return
8743
8817
  self.wait_for_ready_state_complete()
8744
8818
  element = page_actions.wait_for_element_present(
8745
8819
  self.driver, selector, by, timeout
@@ -8760,10 +8834,14 @@ class BaseCase(unittest.TestCase):
8760
8834
  if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
8761
8835
  timeout = self.__get_new_timeout(timeout)
8762
8836
  selector, by = self.__recalculate_selector(selector, by)
8763
- self.wait_for_ready_state_complete()
8764
- element = page_actions.wait_for_element_present(
8765
- self.driver, selector, by, timeout
8766
- )
8837
+ element = None
8838
+ if self.__is_cdp_swap_needed():
8839
+ element = self.cdp.select(selector, timeout=timeout)
8840
+ else:
8841
+ self.wait_for_ready_state_complete()
8842
+ element = page_actions.wait_for_element_present(
8843
+ self.driver, selector, by, timeout
8844
+ )
8767
8845
  if element.tag_name.lower() in ["input", "textarea"]:
8768
8846
  self.js_update_text(selector, text, by=by, timeout=timeout)
8769
8847
  return
@@ -8955,7 +9033,7 @@ class BaseCase(unittest.TestCase):
8955
9033
  self.__passed_then_skipped = True
8956
9034
  self.__will_be_skipped = True
8957
9035
  sb_config._results[test_id] = "Skipped"
8958
- if hasattr(self, "with_db_reporting") and self.with_db_reporting:
9036
+ if getattr(self, "with_db_reporting", None):
8959
9037
  if self.is_pytest:
8960
9038
  self.__skip_reason = reason
8961
9039
  else:
@@ -9226,9 +9304,9 @@ class BaseCase(unittest.TestCase):
9226
9304
  """Same as self.refresh_page()"""
9227
9305
  self.refresh_page()
9228
9306
 
9229
- def open_new_tab(self, switch_to=True):
9307
+ def open_new_tab(self, switch_to=True, **kwargs):
9230
9308
  """Same as self.open_new_window()"""
9231
- self.open_new_window(switch_to=switch_to)
9309
+ self.open_new_window(switch_to=switch_to, **kwargs)
9232
9310
 
9233
9311
  def switch_to_tab(self, tab, timeout=None):
9234
9312
  """Same as self.switch_to_window()
@@ -9253,81 +9331,42 @@ class BaseCase(unittest.TestCase):
9253
9331
  self, selector, text, by="css selector", timeout=None, retry=False
9254
9332
  ):
9255
9333
  """Same as self.update_text()"""
9256
- self.__check_scope()
9257
- if not timeout:
9258
- timeout = settings.LARGE_TIMEOUT
9259
- if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
9260
- timeout = self.__get_new_timeout(timeout)
9261
- selector, by = self.__recalculate_selector(selector, by)
9262
9334
  self.update_text(selector, text, by=by, timeout=timeout, retry=retry)
9263
9335
 
9264
9336
  def fill(
9265
9337
  self, selector, text, by="css selector", timeout=None, retry=False
9266
9338
  ):
9267
9339
  """Same as self.update_text()"""
9268
- self.__check_scope()
9269
- if not timeout:
9270
- timeout = settings.LARGE_TIMEOUT
9271
- if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
9272
- timeout = self.__get_new_timeout(timeout)
9273
- selector, by = self.__recalculate_selector(selector, by)
9274
9340
  self.update_text(selector, text, by=by, timeout=timeout, retry=retry)
9275
9341
 
9276
9342
  def write(
9277
9343
  self, selector, text, by="css selector", timeout=None, retry=False
9278
9344
  ):
9279
9345
  """Same as self.update_text()"""
9280
- self.__check_scope()
9281
- if not timeout:
9282
- timeout = settings.LARGE_TIMEOUT
9283
- if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
9284
- timeout = self.__get_new_timeout(timeout)
9285
- selector, by = self.__recalculate_selector(selector, by)
9286
9346
  self.update_text(selector, text, by=by, timeout=timeout, retry=retry)
9287
9347
 
9288
9348
  def click_link(self, link_text, timeout=None):
9289
9349
  """Same as self.click_link_text()"""
9290
- self.__check_scope()
9291
- if not timeout:
9292
- timeout = settings.SMALL_TIMEOUT
9293
- if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
9294
- timeout = self.__get_new_timeout(timeout)
9295
9350
  self.click_link_text(link_text, timeout=timeout)
9296
9351
 
9297
9352
  def click_partial_link(self, partial_link_text, timeout=None):
9298
9353
  """Same as self.click_partial_link_text()"""
9299
- self.__check_scope()
9300
- if not timeout:
9301
- timeout = settings.SMALL_TIMEOUT
9302
- if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
9303
- timeout = self.__get_new_timeout(timeout)
9304
9354
  self.click_partial_link_text(partial_link_text, timeout=timeout)
9305
9355
 
9306
9356
  def right_click(self, selector, by="css selector", timeout=None):
9307
9357
  """Same as self.context_click()"""
9308
- self.__check_scope()
9309
- if not timeout:
9310
- timeout = settings.SMALL_TIMEOUT
9311
- if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
9312
- timeout = self.__get_new_timeout(timeout)
9313
9358
  self.context_click(selector, by=by, timeout=timeout)
9314
9359
 
9360
+ def hover_element(self, selector, by="css selector", timeout=None):
9361
+ """Same as self.hover()"""
9362
+ return self.hover(selector, by=by, timeout=timeout)
9363
+
9315
9364
  def hover_on_element(self, selector, by="css selector", timeout=None):
9316
9365
  """Same as self.hover()"""
9317
- self.__check_scope()
9318
- if not timeout:
9319
- timeout = settings.SMALL_TIMEOUT
9320
- if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
9321
- timeout = self.__get_new_timeout(timeout)
9322
9366
  return self.hover(selector, by=by, timeout=timeout)
9323
9367
 
9324
9368
  def hover_over_element(self, selector, by="css selector", timeout=None):
9325
9369
  """Same as self.hover()"""
9326
- self.__check_scope()
9327
- if not timeout:
9328
- timeout = settings.SMALL_TIMEOUT
9329
- if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
9330
- timeout = self.__get_new_timeout(timeout)
9331
9370
  return self.hover(selector, by=by, timeout=timeout)
9332
9371
 
9333
9372
  def wait_for_element_visible(
@@ -9652,7 +9691,7 @@ class BaseCase(unittest.TestCase):
9652
9691
  To force a print during multithreaded tests, use: "sys.stderr.write()".
9653
9692
  To print without the new-line character end, use: "sys.stdout.write()".
9654
9693
  """
9655
- if hasattr(sb_config, "_multithreaded") and sb_config._multithreaded:
9694
+ if getattr(sb_config, "_multithreaded", None):
9656
9695
  if not isinstance(msg, str):
9657
9696
  with suppress(Exception):
9658
9697
  msg = str(msg)
@@ -10091,6 +10130,21 @@ class BaseCase(unittest.TestCase):
10091
10130
  if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
10092
10131
  timeout = self.__get_new_timeout(timeout)
10093
10132
  if self.__is_cdp_swap_needed():
10133
+ if self.demo_mode:
10134
+ selector, by = self.__recalculate_selector(
10135
+ selector, by, xp_ok=False
10136
+ )
10137
+ a_t = "ASSERT"
10138
+ if self._language != "English":
10139
+ from seleniumbase.fixtures.words import SD
10140
+
10141
+ a_t = SD.translate_assert(self._language)
10142
+ messenger_post = "<b>%s %s</b>: %s" % (
10143
+ a_t, by.upper(), selector
10144
+ )
10145
+ self.__highlight_with_assert_success(
10146
+ messenger_post, selector, by
10147
+ )
10094
10148
  self.cdp.assert_element(selector, timeout=timeout)
10095
10149
  return True
10096
10150
  if isinstance(selector, list):
@@ -10385,6 +10439,20 @@ class BaseCase(unittest.TestCase):
10385
10439
  messenger_post, selector, by
10386
10440
  )
10387
10441
  elif self.__is_cdp_swap_needed():
10442
+ if self.demo_mode:
10443
+ a_t = "ASSERT TEXT"
10444
+ i_n = "in"
10445
+ if self._language != "English":
10446
+ from seleniumbase.fixtures.words import SD
10447
+
10448
+ a_t = SD.translate_assert_text(self._language)
10449
+ i_n = SD.translate_in(self._language)
10450
+ messenger_post = "<b>%s</b>: {%s} %s %s: %s" % (
10451
+ a_t, text, i_n, by.upper(), selector
10452
+ )
10453
+ self.__highlight_with_assert_success(
10454
+ messenger_post, selector, by
10455
+ )
10388
10456
  self.cdp.assert_text(text, selector, timeout=timeout)
10389
10457
  return True
10390
10458
  elif self.__is_shadow_selector(selector):
@@ -11120,7 +11188,7 @@ class BaseCase(unittest.TestCase):
11120
11188
  return # Skip the rest when deferred visual asserts are used
11121
11189
  the_html = visual_helper.get_sbs_html()
11122
11190
  file_path = os.path.join(test_logpath, constants.SideBySide.HTML_FILE)
11123
- out_file = open(file_path, "w+", encoding="utf-8")
11191
+ out_file = open(file_path, mode="w+", encoding="utf-8")
11124
11192
  out_file.writelines(the_html)
11125
11193
  out_file.close()
11126
11194
 
@@ -11280,16 +11348,16 @@ class BaseCase(unittest.TestCase):
11280
11348
  self.save_screenshot(
11281
11349
  baseline_png, visual_baseline_path, selector="body"
11282
11350
  )
11283
- out_file = open(page_url_file, "w+", encoding="utf-8")
11351
+ out_file = open(page_url_file, mode="w+", encoding="utf-8")
11284
11352
  out_file.writelines(page_url)
11285
11353
  out_file.close()
11286
- out_file = open(level_1_file, "w+", encoding="utf-8")
11354
+ out_file = open(level_1_file, mode="w+", encoding="utf-8")
11287
11355
  out_file.writelines(json.dumps(level_1))
11288
11356
  out_file.close()
11289
- out_file = open(level_2_file, "w+", encoding="utf-8")
11357
+ out_file = open(level_2_file, mode="w+", encoding="utf-8")
11290
11358
  out_file.writelines(json.dumps(level_2))
11291
11359
  out_file.close()
11292
- out_file = open(level_3_file, "w+", encoding="utf-8")
11360
+ out_file = open(level_3_file, mode="w+", encoding="utf-8")
11293
11361
  out_file.writelines(json.dumps(level_3))
11294
11362
  out_file.close()
11295
11363
 
@@ -11428,7 +11496,7 @@ class BaseCase(unittest.TestCase):
11428
11496
  alpha_n_d_name = "".join([x if x.isalnum() else "_" for x in name])
11429
11497
  side_by_side_name = "side_by_side_%s.html" % alpha_n_d_name
11430
11498
  file_path = os.path.join(test_logpath, side_by_side_name)
11431
- out_file = open(file_path, "w+", encoding="utf-8")
11499
+ out_file = open(file_path, mode="w+", encoding="utf-8")
11432
11500
  out_file.writelines(the_html)
11433
11501
  out_file.close()
11434
11502
 
@@ -12120,7 +12188,7 @@ class BaseCase(unittest.TestCase):
12120
12188
  with suppress(Exception):
12121
12189
  os.makedirs(saved_presentations_folder)
12122
12190
  file_path = os.path.join(saved_presentations_folder, filename)
12123
- out_file = open(file_path, "w+", encoding="utf-8")
12191
+ out_file = open(file_path, mode="w+", encoding="utf-8")
12124
12192
  out_file.writelines(the_html)
12125
12193
  out_file.close()
12126
12194
  if self._output_file_saves:
@@ -12815,7 +12883,7 @@ class BaseCase(unittest.TestCase):
12815
12883
  with suppress(Exception):
12816
12884
  os.makedirs(saved_charts_folder)
12817
12885
  file_path = os.path.join(saved_charts_folder, filename)
12818
- out_file = open(file_path, "w+", encoding="utf-8")
12886
+ out_file = open(file_path, mode="w+", encoding="utf-8")
12819
12887
  out_file.writelines(the_html)
12820
12888
  out_file.close()
12821
12889
  if self._output_file_saves:
@@ -13874,7 +13942,7 @@ class BaseCase(unittest.TestCase):
13874
13942
  simulateClick(someLink);"""
13875
13943
  % css_selector
13876
13944
  )
13877
- if hasattr(self, "recorder_mode") and self.recorder_mode:
13945
+ if getattr(self, "recorder_mode", None):
13878
13946
  self.save_recorded_actions()
13879
13947
  try:
13880
13948
  self.execute_script(script)
@@ -13922,7 +13990,7 @@ class BaseCase(unittest.TestCase):
13922
13990
  var someLink = arguments[0];
13923
13991
  simulateClick(someLink);"""
13924
13992
  )
13925
- if hasattr(self, "recorder_mode") and self.recorder_mode:
13993
+ if getattr(self, "recorder_mode", None):
13926
13994
  self.save_recorded_actions()
13927
13995
  try:
13928
13996
  self.execute_script(script, element)
@@ -14129,7 +14197,7 @@ class BaseCase(unittest.TestCase):
14129
14197
  selector = self.convert_to_css_selector(selector, by=by)
14130
14198
  selector = self.__make_css_match_first_element_only(selector)
14131
14199
  click_script = """jQuery('%s')[0].click();""" % selector
14132
- if hasattr(self, "recorder_mode") and self.recorder_mode:
14200
+ if getattr(self, "recorder_mode", None):
14133
14201
  self.save_recorded_actions()
14134
14202
  self.safe_execute_script(click_script)
14135
14203
 
@@ -14285,8 +14353,7 @@ class BaseCase(unittest.TestCase):
14285
14353
  def __needs_minimum_wait(self):
14286
14354
  if (
14287
14355
  self.page_load_strategy == "none"
14288
- and hasattr(settings, "SKIP_JS_WAITS")
14289
- and settings.SKIP_JS_WAITS
14356
+ and getattr(settings, "SKIP_JS_WAITS", None)
14290
14357
  ):
14291
14358
  return True
14292
14359
  else:
@@ -14522,7 +14589,8 @@ class BaseCase(unittest.TestCase):
14522
14589
  sb_config._virtual_display = self._xvfb_display
14523
14590
  if "DISPLAY" not in os.environ.keys():
14524
14591
  print(
14525
- "\nX11 display failed! Will use regular xvfb!"
14592
+ "\n X11 display failed! Is Xvfb installed? "
14593
+ "\n Try this: `sudo apt install -y xvfb`"
14526
14594
  )
14527
14595
  self.__activate_standard_virtual_display()
14528
14596
  else:
@@ -14535,7 +14603,10 @@ class BaseCase(unittest.TestCase):
14535
14603
  print("\n" + str(e.msg))
14536
14604
  else:
14537
14605
  print(e)
14538
- print("\nX11 display failed! Will use regular xvfb!")
14606
+ print(
14607
+ "\n X11 display failed! Is Xvfb installed? "
14608
+ "\n Try this: `sudo apt install -y xvfb`"
14609
+ )
14539
14610
  self.__activate_standard_virtual_display()
14540
14611
  return
14541
14612
  pyautogui_is_installed = False
@@ -14614,10 +14685,7 @@ class BaseCase(unittest.TestCase):
14614
14685
 
14615
14686
  def __disable_beforeunload_as_needed(self):
14616
14687
  """Disables beforeunload as needed. Also resets frame_switch state."""
14617
- if (
14618
- hasattr(self, "_disable_beforeunload")
14619
- and self._disable_beforeunload
14620
- ):
14688
+ if getattr(self, "_disable_beforeunload", None):
14621
14689
  self.disable_beforeunload()
14622
14690
  if self.recorder_mode:
14623
14691
  try:
@@ -15418,9 +15486,9 @@ class BaseCase(unittest.TestCase):
15418
15486
  self.__skip_reason = None
15419
15487
  self.testcase_manager.insert_testcase_data(data_payload)
15420
15488
  self.case_start_time = int(time.time() * 1000.0)
15421
- elif hasattr(self, "is_behave") and self.is_behave:
15489
+ elif getattr(self, "is_behave", None):
15422
15490
  self.__initialize_variables()
15423
- elif hasattr(self, "is_nosetest") and self.is_nosetest:
15491
+ elif getattr(self, "is_nosetest", None):
15424
15492
  pass # Setup performed in plugins for pynose
15425
15493
  else:
15426
15494
  # Pure Python run. (Eg. SB() and Driver() Managers)
@@ -15511,7 +15579,7 @@ class BaseCase(unittest.TestCase):
15511
15579
  "Invalid input for Mobile Emulator device metrics!\n"
15512
15580
  "Expecting a comma-separated string with integer values\n"
15513
15581
  "for Width/Height, and an int or float for Pixel-Ratio.\n"
15514
- 'Example: --metrics="411,731,3" '
15582
+ 'Example: --metrics="412,732,3" '
15515
15583
  )
15516
15584
  if len(metrics_list) != 3:
15517
15585
  raise Exception(exception_string)
@@ -15612,7 +15680,7 @@ class BaseCase(unittest.TestCase):
15612
15680
  )
15613
15681
  raise Exception(message)
15614
15682
 
15615
- if not hasattr(self, "is_nosetest") or not self.is_nosetest:
15683
+ if not getattr(self, "is_nosetest", None):
15616
15684
  # Xvfb Virtual Display activation for Linux
15617
15685
  self.__activate_virtual_display_as_needed()
15618
15686
 
@@ -15788,10 +15856,7 @@ class BaseCase(unittest.TestCase):
15788
15856
  self.__last_page_screenshot_png is for all screenshot log files."""
15789
15857
  SCREENSHOT_SKIPPED = constants.Warnings.SCREENSHOT_SKIPPED
15790
15858
  SCREENSHOT_UNDEFINED = constants.Warnings.SCREENSHOT_UNDEFINED
15791
- if (
15792
- hasattr(self, "no_screenshot_after_test")
15793
- and self.no_screenshot_after_test
15794
- ):
15859
+ if getattr(self, "no_screenshot_after_test", None):
15795
15860
  from seleniumbase.core import encoded_images
15796
15861
 
15797
15862
  NO_SCREENSHOT = encoded_images.get_no_screenshot_png()
@@ -15822,10 +15887,7 @@ class BaseCase(unittest.TestCase):
15822
15887
  ignore_test_time_limit=True,
15823
15888
  )
15824
15889
  try:
15825
- if (
15826
- hasattr(settings, "SCREENSHOT_WITH_BACKGROUND")
15827
- and settings.SCREENSHOT_WITH_BACKGROUND
15828
- ):
15890
+ if getattr(settings, "SCREENSHOT_WITH_BACKGROUND", None):
15829
15891
  self.__last_page_screenshot = (
15830
15892
  self.driver.get_screenshot_as_base64()
15831
15893
  )
@@ -15896,11 +15958,10 @@ class BaseCase(unittest.TestCase):
15896
15958
  exc_message = None
15897
15959
  if (
15898
15960
  hasattr(self, "_outcome")
15899
- and hasattr(self._outcome, "errors")
15900
- and self._outcome.errors
15961
+ and getattr(self._outcome, "errors", None)
15901
15962
  ):
15902
15963
  try:
15903
- exc_message = self._outcome.errors[0][1][1]
15964
+ exc_message = self._outcome.errors[-1][1][1]
15904
15965
  except Exception:
15905
15966
  exc_message = "(Unknown Exception)"
15906
15967
  else:
@@ -15985,8 +16046,7 @@ class BaseCase(unittest.TestCase):
15985
16046
  def __delay_driver_quit(self):
15986
16047
  delay_driver_quit = False
15987
16048
  if (
15988
- hasattr(self, "_using_sb_fixture")
15989
- and self._using_sb_fixture
16049
+ getattr(self, "_using_sb_fixture", None)
15990
16050
  and "--pdb" in sys.argv
15991
16051
  and self.__has_exception()
15992
16052
  and len(self._drivers_list) == 1
@@ -16042,14 +16102,22 @@ class BaseCase(unittest.TestCase):
16042
16102
  has_exception = False
16043
16103
  if hasattr(sys, "last_traceback") and sys.last_traceback is not None:
16044
16104
  has_exception = True
16045
- elif hasattr(self, "is_context_manager") and self.is_context_manager:
16105
+ elif getattr(self, "is_context_manager", None):
16046
16106
  if self.with_testing_base and self._has_failure:
16047
16107
  return True
16048
16108
  else:
16049
16109
  return False
16050
16110
  elif hasattr(self, "_outcome") and hasattr(self._outcome, "errors"):
16051
- if self._outcome.errors:
16052
- has_exception = True
16111
+ if python3_11_or_newer:
16112
+ if (
16113
+ self._outcome.errors
16114
+ and self._outcome.errors[-1]
16115
+ and self._outcome.errors[-1][1]
16116
+ ):
16117
+ has_exception = True
16118
+ else:
16119
+ if self._outcome.errors:
16120
+ has_exception = True
16053
16121
  else:
16054
16122
  has_exception = sys.exc_info()[1] is not None
16055
16123
  if self.__will_be_skipped and hasattr(self, "_using_sb_fixture"):
@@ -16058,7 +16126,7 @@ class BaseCase(unittest.TestCase):
16058
16126
 
16059
16127
  def __get_test_id(self):
16060
16128
  """The id used in various places such as the test log path."""
16061
- if hasattr(self, "is_behave") and self.is_behave:
16129
+ if getattr(self, "is_behave", None):
16062
16130
  file_name = sb_config.behave_scenario.filename
16063
16131
  file_name = file_name.replace("/", ".").replace("\\", ".")
16064
16132
  scenario_name = sb_config.behave_scenario.name
@@ -16068,7 +16136,7 @@ class BaseCase(unittest.TestCase):
16068
16136
  scenario_name = scenario_name.replace(" ", "_")
16069
16137
  test_id = "%s.%s" % (file_name, scenario_name)
16070
16138
  return test_id
16071
- elif hasattr(self, "is_context_manager") and self.is_context_manager:
16139
+ elif getattr(self, "is_context_manager", None):
16072
16140
  if hasattr(self, "_manager_saved_id"):
16073
16141
  self.__saved_id = self._manager_saved_id
16074
16142
  if self.__saved_id:
@@ -16080,7 +16148,7 @@ class BaseCase(unittest.TestCase):
16080
16148
  import traceback
16081
16149
  stack_base = traceback.format_stack()[0].split(os.sep)[-1]
16082
16150
  test_base = stack_base.split(", in ")[0]
16083
- if hasattr(self, "cm_filename") and self.cm_filename:
16151
+ if getattr(self, "cm_filename", None):
16084
16152
  filename = self.cm_filename
16085
16153
  else:
16086
16154
  filename = test_base.split('"')[0]
@@ -16095,7 +16163,7 @@ class BaseCase(unittest.TestCase):
16095
16163
  )
16096
16164
  if self._sb_test_identifier and len(str(self._sb_test_identifier)) > 6:
16097
16165
  test_id = self._sb_test_identifier
16098
- elif hasattr(self, "_using_sb_fixture") and self._using_sb_fixture:
16166
+ elif getattr(self, "_using_sb_fixture", None):
16099
16167
  test_id = sb_config._latest_display_id
16100
16168
  test_id = test_id.replace(".py::", ".").replace("::", ".")
16101
16169
  test_id = test_id.replace("/", ".").replace("\\", ".")
@@ -16117,7 +16185,7 @@ class BaseCase(unittest.TestCase):
16117
16185
  return full_name.split("] ")[0] + "]"
16118
16186
  else:
16119
16187
  return full_name.split(" ")[0]
16120
- if hasattr(self, "is_behave") and self.is_behave:
16188
+ if getattr(self, "is_behave", None):
16121
16189
  return self.__get_test_id()
16122
16190
  test_id = "%s.%s.%s" % (
16123
16191
  self.__class__.__module__.split(".")[-1],
@@ -16138,7 +16206,7 @@ class BaseCase(unittest.TestCase):
16138
16206
  return full_name.split("] ")[0] + "]"
16139
16207
  else:
16140
16208
  return full_name.split(" ")[0]
16141
- if hasattr(self, "is_behave") and self.is_behave:
16209
+ if getattr(self, "is_behave", None):
16142
16210
  file_name = sb_config.behave_scenario.filename
16143
16211
  line_num = sb_config.behave_line_num
16144
16212
  scenario_name = sb_config.behave_scenario.name
@@ -16171,7 +16239,7 @@ class BaseCase(unittest.TestCase):
16171
16239
  if "PYTEST_CURRENT_TEST" in os.environ:
16172
16240
  test_id = os.environ["PYTEST_CURRENT_TEST"].split(" ")[0]
16173
16241
  filename = test_id.split("::")[0].split("/")[-1]
16174
- elif hasattr(self, "is_behave") and self.is_behave:
16242
+ elif getattr(self, "is_behave", None):
16175
16243
  filename = sb_config.behave_scenario.filename
16176
16244
  filename = filename.split("/")[-1].split("\\")[-1]
16177
16245
  else:
@@ -16207,8 +16275,7 @@ class BaseCase(unittest.TestCase):
16207
16275
  sb_config._pdb_failure = True
16208
16276
  elif (
16209
16277
  self.is_pytest
16210
- and hasattr(sb_config, "_pdb_failure")
16211
- and sb_config._pdb_failure
16278
+ and getattr(sb_config, "_pdb_failure", None)
16212
16279
  and not has_exception
16213
16280
  ):
16214
16281
  return # Handle case where "pytest --pdb" marks failures as Passed
@@ -16378,7 +16445,7 @@ class BaseCase(unittest.TestCase):
16378
16445
  dash_pie = json.dumps(sb_config._saved_dashboard_pie)
16379
16446
  dash_pie_loc = constants.Dashboard.DASH_PIE
16380
16447
  pie_path = os.path.join(abs_path, dash_pie_loc)
16381
- pie_file = open(pie_path, "w+", encoding="utf-8")
16448
+ pie_file = open(pie_path, mode="w+", encoding="utf-8")
16382
16449
  pie_file.writelines(dash_pie)
16383
16450
  pie_file.close()
16384
16451
  DASH_PIE_PNG_1 = constants.Dashboard.get_dash_pie_1()
@@ -16538,7 +16605,7 @@ class BaseCase(unittest.TestCase):
16538
16605
  )
16539
16606
  abs_path = os.path.abspath(".")
16540
16607
  file_path = os.path.join(abs_path, "dashboard.html")
16541
- out_file = open(file_path, "w+", encoding="utf-8")
16608
+ out_file = open(file_path, mode="w+", encoding="utf-8")
16542
16609
  out_file.writelines(the_html)
16543
16610
  out_file.close()
16544
16611
  sb_config._dash_html = the_html
@@ -16551,7 +16618,7 @@ class BaseCase(unittest.TestCase):
16551
16618
  dash_json = json.dumps((_results, _display_id, _rt, _tlp, d_stats))
16552
16619
  dash_json_loc = constants.Dashboard.DASH_JSON
16553
16620
  dash_jsonpath = os.path.join(abs_path, dash_json_loc)
16554
- dash_json_file = open(dash_jsonpath, "w+", encoding="utf-8")
16621
+ dash_json_file = open(dash_jsonpath, mode="w+", encoding="utf-8")
16555
16622
  dash_json_file.writelines(dash_json)
16556
16623
  dash_json_file.close()
16557
16624
 
@@ -16602,7 +16669,7 @@ class BaseCase(unittest.TestCase):
16602
16669
  self.__check_scope()
16603
16670
  except Exception:
16604
16671
  return
16605
- if hasattr(self, "recorder_mode") and self.recorder_mode:
16672
+ if getattr(self, "recorder_mode", None):
16606
16673
  # In case tearDown() leaves the origin, save actions first.
16607
16674
  self.save_recorded_actions()
16608
16675
  if (
@@ -16797,7 +16864,7 @@ class BaseCase(unittest.TestCase):
16797
16864
  if not hasattr(self, "_using_sb_fixture") and self.__called_teardown:
16798
16865
  # This test already called tearDown()
16799
16866
  return
16800
- if hasattr(self, "recorder_mode") and self.recorder_mode:
16867
+ if getattr(self, "recorder_mode", None):
16801
16868
  page_actions._reconnect_if_disconnected(self.driver)
16802
16869
  try:
16803
16870
  self.__process_recorded_actions()
@@ -17032,7 +17099,7 @@ class BaseCase(unittest.TestCase):
17032
17099
  self.testcase_manager.update_testcase_log_url(data_payload)
17033
17100
  else:
17034
17101
  # (Pynose / Behave / Pure Python)
17035
- if hasattr(self, "is_behave") and self.is_behave:
17102
+ if getattr(self, "is_behave", None):
17036
17103
  if sb_config.behave_scenario.status.name == "failed":
17037
17104
  has_exception = True
17038
17105
  sb_config._has_exception = True
@@ -17092,8 +17159,8 @@ class BaseCase(unittest.TestCase):
17092
17159
  self._last_page_url = self.get_current_url()
17093
17160
  except Exception:
17094
17161
  self._last_page_url = "(Error: Unknown URL)"
17095
- if hasattr(self, "is_behave") and self.is_behave and has_exception:
17096
- if hasattr(sb_config, "pdb_option") and sb_config.pdb_option:
17162
+ if getattr(self, "is_behave", None) and has_exception:
17163
+ if getattr(sb_config, "pdb_option", None):
17097
17164
  if (
17098
17165
  hasattr(sb_config, "behave_step")
17099
17166
  and hasattr(sb_config.behave_step, "exc_traceback")
@@ -17101,24 +17168,14 @@ class BaseCase(unittest.TestCase):
17101
17168
  self.__activate_behave_post_mortem_debug_mode()
17102
17169
  if self._final_debug:
17103
17170
  self.__activate_debug_mode_in_teardown()
17104
- elif (
17105
- hasattr(sb_config, "_do_sb_post_mortem")
17106
- and sb_config._do_sb_post_mortem
17107
- ):
17171
+ elif getattr(sb_config, "_do_sb_post_mortem", None):
17108
17172
  self.__activate_sb_mgr_post_mortem_debug_mode()
17109
- elif (
17110
- hasattr(sb_config, "_do_sb_final_trace")
17111
- and sb_config._do_sb_final_trace
17112
- ):
17173
+ elif getattr(sb_config, "_do_sb_final_trace", None):
17113
17174
  self.__activate_debug_mode_in_teardown()
17114
17175
  # (Pynose / Behave / Pure Python) Close all open browser windows
17115
17176
  self.__quit_all_drivers()
17116
17177
  # Resume tearDown() for all test runners, (Pytest / Pynose / Behave)
17117
- if (
17118
- hasattr(self, "_xvfb_display")
17119
- and self._xvfb_display
17120
- and not self._reuse_session
17121
- ):
17178
+ if getattr(self, "_xvfb_display", None) and not self._reuse_session:
17122
17179
  # Stop the Xvfb virtual display launched from BaseCase
17123
17180
  try:
17124
17181
  if hasattr(self._xvfb_display, "stop"):
@@ -17130,16 +17187,9 @@ class BaseCase(unittest.TestCase):
17130
17187
  except Exception:
17131
17188
  pass
17132
17189
  if (
17133
- hasattr(sb_config, "_virtual_display")
17134
- and sb_config._virtual_display
17190
+ getattr(sb_config, "_virtual_display", None)
17135
17191
  and hasattr(sb_config._virtual_display, "stop")
17136
- and (
17137
- not hasattr(sb_config, "reuse_session")
17138
- or (
17139
- hasattr(sb_config, "reuse_session")
17140
- and not sb_config.reuse_session
17141
- )
17142
- )
17192
+ and not getattr(sb_config, "reuse_session", None)
17143
17193
  ):
17144
17194
  # CDP Mode may launch a 2nd Xvfb virtual display
17145
17195
  try: