seleniumbase 4.34.8__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 +328 -91
  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 +135 -11
  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 +37 -4
  26. seleniumbase/undetected/webelement.py +3 -2
  27. {seleniumbase-4.34.8.dist-info → seleniumbase-4.36.2.dist-info}/METADATA +30 -28
  28. {seleniumbase-4.34.8.dist-info → seleniumbase-4.36.2.dist-info}/RECORD +32 -32
  29. {seleniumbase-4.34.8.dist-info → seleniumbase-4.36.2.dist-info}/WHEEL +1 -1
  30. {seleniumbase-4.34.8.dist-info → seleniumbase-4.36.2.dist-info}/entry_points.txt +0 -0
  31. {seleniumbase-4.34.8.dist-info → seleniumbase-4.36.2.dist-info/licenses}/LICENSE +0 -0
  32. {seleniumbase-4.34.8.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,7 +953,21 @@ 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."""
@@ -934,10 +1028,61 @@ class CDPMethods():
934
1028
  self.set_window_rect(x, y, width, height)
935
1029
  self.__add_light_pause()
936
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
+
937
1084
  def get_window(self):
938
- return self.loop.run_until_complete(
939
- self.page.get_window()
940
- )
1085
+ return self.loop.run_until_complete(self.page.get_window())
941
1086
 
942
1087
  def get_text(self, selector):
943
1088
  return self.find_element(selector).text_all
@@ -984,6 +1129,16 @@ class CDPMethods():
984
1129
  self.page.evaluate("navigator.language || navigator.languages[0]")
985
1130
  )
986
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
+
987
1142
  def get_screen_rect(self):
988
1143
  coordinates = self.loop.run_until_complete(
989
1144
  self.page.js_dumps("window.screen")
@@ -1131,14 +1286,10 @@ class CDPMethods():
1131
1286
  return ((e_x + e_width / 2.0) + 0.5, (e_y + e_height / 2.0) + 0.5)
1132
1287
 
1133
1288
  def get_document(self):
1134
- return self.loop.run_until_complete(
1135
- self.page.get_document()
1136
- )
1289
+ return self.loop.run_until_complete(self.page.get_document())
1137
1290
 
1138
1291
  def get_flattened_document(self):
1139
- return self.loop.run_until_complete(
1140
- self.page.get_flattened_document()
1141
- )
1292
+ return self.loop.run_until_complete(self.page.get_flattened_document())
1142
1293
 
1143
1294
  def get_element_attributes(self, selector):
1144
1295
  selector = self.__convert_to_css_if_xpath(selector)
@@ -1175,6 +1326,16 @@ class CDPMethods():
1175
1326
  """(Settings will take effect on the next page load)"""
1176
1327
  self.loop.run_until_complete(self.page.set_locale(locale))
1177
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
+
1178
1339
  def set_attributes(self, selector, attribute, value):
1179
1340
  """This method uses JavaScript to set/update a common attribute.
1180
1341
  All matching selectors from querySelectorAll() are used.
@@ -1313,7 +1474,7 @@ class CDPMethods():
1313
1474
  pyautogui.press(key)
1314
1475
  time.sleep(0.044)
1315
1476
  self.__slow_mode_pause_if_set()
1316
- self.loop.run_until_complete(self.page.wait())
1477
+ self.loop.run_until_complete(self.page.sleep(0.025))
1317
1478
 
1318
1479
  def gui_press_keys(self, keys):
1319
1480
  self.__install_pyautogui_if_missing()
@@ -1328,7 +1489,7 @@ class CDPMethods():
1328
1489
  pyautogui.press(key)
1329
1490
  time.sleep(0.044)
1330
1491
  self.__slow_mode_pause_if_set()
1331
- self.loop.run_until_complete(self.page.wait())
1492
+ self.loop.run_until_complete(self.page.sleep(0.025))
1332
1493
 
1333
1494
  def gui_write(self, text):
1334
1495
  self.__install_pyautogui_if_missing()
@@ -1341,7 +1502,7 @@ class CDPMethods():
1341
1502
  self.__make_sure_pyautogui_lock_is_writable()
1342
1503
  pyautogui.write(text)
1343
1504
  self.__slow_mode_pause_if_set()
1344
- self.loop.run_until_complete(self.page.wait())
1505
+ self.loop.run_until_complete(self.page.sleep(0.025))
1345
1506
 
1346
1507
  def __gui_click_x_y(self, x, y, timeframe=0.25, uc_lock=False):
1347
1508
  self.__install_pyautogui_if_missing()
@@ -1671,59 +1832,93 @@ class CDPMethods():
1671
1832
  return True
1672
1833
  return False
1673
1834
 
1674
- def wait_for_element_visible(self, selector, timeout=None):
1675
- if not timeout:
1676
- 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
1677
1839
  try:
1678
- self.select(selector, timeout=timeout)
1840
+ element = self.find_element(selector, timeout=0.1)
1679
1841
  except Exception:
1680
- raise Exception("Element {%s} was not found!" % selector)
1681
- for i in range(30):
1682
- if self.is_element_visible(selector):
1683
- return self.select(selector)
1684
- time.sleep(0.1)
1685
- 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
1686
1847
 
1687
- def assert_element(self, selector, timeout=None):
1688
- """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):
1689
1862
  if not timeout:
1690
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
1691
1868
  try:
1692
- self.select(selector, timeout=timeout)
1869
+ element = self.find_element(selector, timeout=timeout)
1693
1870
  except Exception:
1694
- raise Exception("Element {%s} was not found!" % selector)
1695
- for i in range(30):
1696
- 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:
1697
1876
  return True
1877
+ now_ms = time.time() * 1000.0
1878
+ if now_ms >= stop_ms:
1879
+ break
1698
1880
  time.sleep(0.1)
1699
- 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
+ )
1700
1885
 
1701
- def assert_element_visible(self, selector, timeout=None):
1702
- """Same as assert_element()"""
1886
+ def wait_for_text_not_visible(self, text, selector="body", timeout=None):
1703
1887
  if not timeout:
1704
1888
  timeout = settings.SMALL_TIMEOUT
1705
- try:
1706
- self.select(selector, timeout=timeout)
1707
- except Exception:
1708
- raise Exception("Element {%s} was not found!" % selector)
1709
- for i in range(30):
1710
- 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):
1711
1894
  return True
1895
+ now_ms = time.time() * 1000.0
1896
+ if now_ms >= stop_ms:
1897
+ break
1712
1898
  time.sleep(0.1)
1713
- 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
+ )
1714
1906
 
1715
- def assert_element_present(self, selector, timeout=None):
1716
- """Assert element is present in the DOM. (Visibility NOT required)"""
1907
+ def wait_for_element_visible(self, selector, timeout=None):
1717
1908
  if not timeout:
1718
1909
  timeout = settings.SMALL_TIMEOUT
1719
1910
  try:
1720
1911
  self.select(selector, timeout=timeout)
1721
1912
  except Exception:
1722
1913
  raise Exception("Element {%s} was not found!" % selector)
1723
- 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)
1724
1919
 
1725
- def assert_element_absent(self, selector, timeout=None):
1726
- """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)"""
1727
1922
  if not timeout:
1728
1923
  timeout = settings.SMALL_TIMEOUT
1729
1924
  start_ms = time.time() * 1000.0
@@ -1731,6 +1926,8 @@ class CDPMethods():
1731
1926
  for i in range(int(timeout * 10)):
1732
1927
  if not self.is_element_present(selector):
1733
1928
  return True
1929
+ elif not self.is_element_visible(selector):
1930
+ return True
1734
1931
  now_ms = time.time() * 1000.0
1735
1932
  if now_ms >= stop_ms:
1736
1933
  break
@@ -1739,12 +1936,12 @@ class CDPMethods():
1739
1936
  if timeout == 1:
1740
1937
  plural = ""
1741
1938
  raise Exception(
1742
- "Element {%s} was still present after %s second%s!"
1939
+ "Element {%s} was still visible after %s second%s!"
1743
1940
  % (selector, timeout, plural)
1744
1941
  )
1745
1942
 
1746
- def assert_element_not_visible(self, selector, timeout=None):
1747
- """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."""
1748
1945
  if not timeout:
1749
1946
  timeout = settings.SMALL_TIMEOUT
1750
1947
  start_ms = time.time() * 1000.0
@@ -1752,8 +1949,6 @@ class CDPMethods():
1752
1949
  for i in range(int(timeout * 10)):
1753
1950
  if not self.is_element_present(selector):
1754
1951
  return True
1755
- elif not self.is_element_visible(selector):
1756
- return True
1757
1952
  now_ms = time.time() * 1000.0
1758
1953
  if now_ms >= stop_ms:
1759
1954
  break
@@ -1762,10 +1957,49 @@ class CDPMethods():
1762
1957
  if timeout == 1:
1763
1958
  plural = ""
1764
1959
  raise Exception(
1765
- "Element {%s} was still visible after %s second%s!"
1960
+ "Element {%s} was still present after %s second%s!"
1766
1961
  % (selector, timeout, plural)
1767
1962
  )
1768
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
+
1769
2003
  def assert_element_attribute(self, selector, attribute, value=None):
1770
2004
  attributes = self.get_element_attributes(selector)
1771
2005
  if attribute not in attributes:
@@ -1842,29 +2076,9 @@ class CDPMethods():
1842
2076
  raise Exception(error % (expected, actual))
1843
2077
 
1844
2078
  def assert_text(self, text, selector="body", timeout=None):
1845
- if not timeout:
1846
- timeout = settings.SMALL_TIMEOUT
1847
- start_ms = time.time() * 1000.0
1848
- stop_ms = start_ms + (timeout * 1000.0)
1849
- text = text.strip()
1850
- element = None
1851
- try:
1852
- element = self.find_element(selector, timeout=timeout)
1853
- except Exception:
1854
- raise Exception("Element {%s} not found!" % selector)
1855
- for i in range(int(timeout * 10)):
1856
- with suppress(Exception):
1857
- element = self.find_element(selector, timeout=0.1)
1858
- if text in element.text_all:
1859
- return True
1860
- now_ms = time.time() * 1000.0
1861
- if now_ms >= stop_ms:
1862
- break
1863
- time.sleep(0.1)
1864
- raise Exception(
1865
- "Text {%s} not found in {%s}! Actual text: {%s}"
1866
- % (text, selector, element.text_all)
1867
- )
2079
+ """Same as wait_for_text()"""
2080
+ self.wait_for_text(text, selector=selector, timeout=timeout)
2081
+ return True
1868
2082
 
1869
2083
  def assert_exact_text(self, text, selector="body", timeout=None):
1870
2084
  if not timeout:
@@ -1894,6 +2108,13 @@ class CDPMethods():
1894
2108
  % (text, element.text_all, selector)
1895
2109
  )
1896
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
+
1897
2118
  def assert_true(self, expression):
1898
2119
  if not expression:
1899
2120
  raise AssertionError("%s is not true" % expression)
@@ -1959,3 +2180,19 @@ class CDPMethods():
1959
2180
  )
1960
2181
  else:
1961
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)