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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. seleniumbase/__version__.py +1 -1
  2. seleniumbase/behave/behave_sb.py +14 -0
  3. seleniumbase/console_scripts/run.py +1 -0
  4. seleniumbase/console_scripts/sb_install.py +142 -11
  5. seleniumbase/console_scripts/sb_mkdir.py +76 -0
  6. seleniumbase/console_scripts/sb_mkrec.py +25 -0
  7. seleniumbase/console_scripts/sb_recorder.py +40 -3
  8. seleniumbase/core/browser_launcher.py +279 -117
  9. seleniumbase/core/detect_b_ver.py +8 -6
  10. seleniumbase/core/log_helper.py +11 -16
  11. seleniumbase/core/mysql.py +1 -1
  12. seleniumbase/core/report_helper.py +3 -7
  13. seleniumbase/core/sb_cdp.py +275 -78
  14. seleniumbase/core/sb_driver.py +36 -5
  15. seleniumbase/core/session_helper.py +2 -4
  16. seleniumbase/drivers/chromium_drivers/__init__.py +0 -0
  17. seleniumbase/fixtures/base_case.py +251 -201
  18. seleniumbase/fixtures/constants.py +1 -0
  19. seleniumbase/fixtures/js_utils.py +52 -14
  20. seleniumbase/fixtures/page_actions.py +18 -7
  21. seleniumbase/fixtures/page_utils.py +4 -2
  22. seleniumbase/fixtures/shared_utils.py +2 -4
  23. seleniumbase/masterqa/master_qa.py +16 -2
  24. seleniumbase/plugins/base_plugin.py +8 -0
  25. seleniumbase/plugins/driver_manager.py +15 -5
  26. seleniumbase/plugins/pytest_plugin.py +43 -57
  27. seleniumbase/plugins/sb_manager.py +23 -19
  28. seleniumbase/plugins/selenium_plugin.py +20 -13
  29. seleniumbase/undetected/__init__.py +11 -10
  30. seleniumbase/undetected/cdp.py +1 -12
  31. seleniumbase/undetected/cdp_driver/browser.py +330 -128
  32. seleniumbase/undetected/cdp_driver/cdp_util.py +48 -14
  33. seleniumbase/undetected/cdp_driver/config.py +78 -11
  34. seleniumbase/undetected/cdp_driver/connection.py +15 -43
  35. seleniumbase/undetected/cdp_driver/element.py +37 -22
  36. seleniumbase/undetected/cdp_driver/tab.py +414 -39
  37. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/METADATA +140 -152
  38. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/RECORD +42 -41
  39. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/licenses/LICENSE +1 -1
  40. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/WHEEL +0 -0
  41. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/entry_points.txt +0 -0
  42. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,9 @@
1
1
  """Add CDP methods to extend the driver"""
2
2
  import asyncio
3
3
  import fasteners
4
+ import mycdp
4
5
  import os
6
+ import random
5
7
  import re
6
8
  import sys
7
9
  import time
@@ -14,6 +16,7 @@ from seleniumbase.fixtures import js_utils
14
16
  from seleniumbase.fixtures import page_utils
15
17
  from seleniumbase.fixtures import shared_utils
16
18
  from seleniumbase.undetected.cdp_driver import cdp_util
19
+ from seleniumbase.undetected.cdp_driver import tab as cdp_tab
17
20
 
18
21
 
19
22
  class CDPMethods():
@@ -112,7 +115,18 @@ class CDPMethods():
112
115
  driver = self.driver
113
116
  if hasattr(driver, "cdp_base"):
114
117
  driver = driver.cdp_base
115
- self.loop.run_until_complete(self.page.get(url, **kwargs))
118
+ load_timeout = 60.0
119
+ wait_timeout = 30.0
120
+ if hasattr(sb_config, "_cdp_proxy") and sb_config._cdp_proxy:
121
+ load_timeout = 90.0
122
+ wait_timeout = 45.0
123
+ try:
124
+ task = self.page.get(url, **kwargs)
125
+ self.loop.run_until_complete(
126
+ asyncio.wait_for(task, timeout=load_timeout)
127
+ )
128
+ except asyncio.TimeoutError:
129
+ print("Timeout loading %s" % url)
116
130
  url_protocol = url.split(":")[0]
117
131
  safe_url = True
118
132
  if url_protocol not in ["about", "data", "chrome"]:
@@ -124,7 +138,14 @@ class CDPMethods():
124
138
  else:
125
139
  time.sleep(0.012)
126
140
  self.__slow_mode_pause_if_set()
127
- self.loop.run_until_complete(self.page.wait())
141
+ try:
142
+ self.loop.run_until_complete(
143
+ asyncio.wait_for(self.page.wait(), timeout=wait_timeout)
144
+ )
145
+ except asyncio.TimeoutError:
146
+ pass
147
+ except Exception:
148
+ pass
128
149
 
129
150
  def open(self, url, **kwargs):
130
151
  self.get(url, **kwargs)
@@ -143,6 +164,48 @@ class CDPMethods():
143
164
  def get_event_loop(self):
144
165
  return self.loop
145
166
 
167
+ def get_rd_host(self):
168
+ """Returns the remote-debugging host (likely 127.0.0.1)"""
169
+ driver = self.driver
170
+ if hasattr(driver, "cdp_base"):
171
+ driver = driver.cdp_base
172
+ return driver.config.host
173
+
174
+ def get_rd_port(self):
175
+ """Returns the remote-debugging port (commonly 9222)"""
176
+ driver = self.driver
177
+ if hasattr(driver, "cdp_base"):
178
+ driver = driver.cdp_base
179
+ return driver.config.port
180
+
181
+ def get_rd_url(self):
182
+ """Returns the remote-debugging URL, which is used for
183
+ allowing the Playwright integration to launch stealthy,
184
+ and also applies nest-asyncio for nested event loops so
185
+ that SeleniumBase methods can be called from Playwright
186
+ without encountering event loop error messages such as:
187
+ Cannot run the event loop while another loop is running.
188
+ Also sets an environment variable to hide this warning:
189
+ Deprecation: "url.parse() behavior is not standardized".
190
+ (github.com/microsoft/playwright-python/issues/3016)"""
191
+ import nest_asyncio
192
+ nest_asyncio.apply()
193
+ os.environ["NODE_NO_WARNINGS"] = "1"
194
+ driver = self.driver
195
+ if hasattr(driver, "cdp_base"):
196
+ driver = driver.cdp_base
197
+ host = driver.config.host
198
+ port = driver.config.port
199
+ return f"http://{host}:{port}"
200
+
201
+ def get_endpoint_url(self):
202
+ """Same as get_rd_url(), which returns the remote-debugging URL."""
203
+ return self.get_rd_url()
204
+
205
+ def get_port(self):
206
+ """Same as get_rd_port(), which returns the remote-debugging port."""
207
+ return self.get_rd_port()
208
+
146
209
  def add_handler(self, event, handler):
147
210
  self.page.add_handler(event, handler)
148
211
 
@@ -479,7 +542,7 @@ class CDPMethods():
479
542
  text = text[:-1]
480
543
  for key in text:
481
544
  element.send_keys(key)
482
- time.sleep(0.044)
545
+ time.sleep(float(0.042 + (random.random() / 110.0)))
483
546
  if submit:
484
547
  element.send_keys("\r\n")
485
548
  time.sleep(0.044)
@@ -707,10 +770,15 @@ class CDPMethods():
707
770
  if tag_name:
708
771
  tag_name = tag_name.lower().strip()
709
772
  if (
710
- tag_name in ["a", "button", "canvas", "div", "input", "li", "span"]
773
+ tag_name in [
774
+ "a", "button", "canvas", "div", "input", "li", "span", "label"
775
+ ]
711
776
  and "contains(" not in selector
712
777
  ):
713
- element.mouse_click() # Simulated click (NOT PyAutoGUI)
778
+ try:
779
+ element.mouse_click() # Simulated click (NOT PyAutoGUI)
780
+ except Exception:
781
+ element.click() # Standard CDP click
714
782
  else:
715
783
  element.click() # Standard CDP click
716
784
  self.__slow_mode_pause_if_set()
@@ -938,7 +1006,7 @@ class CDPMethods():
938
1006
  text = text.replace("\n", "\r")
939
1007
  for key in text:
940
1008
  element.send_keys(key)
941
- time.sleep(0.044)
1009
+ time.sleep(float(0.042 + (random.random() / 110.0)))
942
1010
  if submit:
943
1011
  element.send_keys("\r\n")
944
1012
  time.sleep(0.044)
@@ -1120,10 +1188,74 @@ class CDPMethods():
1120
1188
  def switch_to_newest_window(self):
1121
1189
  self.switch_to_tab(-1)
1122
1190
 
1123
- def open_new_tab(self, url=None, switch_to=True):
1191
+ def open_new_tab(self, url=None, switch_to=True, **kwargs):
1192
+ driver = self.driver
1124
1193
  if not isinstance(url, str):
1125
1194
  url = "about:blank"
1126
- self.loop.run_until_complete(self.page.get(url, new_tab=True))
1195
+ if hasattr(driver, "cdp_base"):
1196
+ try:
1197
+ self.loop.run_until_complete(
1198
+ self.page.get(url, new_tab=True, **kwargs)
1199
+ )
1200
+ except Exception:
1201
+ original_targets = self.loop.run_until_complete(
1202
+ self.page.send(mycdp.target.get_targets())
1203
+ )
1204
+ tab_url = driver.cdp_base.tabs[0].websocket_url
1205
+ if not self.driver.is_connected():
1206
+ self.driver.connect()
1207
+ self.driver.open_new_tab()
1208
+ targets = self.loop.run_until_complete(
1209
+ self.page.send(mycdp.target.get_targets())
1210
+ )
1211
+ new_targets = []
1212
+ for target in targets:
1213
+ if target not in original_targets:
1214
+ new_targets.append(target)
1215
+ if new_targets:
1216
+ found_target = new_targets[0]
1217
+ t_str = str(new_targets[0])
1218
+ target_id = (
1219
+ t_str.split("target_id=TargetID('")[-1].split("')")[0]
1220
+ )
1221
+ pre_tab_url = tab_url.split("/page/")[0] + "/page/"
1222
+ new_tab_url = pre_tab_url + target_id
1223
+ new_tab = cdp_tab.Tab(
1224
+ new_tab_url, found_target, driver.cdp_base
1225
+ )
1226
+ driver.cdp_base.targets.append(new_tab)
1227
+ driver.cdp_base.tabs.append(new_tab)
1228
+ self.driver.disconnect()
1229
+ self.switch_to_newest_tab()
1230
+ self.open(url)
1231
+ return
1232
+ elif getattr(sb_config, "guest_mode", None):
1233
+ print(" open_new_tab() failed! (Known Guest Mode issue)")
1234
+ if switch_to:
1235
+ self.switch_to_newest_tab()
1236
+ return
1237
+
1238
+ target_id = self.loop.run_until_complete(
1239
+ self.page.send(mycdp.target.create_target(url))
1240
+ )
1241
+ if not target_id and getattr(sb_config, "guest_mode", None):
1242
+ print(" open_new_tab() failed! (Known Guest Mode issue)")
1243
+ found_target = None
1244
+ targets = self.loop.run_until_complete(
1245
+ self.page.send(mycdp.target.get_targets())
1246
+ )
1247
+ if target_id:
1248
+ for target in targets:
1249
+ if str(target_id) in str(target):
1250
+ found_target = target
1251
+ break
1252
+ if found_target:
1253
+ tab_url = driver.tabs[0].websocket_url
1254
+ pre_tab_url = tab_url.split("/page/")[0] + "/page/"
1255
+ new_tab_url = pre_tab_url + target_id
1256
+ new_tab = cdp_tab.Tab(new_tab_url, found_target, driver)
1257
+ driver.targets.append(new_tab)
1258
+ driver.tabs.append(new_tab)
1127
1259
  if switch_to:
1128
1260
  self.switch_to_newest_tab()
1129
1261
 
@@ -1185,7 +1317,14 @@ class CDPMethods():
1185
1317
  self.page.evaluate("window.location.origin")
1186
1318
  )
1187
1319
 
1188
- def get_page_source(self):
1320
+ def get_html(self, include_shadow_dom=True):
1321
+ return self.get_page_source(
1322
+ include_shadow_dom=include_shadow_dom
1323
+ )
1324
+
1325
+ def get_page_source(self, include_shadow_dom=True):
1326
+ if include_shadow_dom:
1327
+ return self.find_element("html").get_html()
1189
1328
  try:
1190
1329
  source = self.loop.run_until_complete(
1191
1330
  self.page.evaluate("document.documentElement.outerHTML")
@@ -1527,13 +1666,15 @@ class CDPMethods():
1527
1666
  css_selector = self.__convert_to_css_if_xpath(selector)
1528
1667
  css_selector = re.escape(css_selector) # Add "\\" to special chars
1529
1668
  css_selector = js_utils.escape_quotes_if_needed(css_selector)
1530
- js_code = """var $elements = document.querySelectorAll('%s');
1531
- var index = 0, length = $elements.length;
1532
- for(; index < length; index++){
1533
- $elements[index].setAttribute('%s','%s');}""" % (
1534
- css_selector,
1535
- attribute,
1536
- value,
1669
+ js_code = (
1670
+ """var $elements = document.querySelectorAll('%s');
1671
+ var index = 0, length = $elements.length;
1672
+ for(; index < length; index++){
1673
+ $elements[index].setAttribute('%s','%s');}""" % (
1674
+ css_selector,
1675
+ attribute,
1676
+ value,
1677
+ )
1537
1678
  )
1538
1679
  with suppress(Exception):
1539
1680
  self.loop.run_until_complete(self.page.evaluate(js_code))
@@ -1692,7 +1833,7 @@ class CDPMethods():
1692
1833
  self.__make_sure_pyautogui_lock_is_writable()
1693
1834
  for key in keys:
1694
1835
  pyautogui.press(key)
1695
- time.sleep(0.044)
1836
+ time.sleep(float(0.042 + (random.random() / 110.0)))
1696
1837
  self.__slow_mode_pause_if_set()
1697
1838
  self.loop.run_until_complete(self.page.sleep(0.025))
1698
1839
 
@@ -1796,6 +1937,16 @@ class CDPMethods():
1796
1937
  def click_with_offset(self, selector, x, y, center=False):
1797
1938
  element = self.find_element(selector)
1798
1939
  element.scroll_into_view()
1940
+ if "--debug" in sys.argv:
1941
+ displayed_selector = "`%s`" % selector
1942
+ if '"' not in selector:
1943
+ displayed_selector = '"%s"' % selector
1944
+ elif "'" not in selector:
1945
+ displayed_selector = "'%s'" % selector
1946
+ print(
1947
+ " <DEBUG> sb.click_with_offset(%s, %s, %s, center=%s)"
1948
+ % (displayed_selector, x, y, center)
1949
+ )
1799
1950
  element.click_with_offset(x=x, y=y, center=center)
1800
1951
  self.__slow_mode_pause_if_set()
1801
1952
  self.loop.run_until_complete(self.page.wait())
@@ -1805,8 +1956,12 @@ class CDPMethods():
1805
1956
  time.sleep(0.2)
1806
1957
  source = self.get_page_source()
1807
1958
  if (
1808
- 'data-callback="onCaptchaSuccess"' in source
1809
- or "/challenge-platform/scripts/" in source
1959
+ (
1960
+ 'data-callback="onCaptchaSuccess"' in source
1961
+ and 'title="reCAPTCHA"' not in source
1962
+ and 'id="recaptcha-token"' not in source
1963
+ )
1964
+ or "/challenge-platform/h/b/" in source
1810
1965
  or 'id="challenge-widget-' in source
1811
1966
  or "challenges.cloudf" in source
1812
1967
  or "cf-turnstile-" in source
@@ -1814,17 +1969,28 @@ class CDPMethods():
1814
1969
  return True
1815
1970
  return False
1816
1971
 
1817
- def _on_a_g_recaptcha_page(self, source=None):
1818
- if not source or len(source) < 400:
1819
- time.sleep(0.2)
1820
- source = self.get_page_source()
1972
+ def _on_a_g_recaptcha_page(self, *args, **kwargs):
1973
+ time.sleep(0.4) # reCAPTCHA may need a moment to appear
1974
+ self.loop.run_until_complete(self.page.wait())
1975
+ source = self.get_page_source()
1821
1976
  if (
1822
- 'id="recaptcha-token"' in source
1823
- or 'title="reCAPTCHA"' in source
1977
+ (
1978
+ 'id="recaptcha-token"' in source
1979
+ or 'title="reCAPTCHA"' in source
1980
+ )
1981
+ and self.is_element_visible('iframe[title="reCAPTCHA"]')
1824
1982
  ):
1983
+ try:
1984
+ self.loop.run_until_complete(self.page.wait(0.1))
1985
+ except Exception:
1986
+ time.sleep(0.1)
1825
1987
  return True
1826
- elif "/recaptcha/api.js" in source:
1988
+ elif "com/recaptcha/api.js" in source:
1827
1989
  time.sleep(1.6) # Still loading
1990
+ try:
1991
+ self.loop.run_until_complete(self.page.wait(0.1))
1992
+ except Exception:
1993
+ time.sleep(0.1)
1828
1994
  return True
1829
1995
  return False
1830
1996
 
@@ -1834,8 +2000,10 @@ class CDPMethods():
1834
2000
  selector = 'iframe[title="reCAPTCHA"]'
1835
2001
  else:
1836
2002
  return
2003
+ time.sleep(0.25)
2004
+ self.loop.run_until_complete(self.page.wait())
2005
+ time.sleep(0.25)
1837
2006
  with suppress(Exception):
1838
- time.sleep(0.08)
1839
2007
  element_rect = self.get_gui_element_rect(selector, timeout=1)
1840
2008
  e_x = element_rect["x"]
1841
2009
  e_y = element_rect["y"]
@@ -1861,51 +2029,41 @@ class CDPMethods():
1861
2029
  def solve_captcha(self):
1862
2030
  self.__click_captcha(use_cdp=True)
1863
2031
 
2032
+ def click_captcha(self):
2033
+ """Same as solve_captcha()"""
2034
+ self.__click_captcha(use_cdp=True)
2035
+
1864
2036
  def gui_click_captcha(self):
2037
+ """Use PyAutoGUI to click the CAPTCHA"""
1865
2038
  self.__click_captcha(use_cdp=False)
1866
2039
 
1867
2040
  def __click_captcha(self, use_cdp=False):
1868
2041
  """Uses PyAutoGUI unless use_cdp == True"""
1869
- self.sleep(0.056)
2042
+ self.sleep(0.075)
2043
+ self.loop.run_until_complete(self.page.wait())
2044
+ self.sleep(0.025)
1870
2045
  source = self.get_page_source()
1871
- if self._on_a_g_recaptcha_page(source):
2046
+ if self._on_a_cf_turnstile_page(source):
2047
+ pass
2048
+ elif self._on_a_g_recaptcha_page(source):
1872
2049
  self.__gui_click_recaptcha(use_cdp)
1873
2050
  return
1874
- elif not self._on_a_cf_turnstile_page(source):
2051
+ else:
1875
2052
  return
1876
2053
  selector = None
1877
- if (
1878
- self.is_element_present('[name*="cf-turnstile-"]')
1879
- and self.is_element_present("#challenge-form div > div")
1880
- ):
2054
+ if self.is_element_present('[class="cf-turnstile"]'):
2055
+ selector = '[class="cf-turnstile"]'
2056
+ elif self.is_element_present("#challenge-form div > div"):
1881
2057
  selector = "#challenge-form div > div"
1882
- elif (
1883
- self.is_element_present('[name*="cf-turnstile-"]')
1884
- and self.is_element_present(
1885
- '[style="display: grid;"] div div'
1886
- )
1887
- ):
2058
+ elif self.is_element_present('[style="display: grid;"] div div'):
1888
2059
  selector = '[style="display: grid;"] div div'
1889
- elif (
1890
- self.is_element_present('[name*="cf-turnstile-"]')
1891
- and self.is_element_present("[class*=spacer] + div div")
1892
- ):
2060
+ elif self.is_element_present("[class*=spacer] + div div"):
1893
2061
  selector = '[class*=spacer] + div div'
1894
- elif (
1895
- self.is_element_present('[name*="cf-turnstile-"]')
1896
- and self.is_element_present(".spacer div:not([class])")
1897
- ):
2062
+ elif self.is_element_present(".spacer div:not([class])"):
1898
2063
  selector = ".spacer div:not([class])"
1899
- elif (
1900
- self.is_element_present('script[src*="challenges.c"]')
1901
- and self.is_element_present(
1902
- '[data-testid*="challenge-"] div'
1903
- )
1904
- ):
2064
+ elif self.is_element_present('[data-testid*="challenge-"] div'):
1905
2065
  selector = '[data-testid*="challenge-"] div'
1906
- elif self.is_element_present(
1907
- "div#turnstile-widget div:not([class])"
1908
- ):
2066
+ elif self.is_element_present("div#turnstile-widget div:not([class])"):
1909
2067
  selector = "div#turnstile-widget div:not([class])"
1910
2068
  elif self.is_element_present("ngx-turnstile div:not([class])"):
1911
2069
  selector = "ngx-turnstile div:not([class])"
@@ -1913,22 +2071,12 @@ class CDPMethods():
1913
2071
  'form div:not([class]):has(input[name*="cf-turn"])'
1914
2072
  ):
1915
2073
  selector = 'form div:not([class]):has(input[name*="cf-turn"])'
1916
- elif (
1917
- self.is_element_present('[src*="/turnstile/"]')
1918
- and self.is_element_present("form div:not(:has(*))")
1919
- ):
2074
+ elif self.is_element_present("form div:not(:has(*))"):
1920
2075
  selector = "form div:not(:has(*))"
1921
- elif (
1922
- self.is_element_present('[src*="/turnstile/"]')
1923
- and self.is_element_present(
1924
- "body > div#check > div:not([class])"
1925
- )
1926
- ):
2076
+ elif self.is_element_present("body > div#check > div:not([class])"):
1927
2077
  selector = "body > div#check > div:not([class])"
1928
2078
  elif self.is_element_present(".cf-turnstile-wrapper"):
1929
2079
  selector = ".cf-turnstile-wrapper"
1930
- elif self.is_element_present('[class="cf-turnstile"]'):
1931
- selector = '[class="cf-turnstile"]'
1932
2080
  elif self.is_element_present(
1933
2081
  '[id*="turnstile"] div:not([class])'
1934
2082
  ):
@@ -1941,6 +2089,10 @@ class CDPMethods():
1941
2089
  '[data-callback="onCaptchaSuccess"]'
1942
2090
  ):
1943
2091
  selector = '[data-callback="onCaptchaSuccess"]'
2092
+ elif self.is_element_present(
2093
+ "div:not([class]) > div:not([class])"
2094
+ ):
2095
+ selector = "div:not([class]) > div:not([class])"
1944
2096
  else:
1945
2097
  return
1946
2098
  if not selector:
@@ -2028,7 +2180,7 @@ class CDPMethods():
2028
2180
  self.loop.run_until_complete(self.page.evaluate(script))
2029
2181
  self.loop.run_until_complete(self.page.wait())
2030
2182
  with suppress(Exception):
2031
- time.sleep(0.08)
2183
+ time.sleep(0.05)
2032
2184
  element_rect = self.get_gui_element_rect(selector, timeout=1)
2033
2185
  e_x = element_rect["x"]
2034
2186
  e_y = element_rect["y"]
@@ -2039,15 +2191,17 @@ class CDPMethods():
2039
2191
  x = e_x + x_offset
2040
2192
  y = e_y + y_offset
2041
2193
  sb_config._saved_cf_x_y = (x, y)
2042
- time.sleep(0.08)
2194
+ time.sleep(0.05)
2195
+ if hasattr(sb_config, "_cdp_proxy") and sb_config._cdp_proxy:
2196
+ time.sleep(0.22) # CAPTCHA may load slower with proxy
2043
2197
  if use_cdp:
2044
2198
  self.sleep(0.03)
2045
2199
  gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
2046
2200
  with gui_lock: # Prevent issues with multiple processes
2047
2201
  self.bring_active_window_to_front()
2048
- time.sleep(0.056)
2202
+ time.sleep(0.05)
2049
2203
  self.click_with_offset(selector, x_offset, y_offset)
2050
- time.sleep(0.056)
2204
+ time.sleep(0.05)
2051
2205
  else:
2052
2206
  self.gui_click_x_y(x, y)
2053
2207
 
@@ -2071,13 +2225,17 @@ class CDPMethods():
2071
2225
  if uc_lock:
2072
2226
  gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
2073
2227
  with gui_lock: # Prevent issues with multiple processes
2228
+ if "--debug" in sys.argv:
2229
+ print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x1, y1))
2074
2230
  pyautogui.moveTo(x1, y1, 0.25, pyautogui.easeOutQuad)
2075
2231
  self.__add_light_pause()
2076
2232
  if "--debug" in sys.argv:
2077
- print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x1, y1))
2233
+ print(" <DEBUG> pyautogui.dragTo(%s, %s)" % (x2, y2))
2078
2234
  pyautogui.dragTo(x2, y2, button="left", duration=timeframe)
2079
2235
  else:
2080
2236
  # Called from a method where the gui_lock is already active
2237
+ if "--debug" in sys.argv:
2238
+ print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x1, y1))
2081
2239
  pyautogui.moveTo(x1, y1, 0.25, pyautogui.easeOutQuad)
2082
2240
  self.__add_light_pause()
2083
2241
  if "--debug" in sys.argv:
@@ -2156,16 +2314,16 @@ class CDPMethods():
2156
2314
  if uc_lock:
2157
2315
  gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
2158
2316
  with gui_lock: # Prevent issues with multiple processes
2159
- pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
2160
- time.sleep(0.056)
2161
2317
  if "--debug" in sys.argv:
2162
2318
  print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x, y))
2319
+ pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
2320
+ time.sleep(0.056)
2163
2321
  else:
2164
2322
  # Called from a method where the gui_lock is already active
2165
- pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
2166
- time.sleep(0.056)
2167
2323
  if "--debug" in sys.argv:
2168
2324
  print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x, y))
2325
+ pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
2326
+ time.sleep(0.056)
2169
2327
 
2170
2328
  def gui_hover_x_y(self, x, y, timeframe=0.25):
2171
2329
  gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
@@ -2230,7 +2388,35 @@ class CDPMethods():
2230
2388
  self.__slow_mode_pause_if_set()
2231
2389
  self.loop.run_until_complete(self.page.wait())
2232
2390
 
2391
+ def hover_element(self, selector, timeframe=0.25):
2392
+ element = self.select(selector)
2393
+ gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
2394
+ with gui_lock:
2395
+ self.bring_active_window_to_front()
2396
+ self.sleep(0.02)
2397
+ element.mouse_move()
2398
+ self.sleep(timeframe)
2399
+
2400
+ def hover_and_click(self, hover_selector, click_selector):
2401
+ if getattr(sb_config, "_cdp_mobile_mode", None):
2402
+ self.select(click_selector).click()
2403
+ return
2404
+ hover_element = self.select(hover_selector)
2405
+ gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
2406
+ with gui_lock:
2407
+ self.bring_active_window_to_front()
2408
+ self.sleep(0.02)
2409
+ hover_element.mouse_move()
2410
+ self.sleep(0.25)
2411
+ try:
2412
+ self.click(click_selector, timeout=0.5)
2413
+ except Exception:
2414
+ self.select(click_selector, timeout=2).click()
2415
+
2233
2416
  def gui_hover_and_click(self, hover_selector, click_selector):
2417
+ if getattr(sb_config, "_cdp_mobile_mode", None):
2418
+ self.select(click_selector).click()
2419
+ return
2234
2420
  gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
2235
2421
  with gui_lock:
2236
2422
  self.__make_sure_pyautogui_lock_is_writable()
@@ -2870,4 +3056,15 @@ class Chrome(CDPMethods):
2870
3056
  driver = cdp_util.start_sync(**kwargs)
2871
3057
  loop = asyncio.new_event_loop()
2872
3058
  page = loop.run_until_complete(driver.get(url))
3059
+ wait_timeout = 30.0
3060
+ if hasattr(sb_config, "_cdp_proxy") and sb_config._cdp_proxy:
3061
+ wait_timeout = 45.0
3062
+ try:
3063
+ loop.run_until_complete(
3064
+ asyncio.wait_for(page.wait(), timeout=wait_timeout)
3065
+ )
3066
+ except asyncio.TimeoutError:
3067
+ pass
3068
+ except Exception:
3069
+ pass
2873
3070
  super().__init__(loop, page, driver)
@@ -12,6 +12,10 @@ from seleniumbase.fixtures import shared_utils
12
12
  class DriverMethods(WebDriver):
13
13
  def __init__(self, driver):
14
14
  self.driver = driver
15
+ if hasattr(driver, "session_id"):
16
+ self.session_id = driver.session_id
17
+ if hasattr(driver, "command_executor"):
18
+ self.command_executor = driver.command_executor
15
19
 
16
20
  def __is_cdp_swap_needed(self):
17
21
  """If the driver is disconnected, use a CDP method when available."""
@@ -37,6 +41,36 @@ class DriverMethods(WebDriver):
37
41
  value, by = page_utils.swap_selector_and_by_if_reversed(value, by)
38
42
  return self.driver.default_find_elements(by=by, value=value)
39
43
 
44
+ def add_cookie(self, *args, **kwargs):
45
+ page_actions._reconnect_if_disconnected(self.driver)
46
+ self.driver.default_add_cookie(*args, **kwargs)
47
+
48
+ def get_cookie(self, *args, **kwargs):
49
+ page_actions._reconnect_if_disconnected(self.driver)
50
+ self.driver.default_get_cookie(*args, **kwargs)
51
+
52
+ def delete_cookie(self, *args, **kwargs):
53
+ page_actions._reconnect_if_disconnected(self.driver)
54
+ self.driver.default_delete_cookie(*args, **kwargs)
55
+
56
+ def back(self):
57
+ if self.__is_cdp_swap_needed():
58
+ self.driver.cdp.go_back()
59
+ return
60
+ self.driver.default_back()
61
+
62
+ def forward(self):
63
+ if self.__is_cdp_swap_needed():
64
+ self.driver.cdp.go_forward()
65
+ return
66
+ self.driver.default_forward()
67
+
68
+ def refresh(self, *args, **kwargs):
69
+ if self.__is_cdp_swap_needed():
70
+ self.driver.cdp.refresh(*args, **kwargs)
71
+ return
72
+ self.driver.default_refresh()
73
+
40
74
  def locator(self, selector, by=None):
41
75
  if not by:
42
76
  by = "css selector"
@@ -288,11 +322,8 @@ class DriverMethods(WebDriver):
288
322
  selector = kwargs["selector"]
289
323
  else:
290
324
  selector = args[0]
291
- if ":contains(" not in selector:
292
- self.driver.cdp.highlight(selector)
293
- return
294
- else:
295
- self.driver.connect()
325
+ self.driver.cdp.highlight(selector)
326
+ return
296
327
  if "scroll" in kwargs:
297
328
  kwargs.pop("scroll")
298
329
  w_args = kwargs.copy()
@@ -3,10 +3,8 @@ from seleniumbase import config as sb_config
3
3
 
4
4
  def end_reused_class_session_as_needed():
5
5
  if (
6
- hasattr(sb_config, "reuse_class_session")
7
- and sb_config.reuse_class_session
8
- and hasattr(sb_config, "shared_driver")
9
- and sb_config.shared_driver
6
+ getattr(sb_config, "reuse_class_session", None)
7
+ and getattr(sb_config, "shared_driver", None)
10
8
  ):
11
9
  if (
12
10
  hasattr(sb_config.shared_driver, "service")
File without changes