seleniumbase 4.34.15__py3-none-any.whl → 4.35.1__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.34.15"
2
+ __version__ = "4.35.1"
@@ -631,10 +631,13 @@ def main():
631
631
  data = []
632
632
  data.append("from seleniumbase import BaseCase")
633
633
  data.append("from .google_objects import HomePage, ResultsPage")
634
+ data.append('BaseCase.main(__name__, __file__, "--uc")')
634
635
  data.append("")
635
636
  data.append("")
636
637
  data.append("class GoogleTests(BaseCase):")
637
638
  data.append(" def test_google_dot_com(self):")
639
+ data.append(" if not self.undetectable:")
640
+ data.append(" self.get_new_driver(undetectable=True)")
638
641
  data.append(' self.open("https://google.com/ncr")')
639
642
  data.append(' self.assert_title_contains("Google")')
640
643
  data.append(" self.sleep(0.05)")
@@ -718,7 +718,13 @@ def uc_open_with_cdp_mode(driver, url=None):
718
718
  cdp.is_selected = CDPM.is_selected
719
719
  cdp.is_element_present = CDPM.is_element_present
720
720
  cdp.is_element_visible = CDPM.is_element_visible
721
+ cdp.is_text_visible = CDPM.is_text_visible
722
+ cdp.is_exact_text_visible = CDPM.is_exact_text_visible
723
+ cdp.wait_for_text = CDPM.wait_for_text
724
+ cdp.wait_for_text_not_visible = CDPM.wait_for_text_not_visible
721
725
  cdp.wait_for_element_visible = CDPM.wait_for_element_visible
726
+ cdp.wait_for_element_not_visible = CDPM.wait_for_element_not_visible
727
+ cdp.wait_for_element_absent = CDPM.wait_for_element_absent
722
728
  cdp.assert_element = CDPM.assert_element
723
729
  cdp.assert_element_visible = CDPM.assert_element_visible
724
730
  cdp.assert_element_present = CDPM.assert_element_present
@@ -731,6 +737,7 @@ def uc_open_with_cdp_mode(driver, url=None):
731
737
  cdp.assert_url_contains = CDPM.assert_url_contains
732
738
  cdp.assert_text = CDPM.assert_text
733
739
  cdp.assert_exact_text = CDPM.assert_exact_text
740
+ cdp.assert_text_not_visible = CDPM.assert_text_not_visible
734
741
  cdp.assert_true = CDPM.assert_true
735
742
  cdp.assert_false = CDPM.assert_false
736
743
  cdp.assert_equal = CDPM.assert_equal
@@ -1623,9 +1630,19 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
1623
1630
  ):
1624
1631
  driver.uc_open_with_disconnect(driver.current_url, 3.8)
1625
1632
  with suppress(Exception):
1633
+ if "--debug" in sys.argv:
1634
+ if sb_config._saved_cf_tab_count == 1:
1635
+ print(' <DEBUG> pyautogui.press("\\t")')
1636
+ else:
1637
+ print(
1638
+ ' <DEBUG> pyautogui.press("\\t") * %s'
1639
+ % sb_config._saved_cf_tab_count
1640
+ )
1626
1641
  for i in range(sb_config._saved_cf_tab_count):
1627
1642
  pyautogui.press("\t")
1628
1643
  time.sleep(0.027)
1644
+ if "--debug" in sys.argv:
1645
+ print(' <DEBUG> pyautogui.press(" ")')
1629
1646
  pyautogui.press(" ")
1630
1647
  else:
1631
1648
  driver.disconnect()
@@ -2280,6 +2297,7 @@ def _set_chrome_options(
2280
2297
  or proxy_string
2281
2298
  ):
2282
2299
  chrome_options.add_argument("--ignore-certificate-errors")
2300
+ chrome_options.add_argument("--ignore-ssl-errors=yes")
2283
2301
  if not enable_ws:
2284
2302
  chrome_options.add_argument("--disable-web-security")
2285
2303
  if (
@@ -2304,7 +2322,14 @@ def _set_chrome_options(
2304
2322
  and not enable_3d_apis
2305
2323
  ):
2306
2324
  chrome_options.add_argument("--disable-gpu")
2307
- if not IS_LINUX and is_using_uc(undetectable, browser_name):
2325
+ if (
2326
+ (not IS_LINUX and is_using_uc(undetectable, browser_name))
2327
+ or (
2328
+ IS_MAC
2329
+ and binary_location
2330
+ and "chrome-headless-shell" in binary_location
2331
+ )
2332
+ ):
2308
2333
  chrome_options.add_argument("--disable-dev-shm-usage")
2309
2334
  chrome_options.add_argument("--disable-application-cache")
2310
2335
  if IS_LINUX:
@@ -4231,6 +4256,7 @@ def get_local_driver(
4231
4256
  edge_options.add_argument("--log-level=3")
4232
4257
  edge_options.add_argument("--no-first-run")
4233
4258
  edge_options.add_argument("--ignore-certificate-errors")
4259
+ edge_options.add_argument("--ignore-ssl-errors=yes")
4234
4260
  if devtools and not headless:
4235
4261
  edge_options.add_argument("--auto-open-devtools-for-tabs")
4236
4262
  edge_options.add_argument("--allow-file-access-from-files")
@@ -56,12 +56,16 @@ class CDPMethods():
56
56
  element, *args, **kwargs
57
57
  )
58
58
  element.focus = lambda: self.__focus(element)
59
+ element.gui_click = (
60
+ lambda *args, **kwargs: self.__gui_click(element, *args, **kwargs)
61
+ )
59
62
  element.highlight_overlay = lambda: self.__highlight_overlay(element)
60
63
  element.mouse_click = lambda: self.__mouse_click(element)
61
64
  element.mouse_drag = (
62
65
  lambda destination: self.__mouse_drag(element, destination)
63
66
  )
64
67
  element.mouse_move = lambda: self.__mouse_move(element)
68
+ element.press_keys = lambda text: self.__press_keys(element, text)
65
69
  element.query_selector = (
66
70
  lambda selector: self.__query_selector(element, selector)
67
71
  )
@@ -211,7 +215,8 @@ class CDPMethods():
211
215
  element = self.__add_sync_methods(element.parent)
212
216
  return self.__add_sync_methods(element)
213
217
  elif (
214
- element.parent.parent
218
+ element.parent
219
+ and element.parent.parent
215
220
  and tag_name in element.parent.parent.tag_name.lower()
216
221
  and text.strip() in element.parent.parent.text
217
222
  ):
@@ -272,7 +277,8 @@ class CDPMethods():
272
277
  if element not in updated_elements:
273
278
  updated_elements.append(element)
274
279
  elif (
275
- element.parent.parent
280
+ element.parent
281
+ and element.parent.parent
276
282
  and tag_name in element.parent.parent.tag_name.lower()
277
283
  and text.strip() in element.parent.parent.text
278
284
  ):
@@ -282,7 +288,7 @@ class CDPMethods():
282
288
  return updated_elements
283
289
 
284
290
  def select(self, selector, timeout=None):
285
- """Similar to find_element(), but without text-based search."""
291
+ """Similar to find_element()."""
286
292
  if not timeout:
287
293
  timeout = settings.SMALL_TIMEOUT
288
294
  self.__add_light_pause()
@@ -291,12 +297,25 @@ class CDPMethods():
291
297
  tag_name = selector.split(":contains(")[0].split(" ")[-1]
292
298
  text = selector.split(":contains(")[1].split(")")[0][1:-1]
293
299
  with suppress(Exception):
300
+ new_timeout = timeout
301
+ if new_timeout < 1:
302
+ new_timeout = 1
294
303
  self.loop.run_until_complete(
295
- self.page.select(tag_name, timeout=5)
304
+ self.page.select(tag_name, timeout=new_timeout)
296
305
  )
297
- self.loop.run_until_complete(self.page.find(text, timeout=5))
298
- element = self.find_elements_by_text(text, tag_name=tag_name)[0]
299
- return self.__add_sync_methods(element)
306
+ self.loop.run_until_complete(
307
+ self.page.find(text, timeout=new_timeout)
308
+ )
309
+ elements = self.find_elements_by_text(text, tag_name=tag_name)
310
+ if not elements:
311
+ plural = "s"
312
+ if timeout == 1:
313
+ plural = ""
314
+ msg = "\n Element {%s} was not found after %s second%s!"
315
+ message = msg % (selector, timeout, plural)
316
+ raise Exception(message)
317
+ element = self.__add_sync_methods(elements[0])
318
+ return element
300
319
  failure = False
301
320
  try:
302
321
  element = self.loop.run_until_complete(
@@ -307,11 +326,8 @@ class CDPMethods():
307
326
  plural = "s"
308
327
  if timeout == 1:
309
328
  plural = ""
310
- message = "\n Element {%s} was not found after %s second%s!" % (
311
- selector,
312
- timeout,
313
- plural,
314
- )
329
+ msg = "\n Element {%s} was not found after %s second%s!"
330
+ message = msg % (selector, timeout, plural)
315
331
  if failure:
316
332
  raise Exception(message)
317
333
  element = self.__add_sync_methods(element)
@@ -423,6 +439,39 @@ class CDPMethods():
423
439
  self.loop.run_until_complete(element.focus_async())
424
440
  )
425
441
 
442
+ def __gui_click(self, element, timeframe=None):
443
+ element.scroll_into_view()
444
+ self.__add_light_pause()
445
+ position = element.get_position()
446
+ x = position.x
447
+ y = position.y
448
+ e_width = position.width
449
+ e_height = position.height
450
+ # Relative to window
451
+ element_rect = {"height": e_height, "width": e_width, "x": x, "y": y}
452
+ window_rect = self.get_window_rect()
453
+ w_bottom_y = window_rect["y"] + window_rect["height"]
454
+ viewport_height = window_rect["innerHeight"]
455
+ x = window_rect["x"] + element_rect["x"]
456
+ y = w_bottom_y - viewport_height + element_rect["y"]
457
+ y_scroll_offset = window_rect["pageYOffset"]
458
+ y = y - y_scroll_offset
459
+ x = x + window_rect["scrollX"]
460
+ y = y + window_rect["scrollY"]
461
+ # Relative to screen
462
+ element_rect = {"height": e_height, "width": e_width, "x": x, "y": y}
463
+ e_width = element_rect["width"]
464
+ e_height = element_rect["height"]
465
+ e_x = element_rect["x"]
466
+ e_y = element_rect["y"]
467
+ x, y = ((e_x + e_width / 2.0) + 0.5), ((e_y + e_height / 2.0) + 0.5)
468
+ if not timeframe or not isinstance(timeframe, (int, float)):
469
+ timeframe = 0.25
470
+ if timeframe > 3:
471
+ timeframe = 3
472
+ self.gui_click_x_y(x, y, timeframe=timeframe)
473
+ return self.loop.run_until_complete(self.page.wait())
474
+
426
475
  def __highlight_overlay(self, element):
427
476
  return (
428
477
  self.loop.run_until_complete(element.highlight_overlay_async())
@@ -445,6 +494,21 @@ class CDPMethods():
445
494
  self.loop.run_until_complete(element.mouse_move_async())
446
495
  )
447
496
 
497
+ def __press_keys(self, element, text):
498
+ element.scroll_into_view()
499
+ submit = False
500
+ if text.endswith("\n") or text.endswith("\r"):
501
+ submit = True
502
+ text = text[:-1]
503
+ for key in text:
504
+ element.send_keys(key)
505
+ time.sleep(0.044)
506
+ if submit:
507
+ element.send_keys("\r\n")
508
+ time.sleep(0.044)
509
+ self.__slow_mode_pause_if_set()
510
+ return self.loop.run_until_complete(self.page.sleep(0.025))
511
+
448
512
  def __query_selector(self, element, selector):
449
513
  selector = self.__convert_to_css_if_xpath(selector)
450
514
  element2 = self.loop.run_until_complete(
@@ -810,7 +874,7 @@ class CDPMethods():
810
874
  text = text[:-1] + "\r\n"
811
875
  element.send_keys(text)
812
876
  self.__slow_mode_pause_if_set()
813
- self.loop.run_until_complete(self.page.wait())
877
+ self.loop.run_until_complete(self.page.sleep(0.025))
814
878
 
815
879
  def press_keys(self, selector, text, timeout=None):
816
880
  """Similar to send_keys(), but presses keys at human speed."""
@@ -830,7 +894,7 @@ class CDPMethods():
830
894
  element.send_keys("\r\n")
831
895
  time.sleep(0.044)
832
896
  self.__slow_mode_pause_if_set()
833
- self.loop.run_until_complete(self.page.wait())
897
+ self.loop.run_until_complete(self.page.sleep(0.025))
834
898
 
835
899
  def type(self, selector, text, timeout=None):
836
900
  """Similar to send_keys(), but clears the text field first."""
@@ -845,7 +909,7 @@ class CDPMethods():
845
909
  text = text[:-1] + "\r\n"
846
910
  element.send_keys(text)
847
911
  self.__slow_mode_pause_if_set()
848
- self.loop.run_until_complete(self.page.wait())
912
+ self.loop.run_until_complete(self.page.sleep(0.025))
849
913
 
850
914
  def set_value(self, selector, text, timeout=None):
851
915
  """Similar to send_keys(), but clears the text field first."""
@@ -883,7 +947,7 @@ class CDPMethods():
883
947
  self.__add_light_pause()
884
948
  self.send_keys(selector, "\n")
885
949
  self.__slow_mode_pause_if_set()
886
- self.loop.run_until_complete(self.page.wait())
950
+ self.loop.run_until_complete(self.page.sleep(0.025))
887
951
 
888
952
  def evaluate(self, expression):
889
953
  """Run a JavaScript expression and return the result."""
@@ -1323,7 +1387,7 @@ class CDPMethods():
1323
1387
  pyautogui.press(key)
1324
1388
  time.sleep(0.044)
1325
1389
  self.__slow_mode_pause_if_set()
1326
- self.loop.run_until_complete(self.page.wait())
1390
+ self.loop.run_until_complete(self.page.sleep(0.025))
1327
1391
 
1328
1392
  def gui_press_keys(self, keys):
1329
1393
  self.__install_pyautogui_if_missing()
@@ -1338,7 +1402,7 @@ class CDPMethods():
1338
1402
  pyautogui.press(key)
1339
1403
  time.sleep(0.044)
1340
1404
  self.__slow_mode_pause_if_set()
1341
- self.loop.run_until_complete(self.page.wait())
1405
+ self.loop.run_until_complete(self.page.sleep(0.025))
1342
1406
 
1343
1407
  def gui_write(self, text):
1344
1408
  self.__install_pyautogui_if_missing()
@@ -1351,7 +1415,7 @@ class CDPMethods():
1351
1415
  self.__make_sure_pyautogui_lock_is_writable()
1352
1416
  pyautogui.write(text)
1353
1417
  self.__slow_mode_pause_if_set()
1354
- self.loop.run_until_complete(self.page.wait())
1418
+ self.loop.run_until_complete(self.page.sleep(0.025))
1355
1419
 
1356
1420
  def __gui_click_x_y(self, x, y, timeframe=0.25, uc_lock=False):
1357
1421
  self.__install_pyautogui_if_missing()
@@ -1681,59 +1745,93 @@ class CDPMethods():
1681
1745
  return True
1682
1746
  return False
1683
1747
 
1684
- def wait_for_element_visible(self, selector, timeout=None):
1685
- if not timeout:
1686
- timeout = settings.SMALL_TIMEOUT
1748
+ def is_text_visible(self, text, selector="body"):
1749
+ selector = self.__convert_to_css_if_xpath(selector)
1750
+ text = text.strip()
1751
+ element = None
1687
1752
  try:
1688
- self.select(selector, timeout=timeout)
1753
+ element = self.find_element(selector, timeout=0.1)
1689
1754
  except Exception:
1690
- raise Exception("Element {%s} was not found!" % selector)
1691
- for i in range(30):
1692
- if self.is_element_visible(selector):
1693
- return self.select(selector)
1694
- time.sleep(0.1)
1695
- raise Exception("Element {%s} was not visible!" % selector)
1755
+ return False
1756
+ with suppress(Exception):
1757
+ if text in element.text_all:
1758
+ return True
1759
+ return False
1696
1760
 
1697
- def assert_element(self, selector, timeout=None):
1698
- """Same as assert_element_visible()"""
1761
+ def is_exact_text_visible(self, text, selector="body"):
1762
+ selector = self.__convert_to_css_if_xpath(selector)
1763
+ text = text.strip()
1764
+ element = None
1765
+ try:
1766
+ element = self.find_element(selector, timeout=0.1)
1767
+ except Exception:
1768
+ return False
1769
+ with suppress(Exception):
1770
+ if text == element.text_all.strip():
1771
+ return True
1772
+ return False
1773
+
1774
+ def wait_for_text(self, text, selector="body", timeout=None):
1699
1775
  if not timeout:
1700
1776
  timeout = settings.SMALL_TIMEOUT
1777
+ start_ms = time.time() * 1000.0
1778
+ stop_ms = start_ms + (timeout * 1000.0)
1779
+ text = text.strip()
1780
+ element = None
1701
1781
  try:
1702
- self.select(selector, timeout=timeout)
1782
+ element = self.find_element(selector, timeout=timeout)
1703
1783
  except Exception:
1704
- raise Exception("Element {%s} was not found!" % selector)
1705
- for i in range(30):
1706
- if self.is_element_visible(selector):
1784
+ raise Exception("Element {%s} not found!" % selector)
1785
+ for i in range(int(timeout * 10)):
1786
+ with suppress(Exception):
1787
+ element = self.find_element(selector, timeout=0.1)
1788
+ if text in element.text_all:
1707
1789
  return True
1790
+ now_ms = time.time() * 1000.0
1791
+ if now_ms >= stop_ms:
1792
+ break
1708
1793
  time.sleep(0.1)
1709
- raise Exception("Element {%s} was not visible!" % selector)
1794
+ raise Exception(
1795
+ "Text {%s} not found in {%s}! Actual text: {%s}"
1796
+ % (text, selector, element.text_all)
1797
+ )
1710
1798
 
1711
- def assert_element_visible(self, selector, timeout=None):
1712
- """Same as assert_element()"""
1799
+ def wait_for_text_not_visible(self, text, selector="body", timeout=None):
1713
1800
  if not timeout:
1714
1801
  timeout = settings.SMALL_TIMEOUT
1715
- try:
1716
- self.select(selector, timeout=timeout)
1717
- except Exception:
1718
- raise Exception("Element {%s} was not found!" % selector)
1719
- for i in range(30):
1720
- if self.is_element_visible(selector):
1802
+ text = text.strip()
1803
+ start_ms = time.time() * 1000.0
1804
+ stop_ms = start_ms + (timeout * 1000.0)
1805
+ for i in range(int(timeout * 10)):
1806
+ if not self.is_text_visible(text, selector):
1721
1807
  return True
1808
+ now_ms = time.time() * 1000.0
1809
+ if now_ms >= stop_ms:
1810
+ break
1722
1811
  time.sleep(0.1)
1723
- raise Exception("Element {%s} was not visible!" % selector)
1812
+ plural = "s"
1813
+ if timeout == 1:
1814
+ plural = ""
1815
+ raise Exception(
1816
+ "Text {%s} in {%s} was still visible after %s second%s!"
1817
+ % (text, selector, timeout, plural)
1818
+ )
1724
1819
 
1725
- def assert_element_present(self, selector, timeout=None):
1726
- """Assert element is present in the DOM. (Visibility NOT required)"""
1820
+ def wait_for_element_visible(self, selector, timeout=None):
1727
1821
  if not timeout:
1728
1822
  timeout = settings.SMALL_TIMEOUT
1729
1823
  try:
1730
1824
  self.select(selector, timeout=timeout)
1731
1825
  except Exception:
1732
1826
  raise Exception("Element {%s} was not found!" % selector)
1733
- return True
1827
+ for i in range(30):
1828
+ if self.is_element_visible(selector):
1829
+ return self.select(selector)
1830
+ time.sleep(0.1)
1831
+ raise Exception("Element {%s} was not visible!" % selector)
1734
1832
 
1735
- def assert_element_absent(self, selector, timeout=None):
1736
- """Assert element is not present in the DOM."""
1833
+ def wait_for_element_not_visible(self, selector, timeout=None):
1834
+ """Wait for element to not be visible on page. (May still be in DOM)"""
1737
1835
  if not timeout:
1738
1836
  timeout = settings.SMALL_TIMEOUT
1739
1837
  start_ms = time.time() * 1000.0
@@ -1741,6 +1839,8 @@ class CDPMethods():
1741
1839
  for i in range(int(timeout * 10)):
1742
1840
  if not self.is_element_present(selector):
1743
1841
  return True
1842
+ elif not self.is_element_visible(selector):
1843
+ return True
1744
1844
  now_ms = time.time() * 1000.0
1745
1845
  if now_ms >= stop_ms:
1746
1846
  break
@@ -1749,12 +1849,12 @@ class CDPMethods():
1749
1849
  if timeout == 1:
1750
1850
  plural = ""
1751
1851
  raise Exception(
1752
- "Element {%s} was still present after %s second%s!"
1852
+ "Element {%s} was still visible after %s second%s!"
1753
1853
  % (selector, timeout, plural)
1754
1854
  )
1755
1855
 
1756
- def assert_element_not_visible(self, selector, timeout=None):
1757
- """Assert element is not visible on page. (May still be in DOM)"""
1856
+ def wait_for_element_absent(self, selector, timeout=None):
1857
+ """Wait for element to not be present in the DOM."""
1758
1858
  if not timeout:
1759
1859
  timeout = settings.SMALL_TIMEOUT
1760
1860
  start_ms = time.time() * 1000.0
@@ -1762,8 +1862,6 @@ class CDPMethods():
1762
1862
  for i in range(int(timeout * 10)):
1763
1863
  if not self.is_element_present(selector):
1764
1864
  return True
1765
- elif not self.is_element_visible(selector):
1766
- return True
1767
1865
  now_ms = time.time() * 1000.0
1768
1866
  if now_ms >= stop_ms:
1769
1867
  break
@@ -1772,10 +1870,49 @@ class CDPMethods():
1772
1870
  if timeout == 1:
1773
1871
  plural = ""
1774
1872
  raise Exception(
1775
- "Element {%s} was still visible after %s second%s!"
1873
+ "Element {%s} was still present after %s second%s!"
1776
1874
  % (selector, timeout, plural)
1777
1875
  )
1778
1876
 
1877
+ def assert_element(self, selector, timeout=None):
1878
+ """Same as assert_element_visible()"""
1879
+ self.assert_element_visible(selector, timeout=timeout)
1880
+ return True
1881
+
1882
+ def assert_element_visible(self, selector, timeout=None):
1883
+ """Same as assert_element()"""
1884
+ if not timeout:
1885
+ timeout = settings.SMALL_TIMEOUT
1886
+ try:
1887
+ self.select(selector, timeout=timeout)
1888
+ except Exception:
1889
+ raise Exception("Element {%s} was not found!" % selector)
1890
+ for i in range(30):
1891
+ if self.is_element_visible(selector):
1892
+ return True
1893
+ time.sleep(0.1)
1894
+ raise Exception("Element {%s} was not visible!" % selector)
1895
+
1896
+ def assert_element_present(self, selector, timeout=None):
1897
+ """Assert element is present in the DOM. (Visibility NOT required)"""
1898
+ if not timeout:
1899
+ timeout = settings.SMALL_TIMEOUT
1900
+ try:
1901
+ self.select(selector, timeout=timeout)
1902
+ except Exception:
1903
+ raise Exception("Element {%s} was not found!" % selector)
1904
+ return True
1905
+
1906
+ def assert_element_absent(self, selector, timeout=None):
1907
+ """Assert element is not present in the DOM."""
1908
+ self.wait_for_element_absent(selector, timeout=timeout)
1909
+ return True
1910
+
1911
+ def assert_element_not_visible(self, selector, timeout=None):
1912
+ """Assert element is not visible on page. (May still be in DOM)"""
1913
+ self.wait_for_element_not_visible(selector, timeout=timeout)
1914
+ return True
1915
+
1779
1916
  def assert_element_attribute(self, selector, attribute, value=None):
1780
1917
  attributes = self.get_element_attributes(selector)
1781
1918
  if attribute not in attributes:
@@ -1852,29 +1989,9 @@ class CDPMethods():
1852
1989
  raise Exception(error % (expected, actual))
1853
1990
 
1854
1991
  def assert_text(self, text, selector="body", timeout=None):
1855
- if not timeout:
1856
- timeout = settings.SMALL_TIMEOUT
1857
- start_ms = time.time() * 1000.0
1858
- stop_ms = start_ms + (timeout * 1000.0)
1859
- text = text.strip()
1860
- element = None
1861
- try:
1862
- element = self.find_element(selector, timeout=timeout)
1863
- except Exception:
1864
- raise Exception("Element {%s} not found!" % selector)
1865
- for i in range(int(timeout * 10)):
1866
- with suppress(Exception):
1867
- element = self.find_element(selector, timeout=0.1)
1868
- if text in element.text_all:
1869
- return True
1870
- now_ms = time.time() * 1000.0
1871
- if now_ms >= stop_ms:
1872
- break
1873
- time.sleep(0.1)
1874
- raise Exception(
1875
- "Text {%s} not found in {%s}! Actual text: {%s}"
1876
- % (text, selector, element.text_all)
1877
- )
1992
+ """Same as wait_for_text()"""
1993
+ self.wait_for_text(text, selector=selector, timeout=timeout)
1994
+ return True
1878
1995
 
1879
1996
  def assert_exact_text(self, text, selector="body", timeout=None):
1880
1997
  if not timeout:
@@ -1904,6 +2021,13 @@ class CDPMethods():
1904
2021
  % (text, element.text_all, selector)
1905
2022
  )
1906
2023
 
2024
+ def assert_text_not_visible(self, text, selector="body", timeout=None):
2025
+ """Raises an exception if the text is still visible after timeout."""
2026
+ self.wait_for_text_not_visible(
2027
+ text, selector=selector, timeout=timeout
2028
+ )
2029
+ return True
2030
+
1907
2031
  def assert_true(self, expression):
1908
2032
  if not expression:
1909
2033
  raise AssertionError("%s is not true" % expression)
@@ -1454,6 +1454,8 @@ class BaseCase(unittest.TestCase):
1454
1454
 
1455
1455
  def is_text_visible(self, text, selector="body", by="css selector"):
1456
1456
  """Returns whether the text substring is visible in the element."""
1457
+ if self.__is_cdp_swap_needed():
1458
+ return self.cdp.is_text_visible(text, selector)
1457
1459
  self.wait_for_ready_state_complete()
1458
1460
  time.sleep(0.01)
1459
1461
  selector, by = self.__recalculate_selector(selector, by)
@@ -1464,6 +1466,8 @@ class BaseCase(unittest.TestCase):
1464
1466
  def is_exact_text_visible(self, text, selector="body", by="css selector"):
1465
1467
  """Returns whether the text is exactly equal to the element text.
1466
1468
  (Leading and trailing whitespace is ignored in the verification.)"""
1469
+ if self.__is_cdp_swap_needed():
1470
+ return self.cdp.is_exact_text_visible(text, selector)
1467
1471
  self.wait_for_ready_state_complete()
1468
1472
  time.sleep(0.01)
1469
1473
  selector, by = self.__recalculate_selector(selector, by)
@@ -9120,7 +9124,7 @@ class BaseCase(unittest.TestCase):
9120
9124
  original_selector = selector
9121
9125
  selector, by = self.__recalculate_selector(selector, by)
9122
9126
  if self.__is_cdp_swap_needed():
9123
- self.cdp.assert_element_absent(selector)
9127
+ self.cdp.wait_for_element_absent(selector, timeout=timeout)
9124
9128
  return True
9125
9129
  return page_actions.wait_for_element_absent(
9126
9130
  self.driver,
@@ -9281,7 +9285,8 @@ class BaseCase(unittest.TestCase):
9281
9285
  "bottom_left", "bottom_center", "bottom_right"]
9282
9286
  max_messages: The limit of concurrent messages to display."""
9283
9287
  self.__check_scope()
9284
- self._check_browser()
9288
+ if not self.__is_cdp_swap_needed():
9289
+ self._check_browser()
9285
9290
  if not theme:
9286
9291
  theme = "default" # "flat"
9287
9292
  if not location:
@@ -9308,7 +9313,8 @@ class BaseCase(unittest.TestCase):
9308
9313
  You can also post messages by using =>
9309
9314
  self.execute_script('Messenger().post("My Message")') """
9310
9315
  self.__check_scope()
9311
- self._check_browser()
9316
+ if not self.__is_cdp_swap_needed():
9317
+ self._check_browser()
9312
9318
  if style not in ["info", "success", "error"]:
9313
9319
  style = "info"
9314
9320
  if not duration:
@@ -9579,7 +9585,7 @@ class BaseCase(unittest.TestCase):
9579
9585
  self.assert_elements_present(selector, by=by, timeout=timeout)
9580
9586
  return True
9581
9587
  if self.__is_cdp_swap_needed():
9582
- self.cdp.assert_element_present(selector)
9588
+ self.cdp.assert_element_present(selector, timeout=timeout)
9583
9589
  return True
9584
9590
  if self.__is_shadow_selector(selector):
9585
9591
  self.__assert_shadow_element_present(selector)
@@ -9656,7 +9662,7 @@ class BaseCase(unittest.TestCase):
9656
9662
  if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
9657
9663
  timeout = self.__get_new_timeout(timeout)
9658
9664
  if self.__is_cdp_swap_needed():
9659
- self.cdp.assert_element(selector)
9665
+ self.cdp.assert_element(selector, timeout=timeout)
9660
9666
  return True
9661
9667
  if isinstance(selector, list):
9662
9668
  self.assert_elements(selector, by=by, timeout=timeout)
@@ -9949,7 +9955,7 @@ class BaseCase(unittest.TestCase):
9949
9955
  messenger_post, selector, by
9950
9956
  )
9951
9957
  elif self.__is_cdp_swap_needed():
9952
- self.cdp.assert_text(text, selector)
9958
+ self.cdp.assert_text(text, selector, timeout=timeout)
9953
9959
  return True
9954
9960
  elif not self.is_connected():
9955
9961
  self.connect()
@@ -9999,7 +10005,7 @@ class BaseCase(unittest.TestCase):
9999
10005
  original_selector = selector
10000
10006
  selector, by = self.__recalculate_selector(selector, by)
10001
10007
  if self.__is_cdp_swap_needed():
10002
- self.cdp.assert_exact_text(text, selector)
10008
+ self.cdp.assert_exact_text(text, selector, timeout=timeout)
10003
10009
  return True
10004
10010
  if self.__is_shadow_selector(selector):
10005
10011
  self.__assert_exact_shadow_text_visible(text, selector, timeout)
@@ -10239,6 +10245,9 @@ class BaseCase(unittest.TestCase):
10239
10245
  timeout = self.__get_new_timeout(timeout)
10240
10246
  original_selector = selector
10241
10247
  selector, by = self.__recalculate_selector(selector, by)
10248
+ if self.__is_cdp_swap_needed():
10249
+ self.cdp.wait_for_element_absent(selector, timeout=timeout)
10250
+ return True
10242
10251
  return page_actions.wait_for_element_absent(
10243
10252
  self.driver,
10244
10253
  selector,
@@ -10261,7 +10270,7 @@ class BaseCase(unittest.TestCase):
10261
10270
  if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
10262
10271
  timeout = self.__get_new_timeout(timeout)
10263
10272
  if self.__is_cdp_swap_needed():
10264
- self.cdp.assert_element_absent(selector)
10273
+ self.cdp.assert_element_absent(selector, timeout=timeout)
10265
10274
  return True
10266
10275
  self.wait_for_element_absent(selector, by=by, timeout=timeout)
10267
10276
  return True
@@ -10282,7 +10291,7 @@ class BaseCase(unittest.TestCase):
10282
10291
  original_selector = selector
10283
10292
  selector, by = self.__recalculate_selector(selector, by)
10284
10293
  if self.__is_cdp_swap_needed():
10285
- self.cdp.assert_element_not_visible(selector)
10294
+ self.cdp.wait_for_element_not_visible(selector, timeout=timeout)
10286
10295
  return True
10287
10296
  return page_actions.wait_for_element_not_visible(
10288
10297
  self.driver,
@@ -10304,7 +10313,7 @@ class BaseCase(unittest.TestCase):
10304
10313
  if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
10305
10314
  timeout = self.__get_new_timeout(timeout)
10306
10315
  if self.__is_cdp_swap_needed():
10307
- self.cdp.assert_element_not_visible(selector)
10316
+ self.cdp.assert_element_not_visible(selector, timeout=timeout)
10308
10317
  return True
10309
10318
  self.wait_for_element_not_visible(selector, by=by, timeout=timeout)
10310
10319
  if self.recorder_mode and self.__current_url_is_recordable():
@@ -10326,6 +10335,10 @@ class BaseCase(unittest.TestCase):
10326
10335
  if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
10327
10336
  timeout = self.__get_new_timeout(timeout)
10328
10337
  selector, by = self.__recalculate_selector(selector, by)
10338
+ if self.__is_cdp_swap_needed():
10339
+ return self.cdp.wait_for_text(
10340
+ text, selector=selector, timeout=timeout
10341
+ )
10329
10342
  return page_actions.wait_for_text_not_visible(
10330
10343
  self.driver, text, selector, by, timeout
10331
10344
  )
@@ -13909,7 +13922,8 @@ class BaseCase(unittest.TestCase):
13909
13922
  js_utils.highlight_element_with_js(self.driver, element, loops, o_bs)
13910
13923
 
13911
13924
  def __highlight_with_jquery(self, selector, loops, o_bs):
13912
- self.wait_for_ready_state_complete()
13925
+ if not self.__is_cdp_swap_needed():
13926
+ self.wait_for_ready_state_complete()
13913
13927
  js_utils.highlight_with_jquery(self.driver, selector, loops, o_bs)
13914
13928
 
13915
13929
  def __highlight_with_js_2(self, message, selector, o_bs):
@@ -585,52 +585,60 @@ def highlight_with_jquery(driver, selector, loops=4, o_bs=""):
585
585
  '0px 0px 6px 6px rgba(128, 128, 128, 0.5)');"""
586
586
  % selector
587
587
  )
588
- safe_execute_script(driver, script)
588
+ with suppress(Exception):
589
+ safe_execute_script(driver, script)
589
590
  for n in range(loops):
590
591
  script = (
591
592
  """jQuery('%s').css('box-shadow',
592
593
  '0px 0px 6px 6px rgba(255, 0, 0, 1)');"""
593
594
  % selector
594
595
  )
595
- execute_script(driver, script)
596
+ with suppress(Exception):
597
+ execute_script(driver, script)
596
598
  time.sleep(0.0181)
597
599
  script = (
598
600
  """jQuery('%s').css('box-shadow',
599
601
  '0px 0px 6px 6px rgba(128, 0, 128, 1)');"""
600
602
  % selector
601
603
  )
602
- execute_script(driver, script)
604
+ with suppress(Exception):
605
+ execute_script(driver, script)
603
606
  time.sleep(0.0181)
604
607
  script = (
605
608
  """jQuery('%s').css('box-shadow',
606
609
  '0px 0px 6px 6px rgba(0, 0, 255, 1)');"""
607
610
  % selector
608
611
  )
609
- execute_script(driver, script)
612
+ with suppress(Exception):
613
+ execute_script(driver, script)
610
614
  time.sleep(0.0181)
611
615
  script = (
612
616
  """jQuery('%s').css('box-shadow',
613
617
  '0px 0px 6px 6px rgba(0, 255, 0, 1)');"""
614
618
  % selector
615
619
  )
616
- execute_script(driver, script)
620
+ with suppress(Exception):
621
+ execute_script(driver, script)
617
622
  time.sleep(0.0181)
618
623
  script = (
619
624
  """jQuery('%s').css('box-shadow',
620
625
  '0px 0px 6px 6px rgba(128, 128, 0, 1)');"""
621
626
  % selector
622
627
  )
623
- execute_script(driver, script)
628
+ with suppress(Exception):
629
+ execute_script(driver, script)
624
630
  time.sleep(0.0181)
625
631
  script = (
626
632
  """jQuery('%s').css('box-shadow',
627
633
  '0px 0px 6px 6px rgba(128, 0, 128, 1)');"""
628
634
  % selector
629
635
  )
630
- execute_script(driver, script)
636
+ with suppress(Exception):
637
+ execute_script(driver, script)
631
638
  time.sleep(0.0181)
632
639
  script = """jQuery('%s').css('box-shadow', '%s');""" % (selector, o_bs)
633
- execute_script(driver, script)
640
+ with suppress(Exception):
641
+ execute_script(driver, script)
634
642
 
635
643
 
636
644
  def add_css_link(driver, css_link):
@@ -924,13 +932,26 @@ def post_message(driver, message, msg_dur=None, style="info"):
924
932
  """hideAfter: %s, hideOnNavigate: true});"""
925
933
  % (message, style, msg_dur)
926
934
  )
935
+ retry = False
927
936
  try:
928
937
  execute_script(driver, messenger_script)
938
+ except TypeError as e:
939
+ if (
940
+ shared_utils.is_cdp_swap_needed(driver)
941
+ and "cannot unpack non-iterable" in str(e)
942
+ ):
943
+ pass
944
+ else:
945
+ retry = True
929
946
  except Exception:
947
+ retry = True
948
+ if retry:
930
949
  activate_messenger(driver)
931
950
  set_messenger_theme(driver)
932
951
  try:
933
952
  execute_script(driver, messenger_script)
953
+ except TypeError:
954
+ pass
934
955
  except Exception:
935
956
  time.sleep(0.17)
936
957
  activate_messenger(driver)
@@ -1273,7 +1294,10 @@ def slow_scroll_to_element(driver, element, *args, **kwargs):
1273
1294
  scroll_position = execute_script(driver, "return window.scrollY;")
1274
1295
  element_location_y = None
1275
1296
  try:
1276
- element_location_y = element.location["y"]
1297
+ if shared_utils.is_cdp_swap_needed(driver):
1298
+ element.get_position().y
1299
+ else:
1300
+ element_location_y = element.location["y"]
1277
1301
  except Exception:
1278
1302
  element.location_once_scrolled_into_view
1279
1303
  return
@@ -208,7 +208,6 @@ class Base(Plugin):
208
208
  self.duration = float(0)
209
209
  self.page_results_list = []
210
210
  self.test_count = 0
211
- self.import_error = False
212
211
  log_path = constants.Logs.LATEST + "/"
213
212
  archive_logs = options.archive_logs
214
213
  log_helper.log_folder_setup(log_path, archive_logs)
@@ -238,6 +237,7 @@ class Base(Plugin):
238
237
  )
239
238
  else:
240
239
  variables = {}
240
+ test.test.test_id = test.id()
241
241
  test.test.is_nosetest = True
242
242
  test.test.environment = self.options.environment
243
243
  test.test.env = self.options.environment # Add a shortened version
@@ -263,17 +263,16 @@ class Base(Plugin):
263
263
  )
264
264
  log_helper.clear_empty_logs()
265
265
  if self.report_on:
266
- if not self.import_error:
267
- report_helper.add_bad_page_log_file(self.page_results_list)
268
- report_log_path = report_helper.archive_new_report_logs()
269
- report_helper.build_report(
270
- report_log_path,
271
- self.page_results_list,
272
- self.successes,
273
- self.failures,
274
- self.options.browser,
275
- self.show_report,
276
- )
266
+ report_helper.add_bad_page_log_file(self.page_results_list)
267
+ report_log_path = report_helper.archive_new_report_logs()
268
+ report_helper.build_report(
269
+ report_log_path,
270
+ self.page_results_list,
271
+ self.successes,
272
+ self.failures,
273
+ self.options.browser,
274
+ self.show_report,
275
+ )
277
276
 
278
277
  def addSuccess(self, test, capt):
279
278
  if self.report_on:
@@ -293,9 +292,6 @@ class Base(Plugin):
293
292
  "%.2fs" % (float(time.time()) - float(self.start_time))
294
293
  )
295
294
  if test.id() == "nose.failure.Failure.runTest":
296
- print(">>> ERROR: Could not locate tests to run!")
297
- print(">>> The Test Report WILL NOT be generated!")
298
- self.import_error = True
299
295
  return
300
296
  self.failures.append(test.id())
301
297
  self.page_results_list.append(
@@ -314,6 +310,7 @@ class Base(Plugin):
314
310
  test._log_fail_data()
315
311
  sb_config._excinfo_tb = err
316
312
  log_path = None
313
+ source = None
317
314
  if hasattr(sb_config, "_test_logpath"):
318
315
  log_path = sb_config._test_logpath
319
316
  if hasattr(sb_config, "_last_page_source"):
@@ -10,7 +10,7 @@ Example -->
10
10
  ```python
11
11
  from seleniumbase import DriverContext
12
12
 
13
- with DriverContext() as driver:
13
+ with DriverContext(uc=True) as driver:
14
14
  driver.get("https://google.com/ncr")
15
15
  ```
16
16
 
@@ -30,7 +30,7 @@ Example -->
30
30
  ```python
31
31
  from seleniumbase import Driver
32
32
 
33
- driver = Driver()
33
+ driver = Driver(uc=True)
34
34
  driver.get("https://google.com/ncr")
35
35
  ```
36
36
 
@@ -2496,6 +2496,9 @@ def pytest_unconfigure(config):
2496
2496
  """This runs after all tests have completed with pytest."""
2497
2497
  if "--co" in sys_argv or "--collect-only" in sys_argv:
2498
2498
  return
2499
+ reporter = config.pluginmanager.get_plugin("terminalreporter")
2500
+ if not hasattr(reporter, "_sessionstarttime"):
2501
+ return
2499
2502
  if hasattr(sb_config, "_multithreaded") and sb_config._multithreaded:
2500
2503
  import fasteners
2501
2504
 
@@ -10,7 +10,7 @@ Example -->
10
10
  ```python
11
11
  from seleniumbase import SB
12
12
 
13
- with SB() as sb: # Many args! Eg. SB(browser="edge")
13
+ with SB(uc=True) as sb: # Many args! Eg. SB(browser="edge")
14
14
  sb.open("https://google.com/ncr")
15
15
  sb.type('[name="q"]', "SeleniumBase on GitHub\n")
16
16
  sb.click('a[href*="github.com/seleniumbase"]')
@@ -1309,6 +1309,7 @@ class SeleniumBrowser(Plugin):
1309
1309
  test.test.dashboard = False
1310
1310
  test.test._multithreaded = False
1311
1311
  test.test._reuse_session = False
1312
+ sb_config.recorder_mode = test.test.recorder_mode
1312
1313
  sb_config.no_screenshot = test.test.no_screenshot_after_test
1313
1314
  if test.test.servername != "localhost":
1314
1315
  # Using Selenium Grid
@@ -265,6 +265,8 @@ class Browser:
265
265
  :param new_window: Open new window
266
266
  :return: Page
267
267
  """
268
+ if url and ":" not in url:
269
+ url = "https://" + url
268
270
  if new_tab or new_window:
269
271
  # Create new target using the browser session.
270
272
  target_id = await self.connection.send(
@@ -4,6 +4,7 @@ import asyncio
4
4
  import fasteners
5
5
  import logging
6
6
  import os
7
+ import sys
7
8
  import time
8
9
  import types
9
10
  import typing
@@ -11,6 +12,7 @@ from contextlib import suppress
11
12
  from seleniumbase import config as sb_config
12
13
  from seleniumbase.config import settings
13
14
  from seleniumbase.core import detect_b_ver
15
+ from seleniumbase.core import proxy_helper
14
16
  from seleniumbase.fixtures import constants
15
17
  from seleniumbase.fixtures import shared_utils
16
18
  from typing import Optional, List, Union, Callable
@@ -23,6 +25,7 @@ import mycdp as cdp
23
25
 
24
26
  logger = logging.getLogger(__name__)
25
27
  IS_LINUX = shared_utils.is_linux()
28
+ PROXY_DIR_LOCK = proxy_helper.PROXY_DIR_LOCK
26
29
  T = typing.TypeVar("T")
27
30
 
28
31
 
@@ -139,6 +142,85 @@ def __activate_virtual_display_as_needed(
139
142
  __activate_standard_virtual_display()
140
143
 
141
144
 
145
+ def __set_proxy_filenames():
146
+ DOWNLOADS_DIR = constants.Files.DOWNLOADS_FOLDER
147
+ for num in range(1000):
148
+ PROXY_DIR_PATH = os.path.join(DOWNLOADS_DIR, "proxy_ext_dir_%s" % num)
149
+ if os.path.exists(PROXY_DIR_PATH):
150
+ continue
151
+ proxy_helper.PROXY_DIR_PATH = PROXY_DIR_PATH
152
+ return
153
+ # Exceeded upper bound. Use Defaults:
154
+ PROXY_DIR_PATH = os.path.join(DOWNLOADS_DIR, "proxy_ext_dir")
155
+ proxy_helper.PROXY_DIR_PATH = PROXY_DIR_PATH
156
+
157
+
158
+ def __add_chrome_ext_dir(extension_dir, dir_path):
159
+ # Add dir_path to the existing extension_dir
160
+ option_exists = False
161
+ if extension_dir:
162
+ option_exists = True
163
+ extension_dir = "%s,%s" % (
164
+ extension_dir, os.path.realpath(dir_path)
165
+ )
166
+ if not option_exists:
167
+ extension_dir = os.path.realpath(dir_path)
168
+ return extension_dir
169
+
170
+
171
+ def __add_chrome_proxy_extension(
172
+ extension_dir,
173
+ proxy_string,
174
+ proxy_user,
175
+ proxy_pass,
176
+ proxy_bypass_list=None,
177
+ multi_proxy=False,
178
+ ):
179
+ """Implementation of https://stackoverflow.com/a/35293284/7058266
180
+ for https://stackoverflow.com/q/12848327/7058266
181
+ (Run Selenium on a proxy server that requires authentication.)"""
182
+ args = " ".join(sys.argv)
183
+ bypass_list = proxy_bypass_list
184
+ if (
185
+ not ("-n" in sys.argv or " -n=" in args or args == "-c")
186
+ and not multi_proxy
187
+ ):
188
+ # Single-threaded
189
+ proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK)
190
+ with proxy_dir_lock:
191
+ proxy_helper.create_proxy_ext(
192
+ proxy_string,
193
+ proxy_user,
194
+ proxy_pass,
195
+ bypass_list,
196
+ zip_it=False,
197
+ )
198
+ proxy_dir_path = proxy_helper.PROXY_DIR_PATH
199
+ extension_dir = __add_chrome_ext_dir(
200
+ extension_dir, proxy_dir_path
201
+ )
202
+ else:
203
+ # Multi-threaded
204
+ proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK)
205
+ with proxy_dir_lock:
206
+ with suppress(Exception):
207
+ shared_utils.make_writable(PROXY_DIR_LOCK)
208
+ if multi_proxy:
209
+ __set_proxy_filenames()
210
+ if not os.path.exists(proxy_helper.PROXY_DIR_PATH):
211
+ proxy_helper.create_proxy_ext(
212
+ proxy_string,
213
+ proxy_user,
214
+ proxy_pass,
215
+ bypass_list,
216
+ zip_it=False,
217
+ )
218
+ extension_dir = __add_chrome_ext_dir(
219
+ extension_dir, proxy_helper.PROXY_DIR_PATH
220
+ )
221
+ return extension_dir
222
+
223
+
142
224
  async def start(
143
225
  config: Optional[Config] = None,
144
226
  *,
@@ -156,6 +238,8 @@ async def start(
156
238
  xvfb: Optional[int] = None, # Use a special virtual display on Linux
157
239
  headed: Optional[bool] = None, # Override default Xvfb mode on Linux
158
240
  expert: Optional[bool] = None, # Open up closed Shadow-root elements
241
+ proxy: Optional[str] = None, # "host:port" or "user:pass@host:port"
242
+ extension_dir: Optional[str] = None, # Chrome extension directory
159
243
  **kwargs: Optional[dict],
160
244
  ) -> Browser:
161
245
  """
@@ -200,6 +284,18 @@ async def start(
200
284
  if IS_LINUX and not headless and not headed and not xvfb:
201
285
  xvfb = True # The default setting on Linux
202
286
  __activate_virtual_display_as_needed(headless, headed, xvfb, xvfb_metrics)
287
+ if proxy and "@" in str(proxy):
288
+ user_with_pass = proxy.split("@")[0]
289
+ if ":" in user_with_pass:
290
+ proxy_user = user_with_pass.split(":")[0]
291
+ proxy_pass = user_with_pass.split(":")[1]
292
+ proxy_string = proxy.split("@")[1]
293
+ extension_dir = __add_chrome_proxy_extension(
294
+ extension_dir,
295
+ proxy_string,
296
+ proxy_user,
297
+ proxy_pass,
298
+ )
203
299
  if not config:
204
300
  config = Config(
205
301
  user_data_dir,
@@ -213,13 +309,19 @@ async def start(
213
309
  host=host,
214
310
  port=port,
215
311
  expert=expert,
312
+ proxy=proxy,
313
+ extension_dir=extension_dir,
216
314
  **kwargs,
217
315
  )
316
+ driver = None
218
317
  try:
219
- return await Browser.create(config)
318
+ driver = await Browser.create(config)
220
319
  except Exception:
221
320
  time.sleep(0.15)
222
- return await Browser.create(config)
321
+ driver = await Browser.create(config)
322
+ if proxy and "@" in str(proxy):
323
+ time.sleep(0.11)
324
+ return driver
223
325
 
224
326
 
225
327
  async def start_async(*args, **kwargs) -> Browser:
@@ -40,6 +40,8 @@ class Config:
40
40
  host: str = AUTO,
41
41
  port: int = AUTO,
42
42
  expert: bool = AUTO,
43
+ proxy: Optional[str] = None,
44
+ extension_dir: Optional[str] = None,
43
45
  **kwargs: dict,
44
46
  ):
45
47
  """
@@ -91,6 +93,8 @@ class Config:
91
93
  self.host = host
92
94
  self.port = port
93
95
  self.expert = expert
96
+ self.proxy = proxy
97
+ self.extension_dir = extension_dir
94
98
  self._extensions = []
95
99
  # When using posix-ish operating system and running as root,
96
100
  # you must use no_sandbox=True
@@ -195,6 +199,12 @@ class Config:
195
199
  "--disable-web-security",
196
200
  "--disable-site-isolation-trials",
197
201
  ]
202
+ if self.proxy:
203
+ args.append("--proxy-server=%s" % self.proxy.split("@")[-1])
204
+ args.append("--ignore-certificate-errors")
205
+ args.append("--ignore-ssl-errors=yes")
206
+ if self.extension_dir:
207
+ args.append("--load-extension=%s" % self.extension_dir)
198
208
  if self._browser_args:
199
209
  args.extend([arg for arg in self._browser_args if arg not in args])
200
210
  if self.headless:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: seleniumbase
3
- Version: 4.34.15
3
+ Version: 4.35.1
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
@@ -68,12 +68,12 @@ Requires-Dist: attrs>=25.1.0
68
68
  Requires-Dist: certifi>=2025.1.31
69
69
  Requires-Dist: exceptiongroup>=1.2.2
70
70
  Requires-Dist: websockets~=13.1; python_version < "3.9"
71
- Requires-Dist: websockets>=14.2; python_version >= "3.9"
71
+ Requires-Dist: websockets>=15.0; python_version >= "3.9"
72
72
  Requires-Dist: filelock~=3.16.1; python_version < "3.9"
73
73
  Requires-Dist: filelock>=3.17.0; python_version >= "3.9"
74
74
  Requires-Dist: fasteners>=0.19
75
75
  Requires-Dist: mycdp>=1.1.0
76
- Requires-Dist: pynose>=1.5.3
76
+ Requires-Dist: pynose>=1.5.4
77
77
  Requires-Dist: platformdirs>=4.3.6
78
78
  Requires-Dist: typing-extensions>=4.12.2
79
79
  Requires-Dist: sbvirtualdisplay>=1.4.0
@@ -99,12 +99,12 @@ Requires-Dist: sniffio==1.3.1
99
99
  Requires-Dist: h11==0.14.0
100
100
  Requires-Dist: outcome==1.3.0.post0
101
101
  Requires-Dist: trio==0.27.0; python_version < "3.9"
102
- Requires-Dist: trio==0.28.0; python_version >= "3.9"
103
- Requires-Dist: trio-websocket==0.11.1
102
+ Requires-Dist: trio==0.29.0; python_version >= "3.9"
103
+ Requires-Dist: trio-websocket==0.12.1
104
104
  Requires-Dist: wsproto==1.2.0
105
105
  Requires-Dist: websocket-client==1.8.0
106
106
  Requires-Dist: selenium==4.27.1; python_version < "3.9"
107
- Requires-Dist: selenium==4.28.1; python_version >= "3.9"
107
+ Requires-Dist: selenium==4.29.0; python_version >= "3.9"
108
108
  Requires-Dist: cssselect==1.2.0
109
109
  Requires-Dist: sortedcontainers==2.4.0
110
110
  Requires-Dist: execnet==2.1.1
@@ -137,7 +137,7 @@ Requires-Dist: pytest-cov>=5.0.0; python_version < "3.9" and extra == "coverage"
137
137
  Requires-Dist: pytest-cov>=6.0.0; python_version >= "3.9" and extra == "coverage"
138
138
  Provides-Extra: flake8
139
139
  Requires-Dist: flake8==5.0.4; python_version < "3.9" and extra == "flake8"
140
- Requires-Dist: flake8==7.1.1; python_version >= "3.9" and extra == "flake8"
140
+ Requires-Dist: flake8==7.1.2; python_version >= "3.9" and extra == "flake8"
141
141
  Requires-Dist: mccabe==0.7.0; extra == "flake8"
142
142
  Requires-Dist: pyflakes==2.5.0; python_version < "3.9" and extra == "flake8"
143
143
  Requires-Dist: pyflakes==3.2.0; python_version >= "3.9" and extra == "flake8"
@@ -265,7 +265,7 @@ Dynamic: summary
265
265
  ```python
266
266
  from seleniumbase import SB
267
267
 
268
- with SB(test=True) as sb:
268
+ with SB(test=True, uc=True) as sb:
269
269
  sb.open("https://google.com/ncr")
270
270
  sb.type('[title="Search"]', "SeleniumBase GitHub page\n")
271
271
  sb.click('[href*="github.com/seleniumbase/"]')
@@ -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=HQYREOtXe_KHVKfcnng511WpKG975mX8PLrqOajrkgI,47
6
+ seleniumbase/__version__.py,sha256=Db8lU-34AfPk_0zWgBNe5iBhYA1JOuCwJ1W2-rdR45g,46
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=qQF85LoohJBfrPK5ZcPi50v-pWtOrC9qcN1B3Ki_3tY,59401
@@ -27,7 +27,7 @@ seleniumbase/console_scripts/sb_caseplans.py,sha256=qlmvjQ49bOBE1Q29fVmabimkVibC
27
27
  seleniumbase/console_scripts/sb_commander.py,sha256=exQGKzqRAoGqRmQtDmlmoHnSG9eSy9eh8HVy-tXw6s4,13343
28
28
  seleniumbase/console_scripts/sb_install.py,sha256=RdByOmTQebibRFk5ym-FtFr0Gf91_ZfOsBB2MN5izUc,55524
29
29
  seleniumbase/console_scripts/sb_mkchart.py,sha256=ep9g-9CSIwaOJKVxhB3xjRQpfsuApyN8-Dr129cNXwQ,10941
30
- seleniumbase/console_scripts/sb_mkdir.py,sha256=TpXjsG-SZE496EFhbR1aJr5tcHRBo57QSmA9nfpxNXs,30713
30
+ seleniumbase/console_scripts/sb_mkdir.py,sha256=iFN6ZMOgmH_GAvEuvzYltWuYiS3PRFQcL5fJIj1yX_Y,30897
31
31
  seleniumbase/console_scripts/sb_mkfile.py,sha256=OWYd4yFccmjrd-gNn1t1una-HDRU2_N2-r4Tg3nHsj0,17744
32
32
  seleniumbase/console_scripts/sb_mkpres.py,sha256=EWFRVacjYTX49y-fEiYTZacM9_01IxuuaO4nMjHrIGo,11015
33
33
  seleniumbase/console_scripts/sb_mkrec.py,sha256=PrizjTmyrROYPO0yDm-zQS3QSfsZNeAmcJKKUvfgLhc,11966
@@ -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=ahPk232q2gp5H55X5uH1B93IWvFioeuuoGlahgbnNWo,235352
39
+ seleniumbase/core/browser_launcher.py,sha256=jUDL2RlOXnSDUwhQuZekgGisY11hhmMU-ftFVyM-afs,236519
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
@@ -50,7 +50,7 @@ seleniumbase/core/proxy_helper.py,sha256=4VkpMwavz0fx8wcOqJ_jyBT0HIXwcxmAcpd1gjJ
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=r9sv8WjwLhS_AFLn4QGLvbK4osfaC6ncRJRj0asrBvI,76754
53
+ seleniumbase/core/sb_cdp.py,sha256=rfMcBNGY90qfusHyTJzexWk6wHv9GTH9klQspgxENHE,81554
54
54
  seleniumbase/core/sb_driver.py,sha256=yvTDRblBzG6bDX7XcLiAA6QcBelSJj_HHL_04lcfeeE,13760
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,11 +65,11 @@ seleniumbase/extensions/disable_csp.zip,sha256=5RvomXnm2PdivUVcxTV6jfvD8WhTEsQYH
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=0bW99a1g8YH5YCXLnsJvZ5uSXvLOmY6QutDEpxDjX9U,721561
68
+ seleniumbase/fixtures/base_case.py,sha256=ncejN3q7m6_GHQTASuy4LHkeeCn829RRZN4lRB0-MkA,722341
69
69
  seleniumbase/fixtures/constants.py,sha256=WMrItuNyKq3XVJ64NluLIRc4gJCxDw8K8qXED0b9S2w,13752
70
70
  seleniumbase/fixtures/css_to_xpath.py,sha256=9ouDB1xl4MJ2os6JOgTIAyHKOQfuxtxvXC3O5hSnEKA,1954
71
71
  seleniumbase/fixtures/errors.py,sha256=KyxuEVx_e3MPhVrJfNIa_3ltMpbCFxfy_jxK8RFNTns,555
72
- seleniumbase/fixtures/js_utils.py,sha256=asZJ0LDFv2BPQOzUVKK_9ie8ToGcmFousDnGdb3Vabg,51534
72
+ seleniumbase/fixtures/js_utils.py,sha256=Ykt019DFP8S27o8Kai_ihT01qQWo3RqHUeljqOEUdFU,52247
73
73
  seleniumbase/fixtures/page_actions.py,sha256=LPcFSkUvBkxLrOt4laQHHN-NLmqInT41E2vlPiOlLFY,66753
74
74
  seleniumbase/fixtures/page_utils.py,sha256=H1iV8f9vDyEy87DBntyiBXC_tg8HskcebUOAJVn0hxE,12160
75
75
  seleniumbase/fixtures/shared_utils.py,sha256=G6CsE-Adt-GfuZF-71jXWKSIQW7YZPx8FIM24pVd_yI,8368
@@ -83,16 +83,16 @@ seleniumbase/js_code/recorder_js.py,sha256=ApFNh6DImuPGmaydvq8OOzGixc0t-xdYSCzfQ
83
83
  seleniumbase/masterqa/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
84
  seleniumbase/masterqa/master_qa.py,sha256=jLWmAx32Rnu1IhmvrRt8BbsUIcDW5xYj2ouVozny-Y4,19258
85
85
  seleniumbase/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
86
- seleniumbase/plugins/base_plugin.py,sha256=FemdftNhOaTfQIw5bWcJQPPPGQ3P0_sMEI_YYDuzZgU,14972
86
+ seleniumbase/plugins/base_plugin.py,sha256=ItLgtaZmu_363iycy8BNX0Do5LyIWGiTMLW6krXM-WQ,14748
87
87
  seleniumbase/plugins/basic_test_info.py,sha256=8ov6n417gPbqqvrlT4zrch7l2XcRt-GF2ny6rR9AMWk,2108
88
88
  seleniumbase/plugins/db_reporting_plugin.py,sha256=En09qUCoojrk9-vbcnsoHdSELoGmag2GDIyu3jTiJas,7331
89
- seleniumbase/plugins/driver_manager.py,sha256=QGGekWvcj58VMGr87UyXl1OvVTMjZDEdt8jaq7K13u8,35863
89
+ seleniumbase/plugins/driver_manager.py,sha256=UqWDnhOFB7GNftaSAqWFsR76vOyeaqMc0JqQKiAw3lU,35877
90
90
  seleniumbase/plugins/page_source.py,sha256=loTnXxOj4kxEukuTZEiGyvKBhY3KDVDMnNlHHheTBDE,1889
91
- seleniumbase/plugins/pytest_plugin.py,sha256=SKCHzUFSd8dHPQwYQTJp-6AX7YD6Kce-MemvNguNgYs,108249
91
+ seleniumbase/plugins/pytest_plugin.py,sha256=952AIyaH-PdmNksoeXjzhXxoc8Z53yV-WPjlrHhp2OM,108382
92
92
  seleniumbase/plugins/s3_logging_plugin.py,sha256=WDfertQgGOW_SRJpFMaekYD6vBVW9VO62POtXXy2HCM,2319
93
- seleniumbase/plugins/sb_manager.py,sha256=aOaP5ZxLM7EfpLml4f_iBXkidKtFA1KcZQQIGm4aSQQ,56242
93
+ seleniumbase/plugins/sb_manager.py,sha256=9hWfSA2DGclgQ2QMJISR8k_rF53NCMGcQbr-K5eh5Dg,56249
94
94
  seleniumbase/plugins/screen_shots.py,sha256=1hrXw-hzuZ1BR6Yh7AyWX2ABnvnP73-RCbwdz958gj4,1127
95
- seleniumbase/plugins/selenium_plugin.py,sha256=qpbP4pMGGdM2s_YWWNzgZQx9pLGETW6H3_Gbx2FQ57o,60023
95
+ seleniumbase/plugins/selenium_plugin.py,sha256=y0eNco8T4KgGLProLPHPLw479QH5lRms4wqwOnTgkSc,60081
96
96
  seleniumbase/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
97
97
  seleniumbase/translate/__init__.py,sha256=N2i5XntTwJZmwr9-qvdX5gC6Rdm5-ClIbnQ8yyPn4Oo,459
98
98
  seleniumbase/translate/chinese.py,sha256=0QhK2eadtsdN4KCvwki1J7jBCe8I4xxWbKzteJKJozY,24698
@@ -115,9 +115,9 @@ seleniumbase/undetected/reactor.py,sha256=NropaXcO54pzmDq6quR27qPJxab6636H7LRAaq
115
115
  seleniumbase/undetected/webelement.py,sha256=OOpUYbEiOG52KsYYyuDW9tYLdA2SMnukvwQHUdPVn9E,1389
116
116
  seleniumbase/undetected/cdp_driver/__init__.py,sha256=c0TjMwPfVFyoqYOJ7PQ-Jln_L_dpN3ebHyaD-juQoM0,64
117
117
  seleniumbase/undetected/cdp_driver/_contradict.py,sha256=lP4b0h5quAy573ETn_TBbYV889cL1AuPLVInpJ0ZkiU,3183
118
- seleniumbase/undetected/cdp_driver/browser.py,sha256=vwccwrAZDssRmVnf-NVXYvs5Dvv8DtGzeiDHSY3RTfM,30189
119
- seleniumbase/undetected/cdp_driver/cdp_util.py,sha256=B5myq2zi-Gdq_vfZGPOG-LTSwbhK4n2h6SY2Z9dmIzQ,17784
120
- seleniumbase/undetected/cdp_driver/config.py,sha256=oHFJ3UH0OmLmEGgG5S6SZwbyBs9ZYMsbUJ02QCA7iZc,12044
118
+ seleniumbase/undetected/cdp_driver/browser.py,sha256=ruPunmJKwE67Veh1MjSkZAF5W38FMwc32lHgy1YULho,30261
119
+ seleniumbase/undetected/cdp_driver/cdp_util.py,sha256=zlYc_oTqtCSjV_T7lzPbFyJlj_78NHMxKkrpleXcfpQ,21374
120
+ seleniumbase/undetected/cdp_driver/config.py,sha256=t8KV1Vqa5SQRBq3-gjkHHmj9h85AplAM01asO3AWufs,12507
121
121
  seleniumbase/undetected/cdp_driver/connection.py,sha256=sOTUGjbUqKA2hPvDcRCdqw1VQjVGJs7mbgVvzS7ldtE,23360
122
122
  seleniumbase/undetected/cdp_driver/element.py,sha256=FIC6v7OmumLCT-_vIc3H4oju_oBbaLpWJUJIKm2c_q4,40467
123
123
  seleniumbase/undetected/cdp_driver/tab.py,sha256=rcASsWY_mC7L0wXpFSWM5mtojzA47qG9BWEMXe7ZGF8,50906
@@ -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.34.15.dist-info/LICENSE,sha256=BRblZsX7HyPUjQmYTiyWr_e9tzWvmR3R4SFclM2R3W0,1085
139
- seleniumbase-4.34.15.dist-info/METADATA,sha256=KFp4laKW_MENuhZdfslrP38qajSDf88F7Yb8HJ9loM8,86522
140
- seleniumbase-4.34.15.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
141
- seleniumbase-4.34.15.dist-info/entry_points.txt,sha256=CNrh2EKNaHYEhO6pP1RJyVLB99LkDDYX7TnUK8xfjqk,623
142
- seleniumbase-4.34.15.dist-info/top_level.txt,sha256=4N97aBOQ8ETCnDnokBsWb07lJfTaq3C1ZzYRxvLMxqU,19
143
- seleniumbase-4.34.15.dist-info/RECORD,,
138
+ seleniumbase-4.35.1.dist-info/LICENSE,sha256=BRblZsX7HyPUjQmYTiyWr_e9tzWvmR3R4SFclM2R3W0,1085
139
+ seleniumbase-4.35.1.dist-info/METADATA,sha256=-RJH6DIDAyALXSl9bXjwxbJQQhMoqi0BBjtb_vTVmCI,86530
140
+ seleniumbase-4.35.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
141
+ seleniumbase-4.35.1.dist-info/entry_points.txt,sha256=CNrh2EKNaHYEhO6pP1RJyVLB99LkDDYX7TnUK8xfjqk,623
142
+ seleniumbase-4.35.1.dist-info/top_level.txt,sha256=4N97aBOQ8ETCnDnokBsWb07lJfTaq3C1ZzYRxvLMxqU,19
143
+ seleniumbase-4.35.1.dist-info/RECORD,,