seleniumbase 4.32.9__py3-none-any.whl → 4.32.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.
- seleniumbase/__version__.py +1 -1
- seleniumbase/core/browser_launcher.py +85 -40
- seleniumbase/core/sb_cdp.py +399 -53
- seleniumbase/core/sb_driver.py +16 -0
- seleniumbase/fixtures/base_case.py +59 -11
- seleniumbase/fixtures/constants.py +1 -0
- seleniumbase/fixtures/js_utils.py +1 -5
- seleniumbase/fixtures/page_actions.py +0 -2
- seleniumbase/fixtures/shared_utils.py +14 -3
- seleniumbase/undetected/cdp_driver/element.py +18 -2
- {seleniumbase-4.32.9.dist-info → seleniumbase-4.32.10.dist-info}/METADATA +5 -5
- {seleniumbase-4.32.9.dist-info → seleniumbase-4.32.10.dist-info}/RECORD +16 -16
- {seleniumbase-4.32.9.dist-info → seleniumbase-4.32.10.dist-info}/WHEEL +1 -1
- {seleniumbase-4.32.9.dist-info → seleniumbase-4.32.10.dist-info}/LICENSE +0 -0
- {seleniumbase-4.32.9.dist-info → seleniumbase-4.32.10.dist-info}/entry_points.txt +0 -0
- {seleniumbase-4.32.9.dist-info → seleniumbase-4.32.10.dist-info}/top_level.txt +0 -0
seleniumbase/core/sb_cdp.py
CHANGED
@@ -39,7 +39,9 @@ class CDPMethods():
|
|
39
39
|
return element
|
40
40
|
element.clear_input = lambda: self.__clear_input(element)
|
41
41
|
element.click = lambda: self.__click(element)
|
42
|
-
element.flash = lambda: self.__flash(
|
42
|
+
element.flash = lambda *args, **kwargs: self.__flash(
|
43
|
+
element, *args, **kwargs
|
44
|
+
)
|
43
45
|
element.focus = lambda: self.__focus(element)
|
44
46
|
element.highlight_overlay = lambda: self.__highlight_overlay(element)
|
45
47
|
element.mouse_click = lambda: self.__mouse_click(element)
|
@@ -87,6 +89,10 @@ class CDPMethods():
|
|
87
89
|
safe_url = False
|
88
90
|
if not safe_url:
|
89
91
|
time.sleep(constants.UC.CDP_MODE_OPEN_WAIT)
|
92
|
+
if shared_utils.is_windows():
|
93
|
+
time.sleep(constants.UC.EXTRA_WINDOWS_WAIT)
|
94
|
+
else:
|
95
|
+
time.sleep(0.012)
|
90
96
|
self.__slow_mode_pause_if_set()
|
91
97
|
self.loop.run_until_complete(self.page.wait())
|
92
98
|
|
@@ -186,7 +192,10 @@ class CDPMethods():
|
|
186
192
|
)
|
187
193
|
updated_elements = []
|
188
194
|
for element in elements:
|
189
|
-
if
|
195
|
+
if (
|
196
|
+
not tag_name
|
197
|
+
or tag_name.lower().strip() in element.tag_name.lower().strip()
|
198
|
+
):
|
190
199
|
element = self.__add_sync_methods(element)
|
191
200
|
updated_elements.append(element)
|
192
201
|
self.__slow_mode_pause_if_set()
|
@@ -258,9 +267,11 @@ class CDPMethods():
|
|
258
267
|
self.loop.run_until_complete(self.page.wait())
|
259
268
|
return result
|
260
269
|
|
261
|
-
def __flash(self, element):
|
270
|
+
def __flash(self, element, *args, **kwargs):
|
262
271
|
return (
|
263
|
-
self.loop.run_until_complete(
|
272
|
+
self.loop.run_until_complete(
|
273
|
+
element.flash_async(*args, **kwargs)
|
274
|
+
)
|
264
275
|
)
|
265
276
|
|
266
277
|
def __focus(self, element):
|
@@ -292,11 +303,11 @@ class CDPMethods():
|
|
292
303
|
|
293
304
|
def __query_selector(self, element, selector):
|
294
305
|
selector = self.__convert_to_css_if_xpath(selector)
|
295
|
-
|
306
|
+
element2 = self.loop.run_until_complete(
|
296
307
|
element.query_selector_async(selector)
|
297
308
|
)
|
298
|
-
|
299
|
-
return
|
309
|
+
element2 = self.__add_sync_methods(element2)
|
310
|
+
return element2
|
300
311
|
|
301
312
|
def __query_selector_all(self, element, selector):
|
302
313
|
selector = self.__convert_to_css_if_xpath(selector)
|
@@ -431,6 +442,7 @@ class CDPMethods():
|
|
431
442
|
|
432
443
|
def bring_active_window_to_front(self):
|
433
444
|
self.loop.run_until_complete(self.page.bring_to_front())
|
445
|
+
self.__add_light_pause()
|
434
446
|
|
435
447
|
def get_active_element(self):
|
436
448
|
return self.loop.run_until_complete(
|
@@ -467,6 +479,19 @@ class CDPMethods():
|
|
467
479
|
self.__slow_mode_pause_if_set()
|
468
480
|
self.loop.run_until_complete(self.page.wait())
|
469
481
|
|
482
|
+
def click_visible_elements(self, selector):
|
483
|
+
elements = self.select_all(selector)
|
484
|
+
for element in elements:
|
485
|
+
try:
|
486
|
+
position = element.get_position()
|
487
|
+
if (position.width != 0 or position.height != 0):
|
488
|
+
element.click()
|
489
|
+
time.sleep(0.0375)
|
490
|
+
self.__slow_mode_pause_if_set()
|
491
|
+
self.loop.run_until_complete(self.page.wait())
|
492
|
+
except Exception:
|
493
|
+
pass
|
494
|
+
|
470
495
|
def mouse_click(self, selector, timeout=settings.SMALL_TIMEOUT):
|
471
496
|
"""(Attempt simulating a mouse click)"""
|
472
497
|
self.__slow_mode_pause_if_set()
|
@@ -491,9 +516,44 @@ class CDPMethods():
|
|
491
516
|
element = self.find_element(parent_selector)
|
492
517
|
return element.query_selector(selector)
|
493
518
|
|
494
|
-
def
|
519
|
+
def select_option_by_text(self, dropdown_selector, option):
|
520
|
+
element = self.find_element(dropdown_selector)
|
521
|
+
options = element.query_selector_all("option")
|
522
|
+
for found_option in options:
|
523
|
+
if found_option.text.strip() == option.strip():
|
524
|
+
found_option.select_option()
|
525
|
+
return
|
526
|
+
raise Exception(
|
527
|
+
"Unable to find text option {%s} in dropdown {%s}!"
|
528
|
+
% (dropdown_selector, option)
|
529
|
+
)
|
530
|
+
|
531
|
+
def flash(
|
532
|
+
self,
|
533
|
+
selector, # The CSS Selector to flash
|
534
|
+
duration=1, # (seconds) flash duration
|
535
|
+
color="44CC88", # RGB hex flash color
|
536
|
+
pause=0, # (seconds) If 0, the next action starts during flash
|
537
|
+
):
|
495
538
|
"""Paint a quickly-vanishing dot over an element."""
|
496
|
-
self.
|
539
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
540
|
+
element = self.find_element(selector)
|
541
|
+
element.flash(duration=duration, color=color)
|
542
|
+
if pause and isinstance(pause, (int, float)):
|
543
|
+
time.sleep(pause)
|
544
|
+
|
545
|
+
def highlight(self, selector):
|
546
|
+
"""Highlight an element with multi-colors."""
|
547
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
548
|
+
element = self.find_element(selector)
|
549
|
+
element.flash(0.46, "44CC88")
|
550
|
+
time.sleep(0.15)
|
551
|
+
element.flash(0.42, "8844CC")
|
552
|
+
time.sleep(0.15)
|
553
|
+
element.flash(0.38, "CC8844")
|
554
|
+
time.sleep(0.15)
|
555
|
+
element.flash(0.30, "44CC88")
|
556
|
+
time.sleep(0.30)
|
497
557
|
|
498
558
|
def focus(self, selector):
|
499
559
|
self.find_element(selector).focus()
|
@@ -522,12 +582,10 @@ class CDPMethods():
|
|
522
582
|
with suppress(Exception):
|
523
583
|
self.loop.run_until_complete(self.page.evaluate(js_code))
|
524
584
|
|
525
|
-
def scroll_into_view(self, selector):
|
526
|
-
self.find_element(selector).scroll_into_view()
|
527
|
-
|
528
585
|
def send_keys(self, selector, text, timeout=settings.SMALL_TIMEOUT):
|
529
|
-
element = self.select(selector)
|
530
586
|
self.__slow_mode_pause_if_set()
|
587
|
+
element = self.select(selector, timeout=timeout)
|
588
|
+
self.__add_light_pause()
|
531
589
|
if text.endswith("\n") or text.endswith("\r"):
|
532
590
|
text = text[:-1] + "\r\n"
|
533
591
|
element.send_keys(text)
|
@@ -536,8 +594,9 @@ class CDPMethods():
|
|
536
594
|
|
537
595
|
def press_keys(self, selector, text, timeout=settings.SMALL_TIMEOUT):
|
538
596
|
"""Similar to send_keys(), but presses keys at human speed."""
|
539
|
-
element = self.select(selector)
|
540
597
|
self.__slow_mode_pause_if_set()
|
598
|
+
element = self.select(selector, timeout=timeout)
|
599
|
+
self.__add_light_pause()
|
541
600
|
submit = False
|
542
601
|
if text.endswith("\n") or text.endswith("\r"):
|
543
602
|
submit = True
|
@@ -553,8 +612,9 @@ class CDPMethods():
|
|
553
612
|
|
554
613
|
def type(self, selector, text, timeout=settings.SMALL_TIMEOUT):
|
555
614
|
"""Similar to send_keys(), but clears the text field first."""
|
556
|
-
element = self.select(selector)
|
557
615
|
self.__slow_mode_pause_if_set()
|
616
|
+
element = self.select(selector, timeout=timeout)
|
617
|
+
self.__add_light_pause()
|
558
618
|
with suppress(Exception):
|
559
619
|
element.clear_input()
|
560
620
|
if text.endswith("\n") or text.endswith("\r"):
|
@@ -563,6 +623,42 @@ class CDPMethods():
|
|
563
623
|
self.__slow_mode_pause_if_set()
|
564
624
|
self.loop.run_until_complete(self.page.wait())
|
565
625
|
|
626
|
+
def set_value(self, selector, text, timeout=settings.SMALL_TIMEOUT):
|
627
|
+
"""Similar to send_keys(), but clears the text field first."""
|
628
|
+
self.__slow_mode_pause_if_set()
|
629
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
630
|
+
self.select(selector, timeout=timeout)
|
631
|
+
self.__add_light_pause()
|
632
|
+
press_enter = False
|
633
|
+
if text.endswith("\n"):
|
634
|
+
text = text[:-1]
|
635
|
+
press_enter = True
|
636
|
+
value = js_utils.escape_quotes_if_needed(re.escape(text))
|
637
|
+
css_selector = re.escape(selector)
|
638
|
+
css_selector = js_utils.escape_quotes_if_needed(css_selector)
|
639
|
+
set_value_script = (
|
640
|
+
"""m_elm = document.querySelector('%s');"""
|
641
|
+
"""m_elm.value = '%s';""" % (css_selector, value)
|
642
|
+
)
|
643
|
+
self.loop.run_until_complete(self.page.evaluate(set_value_script))
|
644
|
+
the_type = self.get_element_attribute(selector, "type")
|
645
|
+
if the_type == "range":
|
646
|
+
# Some input sliders need a mouse event to trigger listeners.
|
647
|
+
with suppress(Exception):
|
648
|
+
mouse_move_script = (
|
649
|
+
"""m_elm = document.querySelector('%s');"""
|
650
|
+
"""m_evt = new Event('mousemove');"""
|
651
|
+
"""m_elm.dispatchEvent(m_evt);""" % css_selector
|
652
|
+
)
|
653
|
+
self.loop.run_until_complete(
|
654
|
+
self.page.evaluate(mouse_move_script)
|
655
|
+
)
|
656
|
+
elif press_enter:
|
657
|
+
self.__add_light_pause()
|
658
|
+
self.send_keys(selector, "\n")
|
659
|
+
self.__slow_mode_pause_if_set()
|
660
|
+
self.loop.run_until_complete(self.page.wait())
|
661
|
+
|
566
662
|
def evaluate(self, expression):
|
567
663
|
"""Run a JavaScript expression and return the result."""
|
568
664
|
return self.loop.run_until_complete(
|
@@ -576,21 +672,30 @@ class CDPMethods():
|
|
576
672
|
)
|
577
673
|
|
578
674
|
def maximize(self):
|
579
|
-
|
580
|
-
|
581
|
-
)
|
675
|
+
if self.get_window()[1].window_state.value == "maximized":
|
676
|
+
return
|
677
|
+
elif self.get_window()[1].window_state.value == "minimized":
|
678
|
+
self.loop.run_until_complete(self.page.maximize())
|
679
|
+
time.sleep(0.0375)
|
680
|
+
return self.loop.run_until_complete(self.page.maximize())
|
582
681
|
|
583
682
|
def minimize(self):
|
584
|
-
|
585
|
-
self.page.minimize()
|
586
|
-
)
|
683
|
+
if self.get_window()[1].window_state.value != "minimized":
|
684
|
+
return self.loop.run_until_complete(self.page.minimize())
|
587
685
|
|
588
686
|
def medimize(self):
|
589
|
-
|
590
|
-
self.page.medimize()
|
591
|
-
|
687
|
+
if self.get_window()[1].window_state.value == "minimized":
|
688
|
+
self.loop.run_until_complete(self.page.medimize())
|
689
|
+
time.sleep(0.0375)
|
690
|
+
return self.loop.run_until_complete(self.page.medimize())
|
592
691
|
|
593
692
|
def set_window_rect(self, x, y, width, height):
|
693
|
+
if self.get_window()[1].window_state.value == "minimized":
|
694
|
+
self.loop.run_until_complete(
|
695
|
+
self.page.set_window_size(
|
696
|
+
left=x, top=y, width=width, height=height)
|
697
|
+
)
|
698
|
+
time.sleep(0.0375)
|
594
699
|
return self.loop.run_until_complete(
|
595
700
|
self.page.set_window_size(
|
596
701
|
left=x, top=y, width=width, height=height)
|
@@ -602,6 +707,7 @@ class CDPMethods():
|
|
602
707
|
width = settings.CHROME_START_WIDTH
|
603
708
|
height = settings.CHROME_START_HEIGHT
|
604
709
|
self.set_window_rect(x, y, width, height)
|
710
|
+
self.__add_light_pause()
|
605
711
|
|
606
712
|
def get_window(self):
|
607
713
|
return self.loop.run_until_complete(
|
@@ -737,8 +843,10 @@ class CDPMethods():
|
|
737
843
|
coordinates["y"] = y if y else 0
|
738
844
|
return coordinates
|
739
845
|
|
740
|
-
def get_element_rect(self, selector):
|
846
|
+
def get_element_rect(self, selector, timeout=settings.SMALL_TIMEOUT):
|
741
847
|
selector = self.__convert_to_css_if_xpath(selector)
|
848
|
+
self.select(selector, timeout=timeout)
|
849
|
+
self.__add_light_pause()
|
742
850
|
coordinates = self.loop.run_until_complete(
|
743
851
|
self.page.js_dumps(
|
744
852
|
"""document.querySelector"""
|
@@ -785,7 +893,7 @@ class CDPMethods():
|
|
785
893
|
e_height = element_rect["height"]
|
786
894
|
e_x = element_rect["x"]
|
787
895
|
e_y = element_rect["y"]
|
788
|
-
return ((e_x + e_width / 2), (e_y + e_height / 2))
|
896
|
+
return ((e_x + e_width / 2.0) + 0.5, (e_y + e_height / 2.0) + 0.5)
|
789
897
|
|
790
898
|
def get_document(self):
|
791
899
|
return self.loop.run_until_complete(
|
@@ -798,6 +906,7 @@ class CDPMethods():
|
|
798
906
|
)
|
799
907
|
|
800
908
|
def get_element_attributes(self, selector):
|
909
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
801
910
|
return self.loop.run_until_complete(
|
802
911
|
self.page.js_dumps(
|
803
912
|
"""document.querySelector('%s')"""
|
@@ -1013,6 +1122,156 @@ class CDPMethods():
|
|
1013
1122
|
pyautogui.click(x=x, y=y)
|
1014
1123
|
|
1015
1124
|
def gui_click_x_y(self, x, y, timeframe=0.25):
|
1125
|
+
gui_lock = fasteners.InterProcessLock(
|
1126
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1127
|
+
)
|
1128
|
+
with gui_lock: # Prevent issues with multiple processes
|
1129
|
+
self.__install_pyautogui_if_missing()
|
1130
|
+
import pyautogui
|
1131
|
+
pyautogui = self.__get_configured_pyautogui(pyautogui)
|
1132
|
+
width_ratio = 1.0
|
1133
|
+
if shared_utils.is_windows():
|
1134
|
+
window_rect = self.get_window_rect()
|
1135
|
+
width = window_rect["width"]
|
1136
|
+
height = window_rect["height"]
|
1137
|
+
win_x = window_rect["x"]
|
1138
|
+
win_y = window_rect["y"]
|
1139
|
+
scr_width = pyautogui.size().width
|
1140
|
+
self.maximize()
|
1141
|
+
self.__add_light_pause()
|
1142
|
+
win_width = self.get_window_size()["width"]
|
1143
|
+
width_ratio = round(float(scr_width) / float(win_width), 2)
|
1144
|
+
width_ratio += 0.01
|
1145
|
+
if width_ratio < 0.45 or width_ratio > 2.55:
|
1146
|
+
width_ratio = 1.01
|
1147
|
+
sb_config._saved_width_ratio = width_ratio
|
1148
|
+
self.minimize()
|
1149
|
+
self.__add_light_pause()
|
1150
|
+
self.set_window_rect(win_x, win_y, width, height)
|
1151
|
+
self.__add_light_pause()
|
1152
|
+
x = x * width_ratio
|
1153
|
+
y = y * width_ratio
|
1154
|
+
self.bring_active_window_to_front()
|
1155
|
+
self.__gui_click_x_y(x, y, timeframe=timeframe, uc_lock=False)
|
1156
|
+
|
1157
|
+
def gui_click_element(self, selector, timeframe=0.25):
|
1158
|
+
self.__slow_mode_pause_if_set()
|
1159
|
+
x, y = self.get_gui_element_center(selector)
|
1160
|
+
self.__add_light_pause()
|
1161
|
+
self.gui_click_x_y(x, y, timeframe=timeframe)
|
1162
|
+
self.__slow_mode_pause_if_set()
|
1163
|
+
self.loop.run_until_complete(self.page.wait())
|
1164
|
+
|
1165
|
+
def __gui_drag_drop(self, x1, y1, x2, y2, timeframe=0.25, uc_lock=False):
|
1166
|
+
self.__install_pyautogui_if_missing()
|
1167
|
+
import pyautogui
|
1168
|
+
pyautogui = self.__get_configured_pyautogui(pyautogui)
|
1169
|
+
screen_width, screen_height = pyautogui.size()
|
1170
|
+
if x1 < 0 or y1 < 0 or x1 > screen_width or y1 > screen_height:
|
1171
|
+
raise Exception(
|
1172
|
+
"PyAutoGUI cannot drag-drop from point (%s, %s)"
|
1173
|
+
" outside screen. (Width: %s, Height: %s)"
|
1174
|
+
% (x1, y1, screen_width, screen_height)
|
1175
|
+
)
|
1176
|
+
if x2 < 0 or y2 < 0 or x2 > screen_width or y2 > screen_height:
|
1177
|
+
raise Exception(
|
1178
|
+
"PyAutoGUI cannot drag-drop to point (%s, %s)"
|
1179
|
+
" outside screen. (Width: %s, Height: %s)"
|
1180
|
+
% (x2, y2, screen_width, screen_height)
|
1181
|
+
)
|
1182
|
+
if uc_lock:
|
1183
|
+
gui_lock = fasteners.InterProcessLock(
|
1184
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1185
|
+
)
|
1186
|
+
with gui_lock: # Prevent issues with multiple processes
|
1187
|
+
pyautogui.moveTo(x1, y1, 0.25, pyautogui.easeOutQuad)
|
1188
|
+
self.__add_light_pause()
|
1189
|
+
if "--debug" in sys.argv:
|
1190
|
+
print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x1, y1))
|
1191
|
+
pyautogui.dragTo(x2, y2, button="left", duration=timeframe)
|
1192
|
+
else:
|
1193
|
+
# Called from a method where the gui_lock is already active
|
1194
|
+
pyautogui.moveTo(x1, y1, 0.25, pyautogui.easeOutQuad)
|
1195
|
+
self.__add_light_pause()
|
1196
|
+
if "--debug" in sys.argv:
|
1197
|
+
print(" <DEBUG> pyautogui.dragTo(%s, %s)" % (x2, y2))
|
1198
|
+
pyautogui.dragTo(x2, y2, button="left", duration=timeframe)
|
1199
|
+
|
1200
|
+
def gui_drag_drop_points(self, x1, y1, x2, y2, timeframe=0.35):
|
1201
|
+
gui_lock = fasteners.InterProcessLock(
|
1202
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1203
|
+
)
|
1204
|
+
with gui_lock: # Prevent issues with multiple processes
|
1205
|
+
self.__install_pyautogui_if_missing()
|
1206
|
+
import pyautogui
|
1207
|
+
pyautogui = self.__get_configured_pyautogui(pyautogui)
|
1208
|
+
width_ratio = 1.0
|
1209
|
+
if shared_utils.is_windows():
|
1210
|
+
window_rect = self.get_window_rect()
|
1211
|
+
width = window_rect["width"]
|
1212
|
+
height = window_rect["height"]
|
1213
|
+
win_x = window_rect["x"]
|
1214
|
+
win_y = window_rect["y"]
|
1215
|
+
scr_width = pyautogui.size().width
|
1216
|
+
self.maximize()
|
1217
|
+
self.__add_light_pause()
|
1218
|
+
win_width = self.get_window_size()["width"]
|
1219
|
+
width_ratio = round(float(scr_width) / float(win_width), 2)
|
1220
|
+
width_ratio += 0.01
|
1221
|
+
if width_ratio < 0.45 or width_ratio > 2.55:
|
1222
|
+
width_ratio = 1.01
|
1223
|
+
sb_config._saved_width_ratio = width_ratio
|
1224
|
+
self.minimize()
|
1225
|
+
self.__add_light_pause()
|
1226
|
+
self.set_window_rect(win_x, win_y, width, height)
|
1227
|
+
self.__add_light_pause()
|
1228
|
+
x1 = x1 * width_ratio
|
1229
|
+
y1 = y1 * width_ratio
|
1230
|
+
x2 = x2 * width_ratio
|
1231
|
+
y2 = y2 * width_ratio
|
1232
|
+
self.bring_active_window_to_front()
|
1233
|
+
self.__gui_drag_drop(
|
1234
|
+
x1, y1, x2, y2, timeframe=timeframe, uc_lock=False
|
1235
|
+
)
|
1236
|
+
self.__slow_mode_pause_if_set()
|
1237
|
+
self.loop.run_until_complete(self.page.wait())
|
1238
|
+
|
1239
|
+
def gui_drag_and_drop(self, drag_selector, drop_selector, timeframe=0.35):
|
1240
|
+
self.__slow_mode_pause_if_set()
|
1241
|
+
x1, y1 = self.get_gui_element_center(drag_selector)
|
1242
|
+
self.__add_light_pause()
|
1243
|
+
x2, y2 = self.get_gui_element_center(drop_selector)
|
1244
|
+
self.__add_light_pause()
|
1245
|
+
self.gui_drag_drop_points(x1, y1, x2, y2, timeframe=timeframe)
|
1246
|
+
|
1247
|
+
def __gui_hover_x_y(self, x, y, timeframe=0.25, uc_lock=False):
|
1248
|
+
self.__install_pyautogui_if_missing()
|
1249
|
+
import pyautogui
|
1250
|
+
pyautogui = self.__get_configured_pyautogui(pyautogui)
|
1251
|
+
screen_width, screen_height = pyautogui.size()
|
1252
|
+
if x < 0 or y < 0 or x > screen_width or y > screen_height:
|
1253
|
+
raise Exception(
|
1254
|
+
"PyAutoGUI cannot hover on point (%s, %s)"
|
1255
|
+
" outside screen. (Width: %s, Height: %s)"
|
1256
|
+
% (x, y, screen_width, screen_height)
|
1257
|
+
)
|
1258
|
+
if uc_lock:
|
1259
|
+
gui_lock = fasteners.InterProcessLock(
|
1260
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1261
|
+
)
|
1262
|
+
with gui_lock: # Prevent issues with multiple processes
|
1263
|
+
pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
|
1264
|
+
time.sleep(0.056)
|
1265
|
+
if "--debug" in sys.argv:
|
1266
|
+
print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x, y))
|
1267
|
+
else:
|
1268
|
+
# Called from a method where the gui_lock is already active
|
1269
|
+
pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
|
1270
|
+
time.sleep(0.056)
|
1271
|
+
if "--debug" in sys.argv:
|
1272
|
+
print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x, y))
|
1273
|
+
|
1274
|
+
def gui_hover_x_y(self, x, y, timeframe=0.25):
|
1016
1275
|
gui_lock = fasteners.InterProcessLock(
|
1017
1276
|
constants.MultiBrowser.PYAUTOGUILOCK
|
1018
1277
|
)
|
@@ -1041,6 +1300,7 @@ class CDPMethods():
|
|
1041
1300
|
else:
|
1042
1301
|
scr_width = pyautogui.size().width
|
1043
1302
|
self.maximize()
|
1303
|
+
self.__add_light_pause()
|
1044
1304
|
win_width = self.get_window_size()["width"]
|
1045
1305
|
width_ratio = round(float(scr_width) / float(win_width), 2)
|
1046
1306
|
width_ratio += 0.01
|
@@ -1048,6 +1308,7 @@ class CDPMethods():
|
|
1048
1308
|
width_ratio = 1.01
|
1049
1309
|
sb_config._saved_width_ratio = width_ratio
|
1050
1310
|
self.set_window_rect(win_x, win_y, width, height)
|
1311
|
+
self.__add_light_pause()
|
1051
1312
|
self.bring_active_window_to_front()
|
1052
1313
|
elif (
|
1053
1314
|
shared_utils.is_windows()
|
@@ -1059,19 +1320,29 @@ class CDPMethods():
|
|
1059
1320
|
if shared_utils.is_windows():
|
1060
1321
|
x = x * width_ratio
|
1061
1322
|
y = y * width_ratio
|
1062
|
-
self.
|
1323
|
+
self.__gui_hover_x_y(x, y, timeframe=timeframe, uc_lock=False)
|
1063
1324
|
return
|
1064
1325
|
self.bring_active_window_to_front()
|
1065
|
-
self.
|
1326
|
+
self.__gui_hover_x_y(x, y, timeframe=timeframe, uc_lock=False)
|
1066
1327
|
|
1067
|
-
def
|
1328
|
+
def gui_hover_element(self, selector, timeframe=0.25):
|
1068
1329
|
self.__slow_mode_pause_if_set()
|
1069
1330
|
x, y = self.get_gui_element_center(selector)
|
1070
1331
|
self.__add_light_pause()
|
1071
|
-
self.
|
1332
|
+
self.__gui_hover_x_y(x, y, timeframe=timeframe)
|
1072
1333
|
self.__slow_mode_pause_if_set()
|
1073
1334
|
self.loop.run_until_complete(self.page.wait())
|
1074
1335
|
|
1336
|
+
def gui_hover_and_click(self, hover_selector, click_selector):
|
1337
|
+
gui_lock = fasteners.InterProcessLock(
|
1338
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1339
|
+
)
|
1340
|
+
with gui_lock:
|
1341
|
+
self.gui_hover_element(hover_selector)
|
1342
|
+
time.sleep(0.15)
|
1343
|
+
self.gui_hover_element(click_selector)
|
1344
|
+
self.click(click_selector)
|
1345
|
+
|
1075
1346
|
def internalize_links(self):
|
1076
1347
|
"""All `target="_blank"` links become `target="_self"`.
|
1077
1348
|
This prevents those links from opening in a new tab."""
|
@@ -1079,24 +1350,30 @@ class CDPMethods():
|
|
1079
1350
|
|
1080
1351
|
def is_checked(self, selector):
|
1081
1352
|
"""Return True if checkbox (or radio button) is checked."""
|
1353
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1082
1354
|
self.find_element(selector, timeout=settings.SMALL_TIMEOUT)
|
1083
1355
|
return self.get_element_attribute(selector, "checked")
|
1084
1356
|
|
1085
1357
|
def is_selected(self, selector):
|
1358
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1086
1359
|
return self.is_checked(selector)
|
1087
1360
|
|
1088
1361
|
def check_if_unchecked(self, selector):
|
1362
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1089
1363
|
if not self.is_checked(selector):
|
1090
1364
|
self.click(selector)
|
1091
1365
|
|
1092
1366
|
def select_if_unselected(self, selector):
|
1367
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1093
1368
|
self.check_if_unchecked(selector)
|
1094
1369
|
|
1095
1370
|
def uncheck_if_checked(self, selector):
|
1371
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1096
1372
|
if self.is_checked(selector):
|
1097
1373
|
self.click(selector)
|
1098
1374
|
|
1099
1375
|
def unselect_if_selected(self, selector):
|
1376
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1100
1377
|
self.uncheck_if_checked(selector)
|
1101
1378
|
|
1102
1379
|
def is_element_present(self, selector):
|
@@ -1105,32 +1382,22 @@ class CDPMethods():
|
|
1105
1382
|
return True
|
1106
1383
|
except Exception:
|
1107
1384
|
return False
|
1108
|
-
selector = self.__convert_to_css_if_xpath(selector)
|
1109
|
-
element = self.loop.run_until_complete(
|
1110
|
-
self.page.js_dumps(
|
1111
|
-
"""document.querySelector('%s')"""
|
1112
|
-
% js_utils.escape_quotes_if_needed(re.escape(selector))
|
1113
|
-
)
|
1114
|
-
)
|
1115
|
-
return element is not None
|
1116
1385
|
|
1117
1386
|
def is_element_visible(self, selector):
|
1118
1387
|
selector = self.__convert_to_css_if_xpath(selector)
|
1119
1388
|
element = None
|
1120
1389
|
if ":contains(" not in selector:
|
1121
1390
|
try:
|
1122
|
-
element = self.
|
1123
|
-
self.page.js_dumps(
|
1124
|
-
"""window.getComputedStyle(document.querySelector"""
|
1125
|
-
"""('%s'))"""
|
1126
|
-
% js_utils.escape_quotes_if_needed(re.escape(selector))
|
1127
|
-
)
|
1128
|
-
)
|
1391
|
+
element = self.select(selector, timeout=0.01)
|
1129
1392
|
except Exception:
|
1130
1393
|
return False
|
1131
1394
|
if not element:
|
1132
1395
|
return False
|
1133
|
-
|
1396
|
+
try:
|
1397
|
+
position = element.get_position()
|
1398
|
+
return (position.width != 0 or position.height != 0)
|
1399
|
+
except Exception:
|
1400
|
+
return False
|
1134
1401
|
else:
|
1135
1402
|
with suppress(Exception):
|
1136
1403
|
tag_name = selector.split(":contains(")[0].split(" ")[-1]
|
@@ -1160,9 +1427,66 @@ class CDPMethods():
|
|
1160
1427
|
raise Exception("Element {%s} not found!" % selector)
|
1161
1428
|
return True
|
1162
1429
|
|
1430
|
+
def assert_element_absent(self, selector, timeout=settings.SMALL_TIMEOUT):
|
1431
|
+
start_ms = time.time() * 1000.0
|
1432
|
+
stop_ms = start_ms + (timeout * 1000.0)
|
1433
|
+
for i in range(int(timeout * 10)):
|
1434
|
+
if not self.is_element_present(selector):
|
1435
|
+
return True
|
1436
|
+
now_ms = time.time() * 1000.0
|
1437
|
+
if now_ms >= stop_ms:
|
1438
|
+
break
|
1439
|
+
time.sleep(0.1)
|
1440
|
+
plural = "s"
|
1441
|
+
if timeout == 1:
|
1442
|
+
plural = ""
|
1443
|
+
raise Exception(
|
1444
|
+
"Element {%s} was still present after %s second%s!"
|
1445
|
+
% (selector, timeout, plural)
|
1446
|
+
)
|
1447
|
+
|
1448
|
+
def assert_element_not_visible(
|
1449
|
+
self, selector, timeout=settings.SMALL_TIMEOUT
|
1450
|
+
):
|
1451
|
+
start_ms = time.time() * 1000.0
|
1452
|
+
stop_ms = start_ms + (timeout * 1000.0)
|
1453
|
+
for i in range(int(timeout * 10)):
|
1454
|
+
if not self.is_element_present(selector):
|
1455
|
+
return True
|
1456
|
+
elif not self.is_element_visible(selector):
|
1457
|
+
return True
|
1458
|
+
now_ms = time.time() * 1000.0
|
1459
|
+
if now_ms >= stop_ms:
|
1460
|
+
break
|
1461
|
+
time.sleep(0.1)
|
1462
|
+
plural = "s"
|
1463
|
+
if timeout == 1:
|
1464
|
+
plural = ""
|
1465
|
+
raise Exception(
|
1466
|
+
"Element {%s} was still visible after %s second%s!"
|
1467
|
+
% (selector, timeout, plural)
|
1468
|
+
)
|
1469
|
+
|
1470
|
+
def assert_title(self, title):
|
1471
|
+
expected = title.strip()
|
1472
|
+
actual = self.get_title().strip()
|
1473
|
+
error = (
|
1474
|
+
"Expected page title [%s] does not match the actual title [%s]!"
|
1475
|
+
)
|
1476
|
+
try:
|
1477
|
+
if expected != actual:
|
1478
|
+
raise Exception(error % (expected, actual))
|
1479
|
+
except Exception:
|
1480
|
+
time.sleep(2)
|
1481
|
+
expected = title.strip()
|
1482
|
+
actual = self.get_title().strip()
|
1483
|
+
if expected != actual:
|
1484
|
+
raise Exception(error % (expected, actual))
|
1485
|
+
|
1163
1486
|
def assert_text(
|
1164
1487
|
self, text, selector="html", timeout=settings.SMALL_TIMEOUT
|
1165
1488
|
):
|
1489
|
+
text = text.strip()
|
1166
1490
|
element = None
|
1167
1491
|
try:
|
1168
1492
|
element = self.select(selector, timeout=timeout)
|
@@ -1180,6 +1504,7 @@ class CDPMethods():
|
|
1180
1504
|
def assert_exact_text(
|
1181
1505
|
self, text, selector="html", timeout=settings.SMALL_TIMEOUT
|
1182
1506
|
):
|
1507
|
+
text = text.strip()
|
1183
1508
|
element = None
|
1184
1509
|
try:
|
1185
1510
|
element = self.select(selector, timeout=timeout)
|
@@ -1197,15 +1522,36 @@ class CDPMethods():
|
|
1197
1522
|
% (text, element.text_all, selector)
|
1198
1523
|
)
|
1199
1524
|
|
1200
|
-
def
|
1201
|
-
self.
|
1202
|
-
|
1203
|
-
|
1525
|
+
def scroll_into_view(self, selector):
|
1526
|
+
self.find_element(selector).scroll_into_view()
|
1527
|
+
self.loop.run_until_complete(self.page.wait())
|
1528
|
+
|
1529
|
+
def scroll_to_y(self, y):
|
1530
|
+
y = int(y)
|
1531
|
+
js_code = "window.scrollTo(0, %s);" % y
|
1532
|
+
with suppress(Exception):
|
1533
|
+
self.loop.run_until_complete(self.page.evaluate(js_code))
|
1534
|
+
self.loop.run_until_complete(self.page.wait())
|
1535
|
+
|
1536
|
+
def scroll_to_top(self):
|
1537
|
+
js_code = "window.scrollTo(0, 0);"
|
1538
|
+
with suppress(Exception):
|
1539
|
+
self.loop.run_until_complete(self.page.evaluate(js_code))
|
1540
|
+
self.loop.run_until_complete(self.page.wait())
|
1541
|
+
|
1542
|
+
def scroll_to_bottom(self):
|
1543
|
+
js_code = "window.scrollTo(0, 10000);"
|
1544
|
+
with suppress(Exception):
|
1545
|
+
self.loop.run_until_complete(self.page.evaluate(js_code))
|
1546
|
+
self.loop.run_until_complete(self.page.wait())
|
1204
1547
|
|
1205
1548
|
def scroll_up(self, amount=25):
|
1206
|
-
self.loop.run_until_complete(
|
1207
|
-
|
1208
|
-
|
1549
|
+
self.loop.run_until_complete(self.page.scroll_up(amount))
|
1550
|
+
self.loop.run_until_complete(self.page.wait())
|
1551
|
+
|
1552
|
+
def scroll_down(self, amount=25):
|
1553
|
+
self.loop.run_until_complete(self.page.scroll_down(amount))
|
1554
|
+
self.loop.run_until_complete(self.page.wait())
|
1209
1555
|
|
1210
1556
|
def save_screenshot(self, name, folder=None, selector=None):
|
1211
1557
|
filename = name
|