seleniumbase 4.34.7__py3-none-any.whl → 4.36.2__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 (32) hide show
  1. seleniumbase/__init__.py +2 -0
  2. seleniumbase/__version__.py +1 -1
  3. seleniumbase/config/proxy_list.py +2 -2
  4. seleniumbase/console_scripts/sb_mkdir.py +3 -0
  5. seleniumbase/core/browser_launcher.py +160 -38
  6. seleniumbase/core/detect_b_ver.py +12 -13
  7. seleniumbase/core/proxy_helper.py +7 -5
  8. seleniumbase/core/sb_cdp.py +335 -93
  9. seleniumbase/core/sb_driver.py +7 -0
  10. seleniumbase/extensions/ad_block.zip +0 -0
  11. seleniumbase/extensions/disable_csp.zip +0 -0
  12. seleniumbase/fixtures/base_case.py +104 -19
  13. seleniumbase/fixtures/js_utils.py +33 -9
  14. seleniumbase/plugins/base_plugin.py +12 -15
  15. seleniumbase/plugins/driver_manager.py +19 -2
  16. seleniumbase/plugins/pytest_plugin.py +3 -0
  17. seleniumbase/plugins/sb_manager.py +18 -1
  18. seleniumbase/plugins/selenium_plugin.py +1 -0
  19. seleniumbase/undetected/__init__.py +38 -17
  20. seleniumbase/undetected/cdp_driver/__init__.py +2 -0
  21. seleniumbase/undetected/cdp_driver/browser.py +16 -10
  22. seleniumbase/undetected/cdp_driver/cdp_util.py +143 -8
  23. seleniumbase/undetected/cdp_driver/config.py +10 -0
  24. seleniumbase/undetected/cdp_driver/connection.py +1 -1
  25. seleniumbase/undetected/cdp_driver/tab.py +41 -4
  26. seleniumbase/undetected/webelement.py +3 -2
  27. {seleniumbase-4.34.7.dist-info → seleniumbase-4.36.2.dist-info}/METADATA +30 -28
  28. {seleniumbase-4.34.7.dist-info → seleniumbase-4.36.2.dist-info}/RECORD +32 -32
  29. {seleniumbase-4.34.7.dist-info → seleniumbase-4.36.2.dist-info}/WHEEL +1 -1
  30. {seleniumbase-4.34.7.dist-info → seleniumbase-4.36.2.dist-info}/entry_points.txt +0 -0
  31. {seleniumbase-4.34.7.dist-info → seleniumbase-4.36.2.dist-info/licenses}/LICENSE +0 -0
  32. {seleniumbase-4.34.7.dist-info → seleniumbase-4.36.2.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  """Add CDP methods to extend the driver"""
2
+ import asyncio
2
3
  import fasteners
3
4
  import os
4
5
  import re
@@ -11,6 +12,7 @@ from seleniumbase.fixtures import constants
11
12
  from seleniumbase.fixtures import js_utils
12
13
  from seleniumbase.fixtures import page_utils
13
14
  from seleniumbase.fixtures import shared_utils
15
+ from seleniumbase.undetected.cdp_driver import cdp_util
14
16
 
15
17
 
16
18
  class CDPMethods():
@@ -56,12 +58,16 @@ class CDPMethods():
56
58
  element, *args, **kwargs
57
59
  )
58
60
  element.focus = lambda: self.__focus(element)
61
+ element.gui_click = (
62
+ lambda *args, **kwargs: self.__gui_click(element, *args, **kwargs)
63
+ )
59
64
  element.highlight_overlay = lambda: self.__highlight_overlay(element)
60
65
  element.mouse_click = lambda: self.__mouse_click(element)
61
66
  element.mouse_drag = (
62
67
  lambda destination: self.__mouse_drag(element, destination)
63
68
  )
64
69
  element.mouse_move = lambda: self.__mouse_move(element)
70
+ element.press_keys = lambda text: self.__press_keys(element, text)
65
71
  element.query_selector = (
66
72
  lambda selector: self.__query_selector(element, selector)
67
73
  )
@@ -91,6 +97,8 @@ class CDPMethods():
91
97
  element.get_attribute = (
92
98
  lambda attribute: self.__get_attribute(element, attribute)
93
99
  )
100
+ # element.get_parent() should come last
101
+ element.get_parent = lambda: self.__get_parent(element)
94
102
  return element
95
103
 
96
104
  def get(self, url):
@@ -98,7 +106,7 @@ class CDPMethods():
98
106
  driver = self.driver
99
107
  if hasattr(driver, "cdp_base"):
100
108
  driver = driver.cdp_base
101
- self.page = self.loop.run_until_complete(driver.get(url))
109
+ self.loop.run_until_complete(self.page.get(url))
102
110
  url_protocol = url.split(":")[0]
103
111
  safe_url = True
104
112
  if url_protocol not in ["about", "data", "chrome"]:
@@ -202,14 +210,17 @@ class CDPMethods():
202
210
  element = self.__add_sync_methods(element)
203
211
  return self.__add_sync_methods(element)
204
212
  elif (
205
- element.parent
213
+ element
214
+ and element.parent
206
215
  and tag_name in element.parent.tag_name.lower()
207
216
  and text.strip() in element.parent.text
208
217
  ):
209
218
  element = self.__add_sync_methods(element.parent)
210
219
  return self.__add_sync_methods(element)
211
220
  elif (
212
- element.parent.parent
221
+ element
222
+ and element.parent
223
+ and element.parent.parent
213
224
  and tag_name in element.parent.parent.tag_name.lower()
214
225
  and text.strip() in element.parent.parent.text
215
226
  ):
@@ -262,7 +273,8 @@ class CDPMethods():
262
273
  if element not in updated_elements:
263
274
  updated_elements.append(element)
264
275
  elif (
265
- element.parent
276
+ element
277
+ and element.parent
266
278
  and tag_name in element.parent.tag_name.lower()
267
279
  and text.strip() in element.parent.text
268
280
  ):
@@ -270,7 +282,9 @@ class CDPMethods():
270
282
  if element not in updated_elements:
271
283
  updated_elements.append(element)
272
284
  elif (
273
- element.parent.parent
285
+ element
286
+ and element.parent
287
+ and element.parent.parent
274
288
  and tag_name in element.parent.parent.tag_name.lower()
275
289
  and text.strip() in element.parent.parent.text
276
290
  ):
@@ -280,7 +294,7 @@ class CDPMethods():
280
294
  return updated_elements
281
295
 
282
296
  def select(self, selector, timeout=None):
283
- """Similar to find_element(), but without text-based search."""
297
+ """Similar to find_element()."""
284
298
  if not timeout:
285
299
  timeout = settings.SMALL_TIMEOUT
286
300
  self.__add_light_pause()
@@ -289,12 +303,25 @@ class CDPMethods():
289
303
  tag_name = selector.split(":contains(")[0].split(" ")[-1]
290
304
  text = selector.split(":contains(")[1].split(")")[0][1:-1]
291
305
  with suppress(Exception):
306
+ new_timeout = timeout
307
+ if new_timeout < 1:
308
+ new_timeout = 1
309
+ self.loop.run_until_complete(
310
+ self.page.select(tag_name, timeout=new_timeout)
311
+ )
292
312
  self.loop.run_until_complete(
293
- self.page.select(tag_name, timeout=5)
313
+ self.page.find(text, timeout=new_timeout)
294
314
  )
295
- self.loop.run_until_complete(self.page.find(text, timeout=5))
296
- element = self.find_elements_by_text(text, tag_name=tag_name)[0]
297
- return self.__add_sync_methods(element)
315
+ elements = self.find_elements_by_text(text, tag_name=tag_name)
316
+ if not elements:
317
+ plural = "s"
318
+ if timeout == 1:
319
+ plural = ""
320
+ msg = "\n Element {%s} was not found after %s second%s!"
321
+ message = msg % (selector, timeout, plural)
322
+ raise Exception(message)
323
+ element = self.__add_sync_methods(elements[0])
324
+ return element
298
325
  failure = False
299
326
  try:
300
327
  element = self.loop.run_until_complete(
@@ -305,11 +332,8 @@ class CDPMethods():
305
332
  plural = "s"
306
333
  if timeout == 1:
307
334
  plural = ""
308
- message = "\n Element {%s} was not found after %s second%s!" % (
309
- selector,
310
- timeout,
311
- plural,
312
- )
335
+ msg = "\n Element {%s} was not found after %s second%s!"
336
+ message = msg % (selector, timeout, plural)
313
337
  if failure:
314
338
  raise Exception(message)
315
339
  element = self.__add_sync_methods(element)
@@ -421,6 +445,39 @@ class CDPMethods():
421
445
  self.loop.run_until_complete(element.focus_async())
422
446
  )
423
447
 
448
+ def __gui_click(self, element, timeframe=None):
449
+ element.scroll_into_view()
450
+ self.__add_light_pause()
451
+ position = element.get_position()
452
+ x = position.x
453
+ y = position.y
454
+ e_width = position.width
455
+ e_height = position.height
456
+ # Relative to window
457
+ element_rect = {"height": e_height, "width": e_width, "x": x, "y": y}
458
+ window_rect = self.get_window_rect()
459
+ w_bottom_y = window_rect["y"] + window_rect["height"]
460
+ viewport_height = window_rect["innerHeight"]
461
+ x = window_rect["x"] + element_rect["x"]
462
+ y = w_bottom_y - viewport_height + element_rect["y"]
463
+ y_scroll_offset = window_rect["pageYOffset"]
464
+ y = y - y_scroll_offset
465
+ x = x + window_rect["scrollX"]
466
+ y = y + window_rect["scrollY"]
467
+ # Relative to screen
468
+ element_rect = {"height": e_height, "width": e_width, "x": x, "y": y}
469
+ e_width = element_rect["width"]
470
+ e_height = element_rect["height"]
471
+ e_x = element_rect["x"]
472
+ e_y = element_rect["y"]
473
+ x, y = ((e_x + e_width / 2.0) + 0.5), ((e_y + e_height / 2.0) + 0.5)
474
+ if not timeframe or not isinstance(timeframe, (int, float)):
475
+ timeframe = 0.25
476
+ if timeframe > 3:
477
+ timeframe = 3
478
+ self.gui_click_x_y(x, y, timeframe=timeframe)
479
+ return self.loop.run_until_complete(self.page.wait())
480
+
424
481
  def __highlight_overlay(self, element):
425
482
  return (
426
483
  self.loop.run_until_complete(element.highlight_overlay_async())
@@ -443,6 +500,21 @@ class CDPMethods():
443
500
  self.loop.run_until_complete(element.mouse_move_async())
444
501
  )
445
502
 
503
+ def __press_keys(self, element, text):
504
+ element.scroll_into_view()
505
+ submit = False
506
+ if text.endswith("\n") or text.endswith("\r"):
507
+ submit = True
508
+ text = text[:-1]
509
+ for key in text:
510
+ element.send_keys(key)
511
+ time.sleep(0.044)
512
+ if submit:
513
+ element.send_keys("\r\n")
514
+ time.sleep(0.044)
515
+ self.__slow_mode_pause_if_set()
516
+ return self.loop.run_until_complete(self.page.sleep(0.025))
517
+
446
518
  def __query_selector(self, element, selector):
447
519
  selector = self.__convert_to_css_if_xpath(selector)
448
520
  element2 = self.loop.run_until_complete(
@@ -549,6 +621,9 @@ class CDPMethods():
549
621
  pass
550
622
  return None
551
623
 
624
+ def __get_parent(self, element):
625
+ return self.__add_sync_methods(element.parent)
626
+
552
627
  def __get_x_scroll_offset(self):
553
628
  x_scroll_offset = self.loop.run_until_complete(
554
629
  self.page.evaluate("window.pageXOffset")
@@ -769,6 +844,11 @@ class CDPMethods():
769
844
  def highlight_overlay(self, selector):
770
845
  self.find_element(selector).highlight_overlay()
771
846
 
847
+ def get_parent(self, element):
848
+ if isinstance(element, str):
849
+ element = self.select(element)
850
+ return self.__add_sync_methods(element.parent)
851
+
772
852
  def remove_element(self, selector):
773
853
  self.select(selector).remove_from_dom()
774
854
 
@@ -800,7 +880,7 @@ class CDPMethods():
800
880
  text = text[:-1] + "\r\n"
801
881
  element.send_keys(text)
802
882
  self.__slow_mode_pause_if_set()
803
- self.loop.run_until_complete(self.page.wait())
883
+ self.loop.run_until_complete(self.page.sleep(0.025))
804
884
 
805
885
  def press_keys(self, selector, text, timeout=None):
806
886
  """Similar to send_keys(), but presses keys at human speed."""
@@ -820,7 +900,7 @@ class CDPMethods():
820
900
  element.send_keys("\r\n")
821
901
  time.sleep(0.044)
822
902
  self.__slow_mode_pause_if_set()
823
- self.loop.run_until_complete(self.page.wait())
903
+ self.loop.run_until_complete(self.page.sleep(0.025))
824
904
 
825
905
  def type(self, selector, text, timeout=None):
826
906
  """Similar to send_keys(), but clears the text field first."""
@@ -835,7 +915,7 @@ class CDPMethods():
835
915
  text = text[:-1] + "\r\n"
836
916
  element.send_keys(text)
837
917
  self.__slow_mode_pause_if_set()
838
- self.loop.run_until_complete(self.page.wait())
918
+ self.loop.run_until_complete(self.page.sleep(0.025))
839
919
 
840
920
  def set_value(self, selector, text, timeout=None):
841
921
  """Similar to send_keys(), but clears the text field first."""
@@ -873,12 +953,31 @@ class CDPMethods():
873
953
  self.__add_light_pause()
874
954
  self.send_keys(selector, "\n")
875
955
  self.__slow_mode_pause_if_set()
876
- self.loop.run_until_complete(self.page.wait())
956
+ self.loop.run_until_complete(self.page.sleep(0.025))
957
+
958
+ def submit(self, selector):
959
+ submit_script = (
960
+ """elm = document.querySelector('%s');
961
+ const event = new KeyboardEvent("keydown", {
962
+ key: "Enter",
963
+ keyCode: 13,
964
+ code: "Enter",
965
+ which: 13,
966
+ bubbles: true
967
+ });
968
+ elm.dispatchEvent(event);""" % selector
969
+ )
970
+ self.loop.run_until_complete(self.page.evaluate(submit_script))
877
971
 
878
972
  def evaluate(self, expression):
879
973
  """Run a JavaScript expression and return the result."""
880
- if expression.startswith("return "):
881
- expression = expression[len("return "):]
974
+ expression = expression.strip()
975
+ exp_list = expression.split("\n")
976
+ if exp_list and exp_list[-1].strip().startswith("return "):
977
+ expression = (
978
+ "\n".join(exp_list[0:-1]) + "\n"
979
+ + exp_list[-1].strip()[len("return "):]
980
+ ).strip()
882
981
  return self.loop.run_until_complete(
883
982
  self.page.evaluate(expression)
884
983
  )
@@ -929,10 +1028,61 @@ class CDPMethods():
929
1028
  self.set_window_rect(x, y, width, height)
930
1029
  self.__add_light_pause()
931
1030
 
1031
+ def open_new_window(self, url=None, switch_to=True):
1032
+ return self.open_new_tab(url=url, switch_to=switch_to)
1033
+
1034
+ def switch_to_window(self, window):
1035
+ self.switch_to_tab(window)
1036
+
1037
+ def switch_to_newest_window(self):
1038
+ self.switch_to_tab(-1)
1039
+
1040
+ def open_new_tab(self, url=None, switch_to=True):
1041
+ if not isinstance(url, str):
1042
+ url = "about:blank"
1043
+ self.loop.run_until_complete(self.page.get(url, new_tab=True))
1044
+ if switch_to:
1045
+ self.switch_to_newest_tab()
1046
+
1047
+ def switch_to_tab(self, tab):
1048
+ driver = self.driver
1049
+ if hasattr(driver, "cdp_base"):
1050
+ driver = driver.cdp_base
1051
+ if isinstance(tab, int):
1052
+ self.page = driver.tabs[tab]
1053
+ elif isinstance(tab, cdp_util.Tab):
1054
+ self.page = tab
1055
+ else:
1056
+ raise Exception("`tab` must be an int or a Tab type!")
1057
+ self.bring_active_window_to_front()
1058
+
1059
+ def switch_to_newest_tab(self):
1060
+ self.switch_to_tab(-1)
1061
+
1062
+ def close_active_tab(self):
1063
+ """Close the active tab.
1064
+ The active tab is the one currenly controlled by CDP.
1065
+ The active tab MIGHT NOT be the currently visible tab!
1066
+ (If a page opens a new tab, the new tab WON'T be active)
1067
+ To switch the active tab, call: sb.switch_to_tab(tab)"""
1068
+ return self.loop.run_until_complete(self.page.close())
1069
+
1070
+ def get_active_tab(self):
1071
+ """Return the active tab.
1072
+ The active tab is the one currenly controlled by CDP.
1073
+ The active tab MIGHT NOT be the currently visible tab!
1074
+ (If a page opens a new tab, the new tab WON'T be active)
1075
+ To switch the active tab, call: sb.switch_to_tab(tab)"""
1076
+ return self.page
1077
+
1078
+ def get_tabs(self):
1079
+ driver = self.driver
1080
+ if hasattr(driver, "cdp_base"):
1081
+ driver = driver.cdp_base
1082
+ return driver.tabs
1083
+
932
1084
  def get_window(self):
933
- return self.loop.run_until_complete(
934
- self.page.get_window()
935
- )
1085
+ return self.loop.run_until_complete(self.page.get_window())
936
1086
 
937
1087
  def get_text(self, selector):
938
1088
  return self.find_element(selector).text_all
@@ -979,6 +1129,16 @@ class CDPMethods():
979
1129
  self.page.evaluate("navigator.language || navigator.languages[0]")
980
1130
  )
981
1131
 
1132
+ def get_local_storage_item(self, key):
1133
+ js_code = """localStorage.getItem('%s');""" % key
1134
+ with suppress(Exception):
1135
+ return self.loop.run_until_complete(self.page.evaluate(js_code))
1136
+
1137
+ def get_session_storage_item(self, key):
1138
+ js_code = """sessionStorage.getItem('%s');""" % key
1139
+ with suppress(Exception):
1140
+ return self.loop.run_until_complete(self.page.evaluate(js_code))
1141
+
982
1142
  def get_screen_rect(self):
983
1143
  coordinates = self.loop.run_until_complete(
984
1144
  self.page.js_dumps("window.screen")
@@ -1126,14 +1286,10 @@ class CDPMethods():
1126
1286
  return ((e_x + e_width / 2.0) + 0.5, (e_y + e_height / 2.0) + 0.5)
1127
1287
 
1128
1288
  def get_document(self):
1129
- return self.loop.run_until_complete(
1130
- self.page.get_document()
1131
- )
1289
+ return self.loop.run_until_complete(self.page.get_document())
1132
1290
 
1133
1291
  def get_flattened_document(self):
1134
- return self.loop.run_until_complete(
1135
- self.page.get_flattened_document()
1136
- )
1292
+ return self.loop.run_until_complete(self.page.get_flattened_document())
1137
1293
 
1138
1294
  def get_element_attributes(self, selector):
1139
1295
  selector = self.__convert_to_css_if_xpath(selector)
@@ -1170,6 +1326,16 @@ class CDPMethods():
1170
1326
  """(Settings will take effect on the next page load)"""
1171
1327
  self.loop.run_until_complete(self.page.set_locale(locale))
1172
1328
 
1329
+ def set_local_storage_item(self, key, value):
1330
+ js_code = """localStorage.setItem('%s','%s');""" % (key, value)
1331
+ with suppress(Exception):
1332
+ self.loop.run_until_complete(self.page.evaluate(js_code))
1333
+
1334
+ def set_session_storage_item(self, key, value):
1335
+ js_code = """sessionStorage.setItem('%s','%s');""" % (key, value)
1336
+ with suppress(Exception):
1337
+ self.loop.run_until_complete(self.page.evaluate(js_code))
1338
+
1173
1339
  def set_attributes(self, selector, attribute, value):
1174
1340
  """This method uses JavaScript to set/update a common attribute.
1175
1341
  All matching selectors from querySelectorAll() are used.
@@ -1308,7 +1474,7 @@ class CDPMethods():
1308
1474
  pyautogui.press(key)
1309
1475
  time.sleep(0.044)
1310
1476
  self.__slow_mode_pause_if_set()
1311
- self.loop.run_until_complete(self.page.wait())
1477
+ self.loop.run_until_complete(self.page.sleep(0.025))
1312
1478
 
1313
1479
  def gui_press_keys(self, keys):
1314
1480
  self.__install_pyautogui_if_missing()
@@ -1323,7 +1489,7 @@ class CDPMethods():
1323
1489
  pyautogui.press(key)
1324
1490
  time.sleep(0.044)
1325
1491
  self.__slow_mode_pause_if_set()
1326
- self.loop.run_until_complete(self.page.wait())
1492
+ self.loop.run_until_complete(self.page.sleep(0.025))
1327
1493
 
1328
1494
  def gui_write(self, text):
1329
1495
  self.__install_pyautogui_if_missing()
@@ -1336,7 +1502,7 @@ class CDPMethods():
1336
1502
  self.__make_sure_pyautogui_lock_is_writable()
1337
1503
  pyautogui.write(text)
1338
1504
  self.__slow_mode_pause_if_set()
1339
- self.loop.run_until_complete(self.page.wait())
1505
+ self.loop.run_until_complete(self.page.sleep(0.025))
1340
1506
 
1341
1507
  def __gui_click_x_y(self, x, y, timeframe=0.25, uc_lock=False):
1342
1508
  self.__install_pyautogui_if_missing()
@@ -1666,59 +1832,93 @@ class CDPMethods():
1666
1832
  return True
1667
1833
  return False
1668
1834
 
1669
- def wait_for_element_visible(self, selector, timeout=None):
1670
- if not timeout:
1671
- timeout = settings.SMALL_TIMEOUT
1835
+ def is_text_visible(self, text, selector="body"):
1836
+ selector = self.__convert_to_css_if_xpath(selector)
1837
+ text = text.strip()
1838
+ element = None
1672
1839
  try:
1673
- self.select(selector, timeout=timeout)
1840
+ element = self.find_element(selector, timeout=0.1)
1674
1841
  except Exception:
1675
- raise Exception("Element {%s} was not found!" % selector)
1676
- for i in range(30):
1677
- if self.is_element_visible(selector):
1678
- return self.select(selector)
1679
- time.sleep(0.1)
1680
- raise Exception("Element {%s} was not visible!" % selector)
1842
+ return False
1843
+ with suppress(Exception):
1844
+ if text in element.text_all:
1845
+ return True
1846
+ return False
1681
1847
 
1682
- def assert_element(self, selector, timeout=None):
1683
- """Same as assert_element_visible()"""
1848
+ def is_exact_text_visible(self, text, selector="body"):
1849
+ selector = self.__convert_to_css_if_xpath(selector)
1850
+ text = text.strip()
1851
+ element = None
1852
+ try:
1853
+ element = self.find_element(selector, timeout=0.1)
1854
+ except Exception:
1855
+ return False
1856
+ with suppress(Exception):
1857
+ if text == element.text_all.strip():
1858
+ return True
1859
+ return False
1860
+
1861
+ def wait_for_text(self, text, selector="body", timeout=None):
1684
1862
  if not timeout:
1685
1863
  timeout = settings.SMALL_TIMEOUT
1864
+ start_ms = time.time() * 1000.0
1865
+ stop_ms = start_ms + (timeout * 1000.0)
1866
+ text = text.strip()
1867
+ element = None
1686
1868
  try:
1687
- self.select(selector, timeout=timeout)
1869
+ element = self.find_element(selector, timeout=timeout)
1688
1870
  except Exception:
1689
- raise Exception("Element {%s} was not found!" % selector)
1690
- for i in range(30):
1691
- if self.is_element_visible(selector):
1871
+ raise Exception("Element {%s} not found!" % selector)
1872
+ for i in range(int(timeout * 10)):
1873
+ with suppress(Exception):
1874
+ element = self.find_element(selector, timeout=0.1)
1875
+ if text in element.text_all:
1692
1876
  return True
1877
+ now_ms = time.time() * 1000.0
1878
+ if now_ms >= stop_ms:
1879
+ break
1693
1880
  time.sleep(0.1)
1694
- raise Exception("Element {%s} was not visible!" % selector)
1881
+ raise Exception(
1882
+ "Text {%s} not found in {%s}! Actual text: {%s}"
1883
+ % (text, selector, element.text_all)
1884
+ )
1695
1885
 
1696
- def assert_element_visible(self, selector, timeout=None):
1697
- """Same as assert_element()"""
1886
+ def wait_for_text_not_visible(self, text, selector="body", timeout=None):
1698
1887
  if not timeout:
1699
1888
  timeout = settings.SMALL_TIMEOUT
1700
- try:
1701
- self.select(selector, timeout=timeout)
1702
- except Exception:
1703
- raise Exception("Element {%s} was not found!" % selector)
1704
- for i in range(30):
1705
- if self.is_element_visible(selector):
1889
+ text = text.strip()
1890
+ start_ms = time.time() * 1000.0
1891
+ stop_ms = start_ms + (timeout * 1000.0)
1892
+ for i in range(int(timeout * 10)):
1893
+ if not self.is_text_visible(text, selector):
1706
1894
  return True
1895
+ now_ms = time.time() * 1000.0
1896
+ if now_ms >= stop_ms:
1897
+ break
1707
1898
  time.sleep(0.1)
1708
- raise Exception("Element {%s} was not visible!" % selector)
1899
+ plural = "s"
1900
+ if timeout == 1:
1901
+ plural = ""
1902
+ raise Exception(
1903
+ "Text {%s} in {%s} was still visible after %s second%s!"
1904
+ % (text, selector, timeout, plural)
1905
+ )
1709
1906
 
1710
- def assert_element_present(self, selector, timeout=None):
1711
- """Assert element is present in the DOM. (Visibility NOT required)"""
1907
+ def wait_for_element_visible(self, selector, timeout=None):
1712
1908
  if not timeout:
1713
1909
  timeout = settings.SMALL_TIMEOUT
1714
1910
  try:
1715
1911
  self.select(selector, timeout=timeout)
1716
1912
  except Exception:
1717
1913
  raise Exception("Element {%s} was not found!" % selector)
1718
- return True
1914
+ for i in range(30):
1915
+ if self.is_element_visible(selector):
1916
+ return self.select(selector)
1917
+ time.sleep(0.1)
1918
+ raise Exception("Element {%s} was not visible!" % selector)
1719
1919
 
1720
- def assert_element_absent(self, selector, timeout=None):
1721
- """Assert element is not present in the DOM."""
1920
+ def wait_for_element_not_visible(self, selector, timeout=None):
1921
+ """Wait for element to not be visible on page. (May still be in DOM)"""
1722
1922
  if not timeout:
1723
1923
  timeout = settings.SMALL_TIMEOUT
1724
1924
  start_ms = time.time() * 1000.0
@@ -1726,6 +1926,8 @@ class CDPMethods():
1726
1926
  for i in range(int(timeout * 10)):
1727
1927
  if not self.is_element_present(selector):
1728
1928
  return True
1929
+ elif not self.is_element_visible(selector):
1930
+ return True
1729
1931
  now_ms = time.time() * 1000.0
1730
1932
  if now_ms >= stop_ms:
1731
1933
  break
@@ -1734,12 +1936,12 @@ class CDPMethods():
1734
1936
  if timeout == 1:
1735
1937
  plural = ""
1736
1938
  raise Exception(
1737
- "Element {%s} was still present after %s second%s!"
1939
+ "Element {%s} was still visible after %s second%s!"
1738
1940
  % (selector, timeout, plural)
1739
1941
  )
1740
1942
 
1741
- def assert_element_not_visible(self, selector, timeout=None):
1742
- """Assert element is not visible on page. (May still be in DOM)"""
1943
+ def wait_for_element_absent(self, selector, timeout=None):
1944
+ """Wait for element to not be present in the DOM."""
1743
1945
  if not timeout:
1744
1946
  timeout = settings.SMALL_TIMEOUT
1745
1947
  start_ms = time.time() * 1000.0
@@ -1747,8 +1949,6 @@ class CDPMethods():
1747
1949
  for i in range(int(timeout * 10)):
1748
1950
  if not self.is_element_present(selector):
1749
1951
  return True
1750
- elif not self.is_element_visible(selector):
1751
- return True
1752
1952
  now_ms = time.time() * 1000.0
1753
1953
  if now_ms >= stop_ms:
1754
1954
  break
@@ -1757,10 +1957,49 @@ class CDPMethods():
1757
1957
  if timeout == 1:
1758
1958
  plural = ""
1759
1959
  raise Exception(
1760
- "Element {%s} was still visible after %s second%s!"
1960
+ "Element {%s} was still present after %s second%s!"
1761
1961
  % (selector, timeout, plural)
1762
1962
  )
1763
1963
 
1964
+ def assert_element(self, selector, timeout=None):
1965
+ """Same as assert_element_visible()"""
1966
+ self.assert_element_visible(selector, timeout=timeout)
1967
+ return True
1968
+
1969
+ def assert_element_visible(self, selector, timeout=None):
1970
+ """Same as assert_element()"""
1971
+ if not timeout:
1972
+ timeout = settings.SMALL_TIMEOUT
1973
+ try:
1974
+ self.select(selector, timeout=timeout)
1975
+ except Exception:
1976
+ raise Exception("Element {%s} was not found!" % selector)
1977
+ for i in range(30):
1978
+ if self.is_element_visible(selector):
1979
+ return True
1980
+ time.sleep(0.1)
1981
+ raise Exception("Element {%s} was not visible!" % selector)
1982
+
1983
+ def assert_element_present(self, selector, timeout=None):
1984
+ """Assert element is present in the DOM. (Visibility NOT required)"""
1985
+ if not timeout:
1986
+ timeout = settings.SMALL_TIMEOUT
1987
+ try:
1988
+ self.select(selector, timeout=timeout)
1989
+ except Exception:
1990
+ raise Exception("Element {%s} was not found!" % selector)
1991
+ return True
1992
+
1993
+ def assert_element_absent(self, selector, timeout=None):
1994
+ """Assert element is not present in the DOM."""
1995
+ self.wait_for_element_absent(selector, timeout=timeout)
1996
+ return True
1997
+
1998
+ def assert_element_not_visible(self, selector, timeout=None):
1999
+ """Assert element is not visible on page. (May still be in DOM)"""
2000
+ self.wait_for_element_not_visible(selector, timeout=timeout)
2001
+ return True
2002
+
1764
2003
  def assert_element_attribute(self, selector, attribute, value=None):
1765
2004
  attributes = self.get_element_attributes(selector)
1766
2005
  if attribute not in attributes:
@@ -1837,29 +2076,9 @@ class CDPMethods():
1837
2076
  raise Exception(error % (expected, actual))
1838
2077
 
1839
2078
  def assert_text(self, text, selector="body", timeout=None):
1840
- if not timeout:
1841
- timeout = settings.SMALL_TIMEOUT
1842
- start_ms = time.time() * 1000.0
1843
- stop_ms = start_ms + (timeout * 1000.0)
1844
- text = text.strip()
1845
- element = None
1846
- try:
1847
- element = self.find_element(selector, timeout=timeout)
1848
- except Exception:
1849
- raise Exception("Element {%s} not found!" % selector)
1850
- for i in range(int(timeout * 10)):
1851
- with suppress(Exception):
1852
- element = self.find_element(selector, timeout=0.1)
1853
- if text in element.text_all:
1854
- return True
1855
- now_ms = time.time() * 1000.0
1856
- if now_ms >= stop_ms:
1857
- break
1858
- time.sleep(0.1)
1859
- raise Exception(
1860
- "Text {%s} not found in {%s}! Actual text: {%s}"
1861
- % (text, selector, element.text_all)
1862
- )
2079
+ """Same as wait_for_text()"""
2080
+ self.wait_for_text(text, selector=selector, timeout=timeout)
2081
+ return True
1863
2082
 
1864
2083
  def assert_exact_text(self, text, selector="body", timeout=None):
1865
2084
  if not timeout:
@@ -1889,6 +2108,13 @@ class CDPMethods():
1889
2108
  % (text, element.text_all, selector)
1890
2109
  )
1891
2110
 
2111
+ def assert_text_not_visible(self, text, selector="body", timeout=None):
2112
+ """Raises an exception if the text is still visible after timeout."""
2113
+ self.wait_for_text_not_visible(
2114
+ text, selector=selector, timeout=timeout
2115
+ )
2116
+ return True
2117
+
1892
2118
  def assert_true(self, expression):
1893
2119
  if not expression:
1894
2120
  raise AssertionError("%s is not true" % expression)
@@ -1954,3 +2180,19 @@ class CDPMethods():
1954
2180
  )
1955
2181
  else:
1956
2182
  self.select(selector).save_screenshot(filename)
2183
+
2184
+ def print_to_pdf(self, name, folder=None):
2185
+ filename = name
2186
+ if folder:
2187
+ filename = os.path.join(folder, name)
2188
+ self.loop.run_until_complete(self.page.print_to_pdf(filename))
2189
+
2190
+
2191
+ class Chrome(CDPMethods):
2192
+ def __init__(self, url=None, **kwargs):
2193
+ if not url:
2194
+ url = "about:blank"
2195
+ loop = asyncio.new_event_loop()
2196
+ driver = cdp_util.start_sync(**kwargs)
2197
+ page = loop.run_until_complete(driver.get(url))
2198
+ super().__init__(loop, page, driver)