seleniumbase 4.33.10__py3-none-any.whl → 4.33.12__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.
@@ -1,2 +1,2 @@
1
1
  # seleniumbase package
2
- __version__ = "4.33.10"
2
+ __version__ = "4.33.12"
@@ -2407,8 +2407,6 @@ def _set_firefox_options(
2407
2407
  options.set_preference("dom.webnotifications.enabled", False)
2408
2408
  options.set_preference("dom.disable_beforeunload", True)
2409
2409
  options.set_preference("browser.contentblocking.database.enabled", True)
2410
- options.set_preference("extensions.allowPrivateBrowsingByDefault", True)
2411
- options.set_preference("extensions.PrivateBrowsing.notification", False)
2412
2410
  options.set_preference("extensions.systemAddon.update.enabled", False)
2413
2411
  options.set_preference("extensions.update.autoUpdateDefault", False)
2414
2412
  options.set_preference("extensions.update.enabled", False)
@@ -15,6 +15,11 @@ if sys.version_info >= (3, 11):
15
15
  py311_patch2 = constants.PatchPy311.PATCH
16
16
 
17
17
 
18
+ def __is_cdp_swap_needed(driver):
19
+ """If the driver is disconnected, use a CDP method when available."""
20
+ return shared_utils.is_cdp_swap_needed(driver)
21
+
22
+
18
23
  def log_screenshot(test_logpath, driver, screenshot=None, get=False):
19
24
  screenshot_name = settings.SCREENSHOT_NAME
20
25
  screenshot_path = os.path.join(test_logpath, screenshot_name)
@@ -356,7 +361,11 @@ def log_page_source(test_logpath, driver, source=None):
356
361
  page_source = source
357
362
  else:
358
363
  try:
359
- page_source = driver.page_source
364
+ page_source = None
365
+ if __is_cdp_swap_needed(driver):
366
+ page_source = driver.cdp.get_page_source()
367
+ else:
368
+ page_source = driver.page_source
360
369
  page_source = get_html_source_with_base_href(driver, page_source)
361
370
  except Exception:
362
371
  source = constants.Warnings.PAGE_SOURCE_UNDEFINED
@@ -448,7 +457,11 @@ def get_test_name(test):
448
457
 
449
458
  def get_last_page(driver):
450
459
  try:
451
- last_page = driver.current_url
460
+ last_page = None
461
+ if __is_cdp_swap_needed(driver):
462
+ last_page = driver.cdp.get_current_url()
463
+ else:
464
+ last_page = driver.current_url
452
465
  except Exception:
453
466
  last_page = "[WARNING! Browser Not Open!]"
454
467
  if len(last_page) < 5:
@@ -127,14 +127,14 @@ class CDPMethods():
127
127
  def add_handler(self, event, handler):
128
128
  self.page.add_handler(event, handler)
129
129
 
130
- def find_element(
131
- self, selector, best_match=False, timeout=settings.SMALL_TIMEOUT
132
- ):
130
+ def find_element(self, selector, best_match=False, timeout=None):
133
131
  """Similar to select(), but also finds elements by text content.
134
132
  When using text-based searches, if best_match=False, then will
135
133
  find the first element with the text. If best_match=True, then
136
134
  if multiple elements have that text, then will use the element
137
135
  with the closest text-length to the text being searched for."""
136
+ if not timeout:
137
+ timeout = settings.SMALL_TIMEOUT
138
138
  self.__add_light_pause()
139
139
  selector = self.__convert_to_css_if_xpath(selector)
140
140
  early_failure = False
@@ -167,12 +167,12 @@ class CDPMethods():
167
167
  self.__slow_mode_pause_if_set()
168
168
  return element
169
169
 
170
- def find_element_by_text(
171
- self, text, tag_name=None, timeout=settings.SMALL_TIMEOUT
172
- ):
170
+ def find_element_by_text(self, text, tag_name=None, timeout=None):
173
171
  """Returns an element by matching text.
174
172
  Optionally, provide a tag_name to narrow down the search to an
175
173
  element with the given tag. (Eg: a, button, div, script, span)"""
174
+ if not timeout:
175
+ timeout = settings.SMALL_TIMEOUT
176
176
  self.__add_light_pause()
177
177
  time_now = time.time()
178
178
  self.assert_text(text, timeout=timeout)
@@ -218,7 +218,9 @@ class CDPMethods():
218
218
  % (text, tag_name, timeout, plural)
219
219
  )
220
220
 
221
- def find_all(self, selector, timeout=settings.SMALL_TIMEOUT):
221
+ def find_all(self, selector, timeout=None):
222
+ if not timeout:
223
+ timeout = settings.SMALL_TIMEOUT
222
224
  self.__add_light_pause()
223
225
  selector = self.__convert_to_css_if_xpath(selector)
224
226
  elements = self.loop.run_until_complete(
@@ -272,8 +274,10 @@ class CDPMethods():
272
274
  updated_elements.append(element)
273
275
  return updated_elements
274
276
 
275
- def select(self, selector, timeout=settings.SMALL_TIMEOUT):
277
+ def select(self, selector, timeout=None):
276
278
  """Similar to find_element(), but without text-based search."""
279
+ if not timeout:
280
+ timeout = settings.SMALL_TIMEOUT
277
281
  self.__add_light_pause()
278
282
  selector = self.__convert_to_css_if_xpath(selector)
279
283
  if (":contains(" in selector):
@@ -307,7 +311,9 @@ class CDPMethods():
307
311
  self.__slow_mode_pause_if_set()
308
312
  return element
309
313
 
310
- def select_all(self, selector, timeout=settings.SMALL_TIMEOUT):
314
+ def select_all(self, selector, timeout=None):
315
+ if not timeout:
316
+ timeout = settings.SMALL_TIMEOUT
311
317
  self.__add_light_pause()
312
318
  selector = self.__convert_to_css_if_xpath(selector)
313
319
  elements = self.loop.run_until_complete(
@@ -319,10 +325,14 @@ class CDPMethods():
319
325
  updated_elements.append(element)
320
326
  return updated_elements
321
327
 
322
- def find_elements(self, selector, timeout=settings.SMALL_TIMEOUT):
328
+ def find_elements(self, selector, timeout=None):
329
+ if not timeout:
330
+ timeout = settings.SMALL_TIMEOUT
323
331
  return self.select_all(selector, timeout=timeout)
324
332
 
325
- def find_visible_elements(self, selector, timeout=settings.SMALL_TIMEOUT):
333
+ def find_visible_elements(self, selector, timeout=None):
334
+ if not timeout:
335
+ timeout = settings.SMALL_TIMEOUT
326
336
  visible_elements = []
327
337
  elements = self.select_all(selector, timeout=timeout)
328
338
  for element in elements:
@@ -519,7 +529,20 @@ class CDPMethods():
519
529
  try:
520
530
  return element.get_js_attributes()[attribute]
521
531
  except Exception:
522
- return None
532
+ if not attribute:
533
+ raise
534
+ try:
535
+ attribute_str = element.get_js_attributes()
536
+ locate = ' %s="' % attribute
537
+ if locate in attribute_str.outerHTML:
538
+ outer_html = attribute_str.outerHTML
539
+ attr_start = outer_html.find(locate) + len(locate)
540
+ attr_end = outer_html.find('"', attr_start)
541
+ value = outer_html[attr_start:attr_end]
542
+ return value
543
+ except Exception:
544
+ pass
545
+ return None
523
546
 
524
547
  def __get_x_scroll_offset(self):
525
548
  x_scroll_offset = self.loop.run_until_complete(
@@ -574,12 +597,12 @@ class CDPMethods():
574
597
  driver.cookies.load(*args, **kwargs)
575
598
  )
576
599
 
577
- def clear_cookies(self, *args, **kwargs):
600
+ def clear_cookies(self):
578
601
  driver = self.driver
579
602
  if hasattr(driver, "cdp_base"):
580
603
  driver = driver.cdp_base
581
604
  return self.loop.run_until_complete(
582
- driver.cookies.clear(*args, **kwargs)
605
+ driver.cookies.clear()
583
606
  )
584
607
 
585
608
  def sleep(self, seconds):
@@ -603,7 +626,9 @@ class CDPMethods():
603
626
  self.page.evaluate(js_code)
604
627
  )
605
628
 
606
- def click(self, selector, timeout=settings.SMALL_TIMEOUT):
629
+ def click(self, selector, timeout=None):
630
+ if not timeout:
631
+ timeout = settings.SMALL_TIMEOUT
607
632
  self.__slow_mode_pause_if_set()
608
633
  element = self.find_element(selector, timeout=timeout)
609
634
  element.scroll_into_view()
@@ -620,11 +645,12 @@ class CDPMethods():
620
645
 
621
646
  def click_if_visible(self, selector):
622
647
  if self.is_element_visible(selector):
623
- element = self.find_element(selector)
624
- element.scroll_into_view()
625
- element.click()
626
- self.__slow_mode_pause_if_set()
627
- self.loop.run_until_complete(self.page.wait())
648
+ with suppress(Exception):
649
+ element = self.find_element(selector, timeout=0)
650
+ element.scroll_into_view()
651
+ element.click()
652
+ self.__slow_mode_pause_if_set()
653
+ self.loop.run_until_complete(self.page.wait())
628
654
 
629
655
  def click_visible_elements(self, selector, limit=0):
630
656
  """Finds all matching page elements and clicks visible ones in order.
@@ -658,8 +684,10 @@ class CDPMethods():
658
684
  except Exception:
659
685
  break
660
686
 
661
- def mouse_click(self, selector, timeout=settings.SMALL_TIMEOUT):
687
+ def mouse_click(self, selector, timeout=None):
662
688
  """(Attempt simulating a mouse click)"""
689
+ if not timeout:
690
+ timeout = settings.SMALL_TIMEOUT
663
691
  self.__slow_mode_pause_if_set()
664
692
  element = self.find_element(selector, timeout=timeout)
665
693
  element.scroll_into_view()
@@ -757,7 +785,9 @@ class CDPMethods():
757
785
  with suppress(Exception):
758
786
  self.loop.run_until_complete(self.page.evaluate(js_code))
759
787
 
760
- def send_keys(self, selector, text, timeout=settings.SMALL_TIMEOUT):
788
+ def send_keys(self, selector, text, timeout=None):
789
+ if not timeout:
790
+ timeout = settings.SMALL_TIMEOUT
761
791
  self.__slow_mode_pause_if_set()
762
792
  element = self.select(selector, timeout=timeout)
763
793
  element.scroll_into_view()
@@ -767,8 +797,10 @@ class CDPMethods():
767
797
  self.__slow_mode_pause_if_set()
768
798
  self.loop.run_until_complete(self.page.wait())
769
799
 
770
- def press_keys(self, selector, text, timeout=settings.SMALL_TIMEOUT):
800
+ def press_keys(self, selector, text, timeout=None):
771
801
  """Similar to send_keys(), but presses keys at human speed."""
802
+ if not timeout:
803
+ timeout = settings.SMALL_TIMEOUT
772
804
  self.__slow_mode_pause_if_set()
773
805
  element = self.select(selector, timeout=timeout)
774
806
  element.scroll_into_view()
@@ -785,8 +817,10 @@ class CDPMethods():
785
817
  self.__slow_mode_pause_if_set()
786
818
  self.loop.run_until_complete(self.page.wait())
787
819
 
788
- def type(self, selector, text, timeout=settings.SMALL_TIMEOUT):
820
+ def type(self, selector, text, timeout=None):
789
821
  """Similar to send_keys(), but clears the text field first."""
822
+ if not timeout:
823
+ timeout = settings.SMALL_TIMEOUT
790
824
  self.__slow_mode_pause_if_set()
791
825
  element = self.select(selector, timeout=timeout)
792
826
  element.scroll_into_view()
@@ -798,8 +832,10 @@ class CDPMethods():
798
832
  self.__slow_mode_pause_if_set()
799
833
  self.loop.run_until_complete(self.page.wait())
800
834
 
801
- def set_value(self, selector, text, timeout=settings.SMALL_TIMEOUT):
835
+ def set_value(self, selector, text, timeout=None):
802
836
  """Similar to send_keys(), but clears the text field first."""
837
+ if not timeout:
838
+ timeout = settings.SMALL_TIMEOUT
803
839
  self.__slow_mode_pause_if_set()
804
840
  selector = self.__convert_to_css_if_xpath(selector)
805
841
  element = self.select(selector, timeout=timeout)
@@ -1022,7 +1058,9 @@ class CDPMethods():
1022
1058
  coordinates["y"] = y if y else 0
1023
1059
  return coordinates
1024
1060
 
1025
- def get_element_rect(self, selector, timeout=settings.SMALL_TIMEOUT):
1061
+ def get_element_rect(self, selector, timeout=None):
1062
+ if not timeout:
1063
+ timeout = settings.SMALL_TIMEOUT
1026
1064
  selector = self.__convert_to_css_if_xpath(selector)
1027
1065
  self.select(selector, timeout=timeout)
1028
1066
  self.__add_light_pause()
@@ -1035,23 +1073,29 @@ class CDPMethods():
1035
1073
  )
1036
1074
  return coordinates
1037
1075
 
1038
- def get_element_size(self, selector):
1039
- element_rect = self.get_element_rect(selector)
1076
+ def get_element_size(self, selector, timeout=None):
1077
+ if not timeout:
1078
+ timeout = settings.SMALL_TIMEOUT
1079
+ element_rect = self.get_element_rect(selector, timeout=timeout)
1040
1080
  coordinates = {}
1041
1081
  coordinates["width"] = element_rect["width"]
1042
1082
  coordinates["height"] = element_rect["height"]
1043
1083
  return coordinates
1044
1084
 
1045
- def get_element_position(self, selector):
1046
- element_rect = self.get_element_rect(selector)
1085
+ def get_element_position(self, selector, timeout=None):
1086
+ if not timeout:
1087
+ timeout = settings.SMALL_TIMEOUT
1088
+ element_rect = self.get_element_rect(selector, timeout=timeout)
1047
1089
  coordinates = {}
1048
1090
  coordinates["x"] = element_rect["x"]
1049
1091
  coordinates["y"] = element_rect["y"]
1050
1092
  return coordinates
1051
1093
 
1052
- def get_gui_element_rect(self, selector):
1094
+ def get_gui_element_rect(self, selector, timeout=None):
1053
1095
  """(Coordinates are relative to the screen. Not the window.)"""
1054
- element_rect = self.get_element_rect(selector)
1096
+ if not timeout:
1097
+ timeout = settings.SMALL_TIMEOUT
1098
+ element_rect = self.get_element_rect(selector, timeout=timeout)
1055
1099
  e_width = element_rect["width"]
1056
1100
  e_height = element_rect["height"]
1057
1101
  window_rect = self.get_window_rect()
@@ -1065,9 +1109,11 @@ class CDPMethods():
1065
1109
  y = y + window_rect["scrollY"]
1066
1110
  return ({"height": e_height, "width": e_width, "x": x, "y": y})
1067
1111
 
1068
- def get_gui_element_center(self, selector):
1112
+ def get_gui_element_center(self, selector, timeout=None):
1069
1113
  """(Coordinates are relative to the screen. Not the window.)"""
1070
- element_rect = self.get_gui_element_rect(selector)
1114
+ if not timeout:
1115
+ timeout = settings.SMALL_TIMEOUT
1116
+ element_rect = self.get_gui_element_rect(selector, timeout=timeout)
1071
1117
  e_width = element_rect["width"]
1072
1118
  e_height = element_rect["height"]
1073
1119
  e_x = element_rect["x"]
@@ -1094,7 +1140,14 @@ class CDPMethods():
1094
1140
  )
1095
1141
 
1096
1142
  def get_element_attribute(self, selector, attribute):
1097
- return self.get_element_attributes(selector)[attribute]
1143
+ attributes = self.get_element_attributes(selector)
1144
+ with suppress(Exception):
1145
+ return attributes[attribute]
1146
+ locate = ' %s="' % attribute
1147
+ value = self.get_attribute(selector, attribute)
1148
+ if not value and locate not in attributes:
1149
+ raise KeyError(attribute)
1150
+ return value
1098
1151
 
1099
1152
  def get_attribute(self, selector, attribute):
1100
1153
  return self.find_element(selector).get_attribute(attribute)
@@ -1608,9 +1661,9 @@ class CDPMethods():
1608
1661
  return True
1609
1662
  return False
1610
1663
 
1611
- def wait_for_element_visible(
1612
- self, selector, timeout=settings.SMALL_TIMEOUT
1613
- ):
1664
+ def wait_for_element_visible(self, selector, timeout=None):
1665
+ if not timeout:
1666
+ timeout = settings.SMALL_TIMEOUT
1614
1667
  try:
1615
1668
  self.select(selector, timeout=timeout)
1616
1669
  except Exception:
@@ -1621,8 +1674,10 @@ class CDPMethods():
1621
1674
  time.sleep(0.1)
1622
1675
  raise Exception("Element {%s} was not visible!" % selector)
1623
1676
 
1624
- def assert_element(self, selector, timeout=settings.SMALL_TIMEOUT):
1677
+ def assert_element(self, selector, timeout=None):
1625
1678
  """Same as assert_element_visible()"""
1679
+ if not timeout:
1680
+ timeout = settings.SMALL_TIMEOUT
1626
1681
  try:
1627
1682
  self.select(selector, timeout=timeout)
1628
1683
  except Exception:
@@ -1633,8 +1688,10 @@ class CDPMethods():
1633
1688
  time.sleep(0.1)
1634
1689
  raise Exception("Element {%s} was not visible!" % selector)
1635
1690
 
1636
- def assert_element_visible(self, selector, timeout=settings.SMALL_TIMEOUT):
1691
+ def assert_element_visible(self, selector, timeout=None):
1637
1692
  """Same as assert_element()"""
1693
+ if not timeout:
1694
+ timeout = settings.SMALL_TIMEOUT
1638
1695
  try:
1639
1696
  self.select(selector, timeout=timeout)
1640
1697
  except Exception:
@@ -1645,16 +1702,20 @@ class CDPMethods():
1645
1702
  time.sleep(0.1)
1646
1703
  raise Exception("Element {%s} was not visible!" % selector)
1647
1704
 
1648
- def assert_element_present(self, selector, timeout=settings.SMALL_TIMEOUT):
1705
+ def assert_element_present(self, selector, timeout=None):
1649
1706
  """Assert element is present in the DOM. (Visibility NOT required)"""
1707
+ if not timeout:
1708
+ timeout = settings.SMALL_TIMEOUT
1650
1709
  try:
1651
1710
  self.select(selector, timeout=timeout)
1652
1711
  except Exception:
1653
1712
  raise Exception("Element {%s} was not found!" % selector)
1654
1713
  return True
1655
1714
 
1656
- def assert_element_absent(self, selector, timeout=settings.SMALL_TIMEOUT):
1715
+ def assert_element_absent(self, selector, timeout=None):
1657
1716
  """Assert element is not present in the DOM."""
1717
+ if not timeout:
1718
+ timeout = settings.SMALL_TIMEOUT
1658
1719
  start_ms = time.time() * 1000.0
1659
1720
  stop_ms = start_ms + (timeout * 1000.0)
1660
1721
  for i in range(int(timeout * 10)):
@@ -1672,10 +1733,10 @@ class CDPMethods():
1672
1733
  % (selector, timeout, plural)
1673
1734
  )
1674
1735
 
1675
- def assert_element_not_visible(
1676
- self, selector, timeout=settings.SMALL_TIMEOUT
1677
- ):
1736
+ def assert_element_not_visible(self, selector, timeout=None):
1678
1737
  """Assert element is not visible on page. (May still be in DOM)"""
1738
+ if not timeout:
1739
+ timeout = settings.SMALL_TIMEOUT
1679
1740
  start_ms = time.time() * 1000.0
1680
1741
  stop_ms = start_ms + (timeout * 1000.0)
1681
1742
  for i in range(int(timeout * 10)):
@@ -1770,9 +1831,9 @@ class CDPMethods():
1770
1831
  if expected not in actual:
1771
1832
  raise Exception(error % (expected, actual))
1772
1833
 
1773
- def assert_text(
1774
- self, text, selector="body", timeout=settings.SMALL_TIMEOUT
1775
- ):
1834
+ def assert_text(self, text, selector="body", timeout=None):
1835
+ if not timeout:
1836
+ timeout = settings.SMALL_TIMEOUT
1776
1837
  start_ms = time.time() * 1000.0
1777
1838
  stop_ms = start_ms + (timeout * 1000.0)
1778
1839
  text = text.strip()
@@ -1795,9 +1856,9 @@ class CDPMethods():
1795
1856
  % (text, selector, element.text_all)
1796
1857
  )
1797
1858
 
1798
- def assert_exact_text(
1799
- self, text, selector="body", timeout=settings.SMALL_TIMEOUT
1800
- ):
1859
+ def assert_exact_text(self, text, selector="body", timeout=None):
1860
+ if not timeout:
1861
+ timeout = settings.SMALL_TIMEOUT
1801
1862
  start_ms = time.time() * 1000.0
1802
1863
  stop_ms = start_ms + (timeout * 1000.0)
1803
1864
  text = text.strip()
@@ -3416,6 +3416,14 @@ class BaseCase(unittest.TestCase):
3416
3416
  self.activate_jquery()
3417
3417
  return self.driver.execute_script(script, *args, **kwargs)
3418
3418
 
3419
+ def get_element_at_x_y(self, x, y):
3420
+ """Return element at current window's x,y coordinates."""
3421
+ self.__check_scope()
3422
+ self._check_browser()
3423
+ return self.execute_script(
3424
+ "return document.elementFromPoint(%s, %s);" % (x, y)
3425
+ )
3426
+
3419
3427
  def get_gui_element_rect(self, selector, by="css selector"):
3420
3428
  """Very similar to element.rect, but the x, y coordinates are
3421
3429
  relative to the entire screen, rather than the browser window.
@@ -4484,7 +4492,8 @@ class BaseCase(unittest.TestCase):
4484
4492
  @Params
4485
4493
  name - The file name to save the current page's HTML to.
4486
4494
  folder - The folder to save the file to. (Default = current folder)"""
4487
- self.wait_for_ready_state_complete()
4495
+ if not self.__is_cdp_swap_needed():
4496
+ self.wait_for_ready_state_complete()
4488
4497
  return page_actions.save_page_source(self.driver, name, folder)
4489
4498
 
4490
4499
  def save_cookies(self, name="cookies.txt"):
@@ -4542,6 +4551,9 @@ class BaseCase(unittest.TestCase):
4542
4551
  def delete_all_cookies(self):
4543
4552
  """Deletes all cookies in the web browser.
4544
4553
  Does NOT delete the saved cookies file."""
4554
+ if self.__is_cdp_swap_needed():
4555
+ self.cdp.clear_cookies()
4556
+ return
4545
4557
  self.wait_for_ready_state_complete()
4546
4558
  self.driver.delete_all_cookies()
4547
4559
  if self.recorder_mode:
@@ -4553,7 +4565,6 @@ class BaseCase(unittest.TestCase):
4553
4565
  def delete_saved_cookies(self, name="cookies.txt"):
4554
4566
  """Deletes the cookies file from the "saved_cookies" folder.
4555
4567
  Does NOT delete the cookies from the web browser."""
4556
- self.wait_for_ready_state_complete()
4557
4568
  if name.endswith("/"):
4558
4569
  raise Exception("Invalid filename for Cookies!")
4559
4570
  if "/" in name:
@@ -4592,14 +4603,20 @@ class BaseCase(unittest.TestCase):
4592
4603
  return json.loads(json_cookies)
4593
4604
 
4594
4605
  def get_cookie(self, name):
4606
+ self.__check_scope()
4607
+ self._check_browser()
4595
4608
  return self.driver.get_cookie(name)
4596
4609
 
4597
4610
  def get_cookies(self):
4611
+ self.__check_scope()
4612
+ self._check_browser()
4598
4613
  return self.driver.get_cookies()
4599
4614
 
4600
4615
  def get_cookie_string(self):
4616
+ self.__check_scope()
4601
4617
  if self.__is_cdp_swap_needed():
4602
4618
  return self.cdp.get_cookie_string()
4619
+ self._check_browser()
4603
4620
  return self.execute_script("return document.cookie;")
4604
4621
 
4605
4622
  def add_cookie(self, cookie_dict, expiry=False):
@@ -4614,6 +4631,8 @@ class BaseCase(unittest.TestCase):
4614
4631
  If expiry > 0: Set "expiry" to expiry minutes in the future.
4615
4632
  If expiry == True: Set "expiry" to 24 hours in the future.
4616
4633
  """
4634
+ self.__check_scope()
4635
+ self._check_browser()
4617
4636
  cookie = cookie_dict
4618
4637
  if "domain" in cookie:
4619
4638
  origin = self.get_origin()
@@ -4638,6 +4657,8 @@ class BaseCase(unittest.TestCase):
4638
4657
  If expiry > 0: Set "expiry" to expiry minutes in the future.
4639
4658
  If expiry == True: Set "expiry" to 24 hours in the future.
4640
4659
  """
4660
+ self.__check_scope()
4661
+ self._check_browser()
4641
4662
  origin = self.get_origin()
4642
4663
  trim_origin = origin.split("://")[-1]
4643
4664
  for cookie in cookies:
@@ -1342,7 +1342,8 @@ def save_page_source(driver, name, folder=None):
1342
1342
  """
1343
1343
  from seleniumbase.core import log_helper
1344
1344
 
1345
- _reconnect_if_disconnected(driver)
1345
+ if not __is_cdp_swap_needed(driver):
1346
+ _reconnect_if_disconnected(driver) # If disconnected without CDP
1346
1347
  if not name.endswith(".html"):
1347
1348
  name = name + ".html"
1348
1349
  if folder:
@@ -1353,7 +1354,11 @@ def save_page_source(driver, name, folder=None):
1353
1354
  html_file_path = os.path.join(file_path, name)
1354
1355
  else:
1355
1356
  html_file_path = name
1356
- page_source = driver.page_source
1357
+ page_source = None
1358
+ if __is_cdp_swap_needed(driver):
1359
+ page_source = driver.cdp.get_page_source()
1360
+ else:
1361
+ page_source = driver.page_source
1357
1362
  html_file = codecs.open(html_file_path, "w+", "utf-8")
1358
1363
  rendered_source = log_helper.get_html_source_with_base_href(
1359
1364
  driver, page_source
@@ -31,12 +31,12 @@ class ContraDict(dict):
31
31
 
32
32
  def __init__(self, *args, **kwargs):
33
33
  super().__init__()
34
- silent = kwargs.pop("silent", False)
34
+ # silent = kwargs.pop("silent", False)
35
35
  _ = dict(*args, **kwargs)
36
36
 
37
37
  super().__setattr__("__dict__", self)
38
38
  for k, v in _.items():
39
- _check_key(k, self, False, silent)
39
+ _check_key(k, self, False, True)
40
40
  super().__setitem__(k, _wrap(self.__class__, v))
41
41
 
42
42
  def __setitem__(self, key, value):
@@ -90,7 +90,7 @@ _warning_names_message = """\n\
90
90
 
91
91
 
92
92
  def _check_key(
93
- key: str, mapping: _Mapping, boolean: bool = False, silent=False
93
+ key: str, mapping: _Mapping, boolean: bool = False, silent=True
94
94
  ):
95
95
  """Checks `key` and warns if needed.
96
96
  :param key:
@@ -10,6 +10,7 @@ import pathlib
10
10
  import pickle
11
11
  import re
12
12
  import shutil
13
+ import time
13
14
  import urllib.parse
14
15
  import urllib.request
15
16
  import warnings
@@ -30,8 +31,6 @@ def get_registered_instances():
30
31
 
31
32
 
32
33
  def deconstruct_browser():
33
- import time
34
-
35
34
  for _ in __registered__instances__:
36
35
  if not _.stopped:
37
36
  _.stop()
@@ -117,8 +116,13 @@ class Browser:
117
116
  port=port,
118
117
  **kwargs,
119
118
  )
120
- instance = cls(config)
121
- await instance.start()
119
+ try:
120
+ instance = cls(config)
121
+ await instance.start()
122
+ except Exception:
123
+ time.sleep(0.15)
124
+ instance = cls(config)
125
+ await instance.start()
122
126
  return instance
123
127
 
124
128
  def __init__(self, config: Config, **kwargs):
@@ -379,8 +383,6 @@ class Browser:
379
383
  --------------------------------
380
384
  Failed to connect to the browser
381
385
  --------------------------------
382
- Possibly because you are running as "root".
383
- If so, you may need to use no_sandbox=True.
384
386
  """
385
387
  )
386
388
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: seleniumbase
3
- Version: 4.33.10
3
+ Version: 4.33.12
4
4
  Summary: A complete web automation framework for end-to-end testing.
5
5
  Home-page: https://github.com/seleniumbase/SeleniumBase
6
6
  Author: Michael Mintz
@@ -64,8 +64,8 @@ Requires-Dist: packaging>=24.2
64
64
  Requires-Dist: setuptools~=70.2; python_version < "3.10"
65
65
  Requires-Dist: setuptools>=75.6.0; python_version >= "3.10"
66
66
  Requires-Dist: wheel>=0.45.1
67
- Requires-Dist: attrs>=24.2.0
68
- Requires-Dist: certifi>=2024.8.30
67
+ Requires-Dist: attrs>=24.3.0
68
+ Requires-Dist: certifi>=2024.12.14
69
69
  Requires-Dist: exceptiongroup>=1.2.2
70
70
  Requires-Dist: websockets~=13.1; python_version < "3.9"
71
71
  Requires-Dist: websockets>=14.1; python_version >= "3.9"
@@ -75,7 +75,10 @@ Requires-Dist: mycdp>=1.1.0
75
75
  Requires-Dist: pynose>=1.5.3
76
76
  Requires-Dist: platformdirs>=4.3.6
77
77
  Requires-Dist: typing-extensions>=4.12.2
78
- Requires-Dist: sbvirtualdisplay>=1.3.1
78
+ Requires-Dist: sbvirtualdisplay>=1.4.0
79
+ Requires-Dist: MarkupSafe==2.1.5; python_version < "3.9"
80
+ Requires-Dist: MarkupSafe>=3.0.2; python_version >= "3.9"
81
+ Requires-Dist: Jinja2>=3.1.5
79
82
  Requires-Dist: six>=1.17.0
80
83
  Requires-Dist: parse>=1.20.2
81
84
  Requires-Dist: parse-type>=0.6.4
@@ -87,9 +90,9 @@ Requires-Dist: tabcompleter>=1.4.0
87
90
  Requires-Dist: pdbp>=1.6.1
88
91
  Requires-Dist: idna==3.10
89
92
  Requires-Dist: chardet==5.2.0
90
- Requires-Dist: charset-normalizer==3.4.0
93
+ Requires-Dist: charset-normalizer==3.4.1
91
94
  Requires-Dist: urllib3<2,>=1.26.20; python_version < "3.10"
92
- Requires-Dist: urllib3<2.3.0,>=1.26.20; python_version >= "3.10"
95
+ Requires-Dist: urllib3<2.4.0,>=1.26.20; python_version >= "3.10"
93
96
  Requires-Dist: requests==2.32.3
94
97
  Requires-Dist: sniffio==1.3.1
95
98
  Requires-Dist: h11==0.14.0
@@ -236,21 +239,76 @@ Requires-Dist: zstandard==0.23.0; extra == "selenium-wire"
236
239
 
237
240
  📚 Learn from [**over 200 examples** in the **SeleniumBase/examples/** folder](https://github.com/seleniumbase/SeleniumBase/tree/master/examples).
238
241
 
239
- 👤 Note that <span translate="no">SeleniumBase</span> <a translate="no" href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/uc_mode.md"><b>UC Mode</b> (Stealth Mode) has its own ReadMe</a>.
242
+ 🐙 Note that <a translate="no" href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/uc_mode.md"><b>UC Mode</b></a> / <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md"><b>CDP Mode</b></a> (Stealth Mode) have their own ReadMe files.
240
243
 
241
- 🐙 Also note that Seleniumbase <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md"><b>CDP Mode</b> has its own separate ReadMe</a>.
244
+ ℹ️ Most scripts run with raw <code translate="no"><b>python</b></code>, although some scripts use <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md">Syntax Formats</a> that expect <a href="https://docs.pytest.org/en/latest/how-to/usage.html" translate="no"><b>pytest</b></a> (a Python unit-testing framework included with SeleniumBase that can discover, collect, and run tests automatically).
242
245
 
243
- ℹ️ Scripts can be called via <code translate="no"><b>python</b></code>, although some <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md">Syntax Formats</a> expect <a href="https://docs.pytest.org/en/latest/how-to/usage.html" translate="no"><b>pytest</b></a> (a Python unit-testing framework included with SeleniumBase that can discover, collect, and run tests automatically).
246
+ --------
244
247
 
245
- <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py">my_first_test.py</a>, which tests login, shopping, and checkout:</p>
248
+ <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_google.py">raw_google.py</a>, which performs a Google search:</p>
246
249
 
247
- ```bash
248
- pytest my_first_test.py
250
+ ```python
251
+ from seleniumbase import SB
252
+
253
+ with SB(test=True) as sb:
254
+ sb.open("https://google.com/ncr")
255
+ sb.type('[title="Search"]', "SeleniumBase GitHub page\n")
256
+ sb.click('[href*="github.com/seleniumbase/"]')
257
+ sb.save_screenshot_to_logs() # ./latest_logs/
258
+ print(sb.get_page_title())
249
259
  ```
250
260
 
251
- <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py"><img src="https://seleniumbase.github.io/cdn/gif/fast_swag_2.gif" alt="SeleniumBase Test" title="SeleniumBase Test" width="520" /></a>
261
+ > `python raw_google.py`
262
+
263
+ <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_google.py"><img src="https://seleniumbase.github.io/cdn/gif/google_search.gif" alt="SeleniumBase Test" title="SeleniumBase Test" width="420" /></a>
264
+
265
+ --------
266
+
267
+ <p align="left">📗 Here's an example of bypassing Cloudflare's challenge page: <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_gitlab.py">SeleniumBase/examples/cdp_mode/raw_gitlab.py</a></p>
252
268
 
253
- > ``pytest`` uses ``--chrome`` by default unless set differently.
269
+ ```python
270
+ from seleniumbase import SB
271
+
272
+ with SB(uc=True, test=True, locale_code="en") as sb:
273
+ url = "https://gitlab.com/users/sign_in"
274
+ sb.activate_cdp_mode(url)
275
+ sb.uc_gui_click_captcha()
276
+ sb.sleep(2)
277
+ ```
278
+
279
+ <img src="https://seleniumbase.github.io/other/cf_sec.jpg" title="SeleniumBase" width="332"> <img src="https://seleniumbase.github.io/other/gitlab_bypass.png" title="SeleniumBase" width="288">
280
+
281
+ --------
282
+
283
+ <p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_get_swag.py">test_get_swag.py</a>, which tests an e-commerce site:</p>
284
+
285
+ ```python
286
+ from seleniumbase import BaseCase
287
+ BaseCase.main(__name__, __file__) # Call pytest
288
+
289
+ class MyTestClass(BaseCase):
290
+ def test_swag_labs(self):
291
+ self.open("https://www.saucedemo.com")
292
+ self.type("#user-name", "standard_user")
293
+ self.type("#password", "secret_sauce\n")
294
+ self.assert_element("div.inventory_list")
295
+ self.click('button[name*="backpack"]')
296
+ self.click("#shopping_cart_container a")
297
+ self.assert_text("Backpack", "div.cart_item")
298
+ self.click("button#checkout")
299
+ self.type("input#first-name", "SeleniumBase")
300
+ self.type("input#last-name", "Automation")
301
+ self.type("input#postal-code", "77123")
302
+ self.click("input#continue")
303
+ self.click("button#finish")
304
+ self.assert_text("Thank you for your order!")
305
+ ```
306
+
307
+ > `pytest test_get_swag.py`
308
+
309
+ <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_get_swag.py"><img src="https://seleniumbase.github.io/cdn/gif/fast_swag_2.gif" alt="SeleniumBase Test" title="SeleniumBase Test" width="480" /></a>
310
+
311
+ > (The default browser is ``--chrome`` if not set.)
254
312
 
255
313
  --------
256
314
 
@@ -260,7 +318,7 @@ pytest my_first_test.py
260
318
  pytest test_coffee_cart.py --demo
261
319
  ```
262
320
 
263
- <p align="left"><a href="https://seleniumbase.io/coffee/" target="_blank"><img src="https://seleniumbase.github.io/cdn/gif/coffee_cart.gif" width="520" alt="SeleniumBase Coffee Cart Test" title="SeleniumBase Coffee Cart Test" /></a></p>
321
+ <p align="left"><a href="https://seleniumbase.io/coffee/" target="_blank"><img src="https://seleniumbase.github.io/cdn/gif/coffee_cart.gif" width="480" alt="SeleniumBase Coffee Cart Test" title="SeleniumBase Coffee Cart Test" /></a></p>
264
322
 
265
323
  > <p>(<code translate="no">--demo</code> mode slows down tests and highlights actions)</p>
266
324
 
@@ -274,7 +332,7 @@ pytest test_coffee_cart.py --demo
274
332
  pytest test_demo_site.py
275
333
  ```
276
334
 
277
- <p align="left"><a href="https://seleniumbase.io/demo_page" target="_blank"><img src="https://seleniumbase.github.io/cdn/gif/demo_page_5.gif" width="520" alt="SeleniumBase Example" title="SeleniumBase Example" /></a></p>
335
+ <p align="left"><a href="https://seleniumbase.io/demo_page" target="_blank"><img src="https://seleniumbase.github.io/cdn/gif/demo_page_5.gif" width="480" alt="SeleniumBase Example" title="SeleniumBase Example" /></a></p>
278
336
 
279
337
  > Easy to type, click, select, toggle, drag & drop, and more.
280
338
 
@@ -344,7 +402,7 @@ With raw Selenium, that requires more code:<br />
344
402
 
345
403
  <p>📚 <b>Learn about different ways of writing tests:</b></p>
346
404
 
347
- <p align="left">📘📝 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_simple_login.py">test_simple_login.py</a>, which uses <code translate="no"><a href="https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/fixtures/base_case.py">BaseCase</a></code> class inheritance, and runs with <a href="https://docs.pytest.org/en/latest/how-to/usage.html">pytest</a> or <a href="https://github.com/mdmintz/pynose">pynose</a>. (Use <code translate="no">self.driver</code> to access Selenium's raw <code translate="no">driver</code>.)</p>
405
+ <p align="left">📗📝 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_simple_login.py">test_simple_login.py</a>, which uses <code translate="no"><a href="https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/fixtures/base_case.py">BaseCase</a></code> class inheritance, and runs with <a href="https://docs.pytest.org/en/latest/how-to/usage.html">pytest</a> or <a href="https://github.com/mdmintz/pynose">pynose</a>. (Use <code translate="no">self.driver</code> to access Selenium's raw <code translate="no">driver</code>.)</p>
348
406
 
349
407
  ```python
350
408
  from seleniumbase import BaseCase
@@ -363,22 +421,7 @@ class TestSimpleLogin(BaseCase):
363
421
  self.assert_text("signed out", "#top_message")
364
422
  ```
365
423
 
366
- <p align="left">📗📝 Here's a test from <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/sb_fixture_tests.py">sb_fixture_tests.py</a>, which uses the <b><code translate="no">sb</code></b> <code translate="no">pytest</code> fixture. Runs with <a href="https://docs.pytest.org/en/latest/how-to/usage.html">pytest</a>. (Use <code translate="no">sb.driver</code> to access Selenium's raw <code translate="no">driver</code>.)</p>
367
-
368
- ```python
369
- def test_sb_fixture_with_no_class(sb):
370
- sb.open("seleniumbase.io/simple/login")
371
- sb.type("#username", "demo_user")
372
- sb.type("#password", "secret_pass")
373
- sb.click('a:contains("Sign in")')
374
- sb.assert_exact_text("Welcome!", "h1")
375
- sb.assert_element("img#image1")
376
- sb.highlight("#image1")
377
- sb.click_link("Sign out")
378
- sb.assert_text("signed out", "#top_message")
379
- ```
380
-
381
- <p align="left">📙📝 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_login_sb.py">raw_login_sb.py</a>, which uses the <b><code translate="no">SB</code></b> Context Manager. Runs with pure <code translate="no">python</code>. (Use <code translate="no">sb.driver</code> to access Selenium's raw <code translate="no">driver</code>.)</p>
424
+ <p align="left">📘📝 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_login_sb.py">raw_login_sb.py</a>, which uses the <b><code translate="no">SB</code></b> Context Manager. Runs with pure <code translate="no">python</code>. (Use <code translate="no">sb.driver</code> to access Selenium's raw <code translate="no">driver</code>.)</p>
382
425
 
383
426
  ```python
384
427
  from seleniumbase import SB
@@ -395,24 +438,7 @@ with SB() as sb:
395
438
  sb.assert_text("signed out", "#top_message")
396
439
  ```
397
440
 
398
- <p align="left">📔📝 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_login_context.py">raw_login_context.py</a>, which uses the <b><code translate="no">DriverContext</code></b> Manager. Runs with pure <code translate="no">python</code>. (The <code translate="no">driver</code> is an improved version of Selenium's raw <code translate="no">driver</code>, with more methods.)</p>
399
-
400
- ```python
401
- from seleniumbase import DriverContext
402
-
403
- with DriverContext() as driver:
404
- driver.open("seleniumbase.io/simple/login")
405
- driver.type("#username", "demo_user")
406
- driver.type("#password", "secret_pass")
407
- driver.click('a:contains("Sign in")')
408
- driver.assert_exact_text("Welcome!", "h1")
409
- driver.assert_element("img#image1")
410
- driver.highlight("#image1")
411
- driver.click_link("Sign out")
412
- driver.assert_text("signed out", "#top_message")
413
- ```
414
-
415
- <p align="left">📔📝 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_login_driver.py">raw_login_driver.py</a>, which uses the <b><code translate="no">Driver</code></b> Manager. Runs with pure <code translate="no">python</code>. (The <code>driver</code> is an improved version of Selenium's raw <code translate="no">driver</code>, with more methods.)</p>
441
+ <p align="left">📙📝 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_login_driver.py">raw_login_driver.py</a>, which uses the <b><code translate="no">Driver</code></b> Manager. Runs with pure <code translate="no">python</code>. (The <code>driver</code> is an improved version of Selenium's raw <code translate="no">driver</code>, with more methods.)</p>
416
442
 
417
443
  ```python
418
444
  from seleniumbase import Driver
@@ -432,23 +458,6 @@ finally:
432
458
  driver.quit()
433
459
  ```
434
460
 
435
- <p align="left">📕📝 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/behave_bdd/features/login_app.feature">login_app.feature</a>, which uses <a translate="no" href="https://behave.readthedocs.io/en/stable/gherkin.html#features" target="_blank">behave-BDD Gherkin</a> syntax. Runs with <code translate="no">behave</code>. (<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/behave_bdd/ReadMe.md">Learn about the <b>SeleniumBase behave-BDD</b> integration</a>)</p>
436
-
437
- ```gherkin
438
- Feature: SeleniumBase scenarios for the Simple App
439
-
440
- Scenario: Verify the Simple App (Login / Logout)
441
- Given Open "seleniumbase.io/simple/login"
442
- And Type "demo_user" into "#username"
443
- And Type "secret_pass" into "#password"
444
- And Click 'a:contains("Sign in")'
445
- And Assert exact text "Welcome!" in "h1"
446
- And Assert element "img#image1"
447
- And Highlight "#image1"
448
- And Click link "Sign out"
449
- And Assert text "signed out" in "#top_message"
450
- ```
451
-
452
461
  --------
453
462
 
454
463
  <a id="python_installation"></a>
@@ -547,20 +556,21 @@ pip install -e .
547
556
  <summary> ▶️ Here's sample output from a chromedriver download. (<b>click to expand</b>)</summary>
548
557
 
549
558
  ```bash
550
- *** chromedriver to download = 121.0.6167.85 (Latest Stable)
559
+ *** chromedriver to download = 131.0.6778.108 (Latest Stable)
551
560
 
552
561
  Downloading chromedriver-mac-arm64.zip from:
553
- https://storage.googleapis.com/chrome-for-testing-public/121.0.6167.85/mac-arm64/chromedriver-mac-arm64.zip ...
562
+ https://storage.googleapis.com/chrome-for-testing-public/131.0.6778.108/mac-arm64/chromedriver-mac-arm64.zip ...
554
563
  Download Complete!
555
564
 
556
565
  Extracting ['chromedriver'] from chromedriver-mac-arm64.zip ...
557
566
  Unzip Complete!
558
567
 
559
568
  The file [chromedriver] was saved to:
560
- /Users/michael/github/SeleniumBase/seleniumbase/drivers/chromedriver
569
+ ~/github/SeleniumBase/seleniumbase/drivers/
570
+ chromedriver
561
571
 
562
- Making [chromedriver 121.0.6167.85] executable ...
563
- [chromedriver 121.0.6167.85] is now ready for use!
572
+ Making [chromedriver 131.0.6778.108] executable ...
573
+ [chromedriver 131.0.6778.108] is now ready for use!
564
574
  ```
565
575
 
566
576
  </details>
@@ -578,9 +588,9 @@ cd examples/
578
588
  pytest my_first_test.py
579
589
  ```
580
590
 
581
- <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py"><img src="https://seleniumbase.github.io/cdn/gif/fast_swag_2.gif" alt="SeleniumBase Test" title="SeleniumBase Test" width="520" /></a>
591
+ <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py"><img src="https://seleniumbase.github.io/cdn/gif/fast_swag_2.gif" alt="SeleniumBase Test" title="SeleniumBase Test" width="480" /></a>
582
592
 
583
- <p align="left"><b>Here's the code for <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py">my_first_test.py</a>:</b></p>
593
+ <p align="left"><b>Here's the full code for <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py">my_first_test.py</a>:</b></p>
584
594
 
585
595
  ```python
586
596
  from seleniumbase import BaseCase
@@ -996,7 +1006,7 @@ You can run it from the ``examples/`` folder like this:
996
1006
  pytest test_fail.py
997
1007
  ```
998
1008
 
999
- 🔵 You'll notice that a logs folder, "latest_logs", was created to hold information about the failing test, and screenshots. During test runs, past results get moved to the archived_logs folder if you have ARCHIVE_EXISTING_LOGS set to True in [settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py), or if your run tests with ``--archive-logs``. If you choose not to archive existing logs, they will be deleted and replaced by the logs of the latest test run.
1009
+ 🔵 You'll notice that a logs folder, ``./latest_logs/``, was created to hold information (and screenshots) about the failing test. During test runs, past results get moved to the archived_logs folder if you have ARCHIVE_EXISTING_LOGS set to True in [settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py), or if your run tests with ``--archive-logs``. If you choose not to archive existing logs, they will be deleted and replaced by the logs of the latest test run.
1000
1010
 
1001
1011
  --------
1002
1012
 
@@ -3,7 +3,7 @@ sbase/__main__.py,sha256=G0bVB1-DM4PGwQ1KyOupaWCs4ePbChZNNWuX2htim5U,647
3
3
  sbase/steps.py,sha256=_WvAjydKqZfTdnZW9LPKkRty-g-lfdUPmLqnZj6ulcs,43013
4
4
  seleniumbase/__init__.py,sha256=OtJh8nGKL4xtZpw8KPqmn7Q6R-86t4cWUDyVF5MbMTo,2398
5
5
  seleniumbase/__main__.py,sha256=dn1p6dgCchmcH1zzTzzQvFwwdQQqnTGH6ULV9m4hv24,654
6
- seleniumbase/__version__.py,sha256=y69L6vxwTfyKakId4Qsl-pVfSdMvJO_IHnQ7VrqavyQ,47
6
+ seleniumbase/__version__.py,sha256=0OZXpruceiqE15CL_lL99PZVeEgaBkTbwpFG6D-9WT4,47
7
7
  seleniumbase/behave/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  seleniumbase/behave/behave_helper.py,sha256=elkl8P9eLulRAioLstE9baYNM9N_PHBmAOcajX-pH_Y,24198
9
9
  seleniumbase/behave/behave_sb.py,sha256=-hza7Nx2U41mSObYiPMi48v3JlPh3sJO3yzP0kqZ1Gk,59174
@@ -36,7 +36,7 @@ seleniumbase/console_scripts/sb_print.py,sha256=tNy-bMDgwHJO3bZxMpmo9weSE8uhbH0C
36
36
  seleniumbase/console_scripts/sb_recorder.py,sha256=fnHb5-kh11Hit-E9Ha-e4QXzqLcZvtij6mb5qNd4B1Q,11032
37
37
  seleniumbase/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
38
  seleniumbase/core/application_manager.py,sha256=e_0sjtI8cjY5BNyZj1QBR0j6_oCScxGmSXYEpcYwuZE,576
39
- seleniumbase/core/browser_launcher.py,sha256=FSnNzrnE52lXvpLToDRm2Isr4280xf7Vax_L_oTgDuY,224640
39
+ seleniumbase/core/browser_launcher.py,sha256=nQ8-5dvNU4Uy5lSTgnwB2nFAemS43PqJhHccdvdGJ94,224486
40
40
  seleniumbase/core/capabilities_parser.py,sha256=meIS2uHapTCq2ldfNAToC7r0cKmZDRXuYNKExM1GHDY,6038
41
41
  seleniumbase/core/colored_traceback.py,sha256=DrRWfg7XEnKcgY59Xj7Jdk09H-XqHYBSUpB-DiZt6iY,2020
42
42
  seleniumbase/core/create_db_tables.sql,sha256=VWPtrdiW_HQ6yETHjqTu-VIrTwvd8I8o1NfBeaVSHpU,972
@@ -44,13 +44,13 @@ seleniumbase/core/detect_b_ver.py,sha256=RxeGRMbBUTMrXh5KsS1P1SH7eEKYbzL1vQw1gTd
44
44
  seleniumbase/core/download_helper.py,sha256=qSR54kQISucF4RQaLCOuuerSu6DR41juGi_30HVvWYY,2943
45
45
  seleniumbase/core/encoded_images.py,sha256=rDKJ4cNJSuKiRcFViYU7bjyTS9_moI57gUPRXVg3u2k,14209
46
46
  seleniumbase/core/jqc_helper.py,sha256=2DDQr9Q2jSSZqFzX588jLlUM9oJvyrRWq2aORSIPUdI,10322
47
- seleniumbase/core/log_helper.py,sha256=N0YbsRy8sEoGQu4BjiAJHC5mK_ydl0YLgRp6jAcwwos,22987
47
+ seleniumbase/core/log_helper.py,sha256=SW8wx2f2HfU3ERbANjxEC-jDbjy_IzaNYRKPlayfRRI,23442
48
48
  seleniumbase/core/mysql.py,sha256=8Fzj3p5dhtDWfMpFqFYxpSwa9s1UltiHsWJ56_aPOqk,3993
49
49
  seleniumbase/core/proxy_helper.py,sha256=kZnfkflB3XhuL2h-3inmx3UOLS8VAZ385BGCc4H8TvU,8267
50
50
  seleniumbase/core/recorder_helper.py,sha256=fNGjbapXmEsht54x1o6Igk198QdnPxDDnjUOzQxNhNQ,25055
51
51
  seleniumbase/core/report_helper.py,sha256=AIl6Qava2yW1uSzbLpJBlPlYDz0KE-rVhogh8hsGWBo,12201
52
52
  seleniumbase/core/s3_manager.py,sha256=bkeI8I4y19ebWuQG1oEZV5qJbotC6eN8vin31OCNWJk,3521
53
- seleniumbase/core/sb_cdp.py,sha256=UB6pDr9qIMq1ExIxtfMRzQe6CZ-fnS1JNLiS2QvYfdk,73720
53
+ seleniumbase/core/sb_cdp.py,sha256=f6l4GUJP-jg-ivukpbwVLyFNvnlzUnXjm5Y2oUw0I88,76028
54
54
  seleniumbase/core/sb_driver.py,sha256=NGa4adi8OAi2WFtFkEguXg3JCd1p-JuZweIpGNifEfU,13488
55
55
  seleniumbase/core/session_helper.py,sha256=s9zD3PVZEWVzG2h81cCUskbNWLfdjC_LwwQjKptHCak,558
56
56
  seleniumbase/core/settings_parser.py,sha256=gqVohHVlE_5L5Cqe2L24uYrRzvoK-saX8E_Df7_-_3I,7609
@@ -65,12 +65,12 @@ seleniumbase/extensions/disable_csp.zip,sha256=YMifIIgEBiLrEFrS1sfW4Exh4br1V4oK1
65
65
  seleniumbase/extensions/recorder.zip,sha256=OOyzF-Ize2cSRu1CqhzSAq5vusI9hqLLd2OIApUHesI,11918
66
66
  seleniumbase/extensions/sbase_ext.zip,sha256=3s1N8zrVaMz8RQEOIoBzC3KDjtmHwVZRvVsX25Odr_s,8175
67
67
  seleniumbase/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
- seleniumbase/fixtures/base_case.py,sha256=DY-UpQRyfl8ZzA-D-5cKJKwm2kAXeE7L06rgMRDz3Xw,718513
68
+ seleniumbase/fixtures/base_case.py,sha256=xwRyaBUd7jpN_RQvTXabrMVMBUnuG65Bp8KDDrcoWYk,719184
69
69
  seleniumbase/fixtures/constants.py,sha256=e1LppavlrAcI4XBJMq7u5j8SffaQ7SPQps1y0YvZYfY,13649
70
70
  seleniumbase/fixtures/css_to_xpath.py,sha256=9ouDB1xl4MJ2os6JOgTIAyHKOQfuxtxvXC3O5hSnEKA,1954
71
71
  seleniumbase/fixtures/errors.py,sha256=KyxuEVx_e3MPhVrJfNIa_3ltMpbCFxfy_jxK8RFNTns,555
72
72
  seleniumbase/fixtures/js_utils.py,sha256=asZJ0LDFv2BPQOzUVKK_9ie8ToGcmFousDnGdb3Vabg,51534
73
- seleniumbase/fixtures/page_actions.py,sha256=dbp63c-7asYZyd8aOu57Y3dxQQozp_VJsP5h74s1kBA,66552
73
+ seleniumbase/fixtures/page_actions.py,sha256=LPcFSkUvBkxLrOt4laQHHN-NLmqInT41E2vlPiOlLFY,66753
74
74
  seleniumbase/fixtures/page_utils.py,sha256=5m7iXpikLs80TJoRO6_gEfXE1AKeQgcH1aFbR8o1C9A,12034
75
75
  seleniumbase/fixtures/shared_utils.py,sha256=G6CsE-Adt-GfuZF-71jXWKSIQW7YZPx8FIM24pVd_yI,8368
76
76
  seleniumbase/fixtures/unittest_helper.py,sha256=sfZ92rZeBAn_sF_yQ3I6_I7h3lyU5-cV_UMegBNoEm8,1294
@@ -114,8 +114,8 @@ seleniumbase/undetected/patcher.py,sha256=n9WfKznr4cQvaE5Gcx7iyK27zMWIc8KdI69_m8
114
114
  seleniumbase/undetected/reactor.py,sha256=NropaXcO54pzmDq6quR27qPJxab6636H7LRAaq-o0ng,2859
115
115
  seleniumbase/undetected/webelement.py,sha256=_s6evgUkdWJpwOnzX4qR9i796PoVbz3txlzHlOBJ4BE,1370
116
116
  seleniumbase/undetected/cdp_driver/__init__.py,sha256=c0TjMwPfVFyoqYOJ7PQ-Jln_L_dpN3ebHyaD-juQoM0,64
117
- seleniumbase/undetected/cdp_driver/_contradict.py,sha256=6thDYeoEGiC7Q3tXLgoD_AhxecCFnATzBSjNympyRHA,3184
118
- seleniumbase/undetected/cdp_driver/browser.py,sha256=n8GYspU7b0pfBT4AqhqY6IdGVOZ1FC4wJ5emIOl-khQ,30129
117
+ seleniumbase/undetected/cdp_driver/_contradict.py,sha256=lP4b0h5quAy573ETn_TBbYV889cL1AuPLVInpJ0ZkiU,3183
118
+ seleniumbase/undetected/cdp_driver/browser.py,sha256=quD0e2aoehXQrs9zxGsobF0kZY11gmJ58Q_JQhheQYg,30144
119
119
  seleniumbase/undetected/cdp_driver/cdp_util.py,sha256=YhtD2Tm6PLIy9VKbgk8lHdGniS3mObyX4yAC1aG0TgQ,16733
120
120
  seleniumbase/undetected/cdp_driver/config.py,sha256=oHFJ3UH0OmLmEGgG5S6SZwbyBs9ZYMsbUJ02QCA7iZc,12044
121
121
  seleniumbase/undetected/cdp_driver/connection.py,sha256=sOTUGjbUqKA2hPvDcRCdqw1VQjVGJs7mbgVvzS7ldtE,23360
@@ -135,9 +135,9 @@ seleniumbase/utilities/selenium_grid/start-grid-hub.bat,sha256=Ftq-GrAKRYH2ssDPr
135
135
  seleniumbase/utilities/selenium_grid/start-grid-hub.sh,sha256=KADv0RUHONLL2_I443QFK8PryBpDmKn5Gy0s4o0vDSM,106
136
136
  seleniumbase/utilities/selenium_ide/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
137
137
  seleniumbase/utilities/selenium_ide/convert_ide.py,sha256=pZFnqEJQEKZPyNFjkLD29s2HPQgCrWW9XJWpCPhWOoM,31691
138
- seleniumbase-4.33.10.dist-info/LICENSE,sha256=odSYtWibXBnQ1gBg6CnDZ82n8kLF_if5-2nbqnEyD8k,1085
139
- seleniumbase-4.33.10.dist-info/METADATA,sha256=axBgznV-hh9inoSLSfWWgqgQyWorR-6q-kai0rYSTPA,86529
140
- seleniumbase-4.33.10.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
141
- seleniumbase-4.33.10.dist-info/entry_points.txt,sha256=CNrh2EKNaHYEhO6pP1RJyVLB99LkDDYX7TnUK8xfjqk,623
142
- seleniumbase-4.33.10.dist-info/top_level.txt,sha256=4N97aBOQ8ETCnDnokBsWb07lJfTaq3C1ZzYRxvLMxqU,19
143
- seleniumbase-4.33.10.dist-info/RECORD,,
138
+ seleniumbase-4.33.12.dist-info/LICENSE,sha256=odSYtWibXBnQ1gBg6CnDZ82n8kLF_if5-2nbqnEyD8k,1085
139
+ seleniumbase-4.33.12.dist-info/METADATA,sha256=6bDBhrptfHbhYpEAgxncG9BFuROdyma7C_CB3UXd8ao,86044
140
+ seleniumbase-4.33.12.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
141
+ seleniumbase-4.33.12.dist-info/entry_points.txt,sha256=CNrh2EKNaHYEhO6pP1RJyVLB99LkDDYX7TnUK8xfjqk,623
142
+ seleniumbase-4.33.12.dist-info/top_level.txt,sha256=4N97aBOQ8ETCnDnokBsWb07lJfTaq3C1ZzYRxvLMxqU,19
143
+ seleniumbase-4.33.12.dist-info/RECORD,,