seleniumbase 4.32.9__py3-none-any.whl → 4.32.10a1__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 +57 -31
- seleniumbase/core/sb_cdp.py +360 -41
- seleniumbase/core/sb_driver.py +13 -0
- seleniumbase/fixtures/base_case.py +36 -3
- seleniumbase/fixtures/shared_utils.py +3 -3
- seleniumbase/undetected/cdp_driver/element.py +18 -2
- {seleniumbase-4.32.9.dist-info → seleniumbase-4.32.10a1.dist-info}/METADATA +4 -4
- {seleniumbase-4.32.9.dist-info → seleniumbase-4.32.10a1.dist-info}/RECORD +13 -13
- {seleniumbase-4.32.9.dist-info → seleniumbase-4.32.10a1.dist-info}/WHEEL +1 -1
- {seleniumbase-4.32.9.dist-info → seleniumbase-4.32.10a1.dist-info}/LICENSE +0 -0
- {seleniumbase-4.32.9.dist-info → seleniumbase-4.32.10a1.dist-info}/entry_points.txt +0 -0
- {seleniumbase-4.32.9.dist-info → seleniumbase-4.32.10a1.dist-info}/top_level.txt +0 -0
seleniumbase/__version__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
# seleniumbase package
|
2
|
-
__version__ = "4.32.
|
2
|
+
__version__ = "4.32.10a1"
|
@@ -603,6 +603,7 @@ def uc_open_with_cdp_mode(driver, url=None):
|
|
603
603
|
cdp.click = CDPM.click
|
604
604
|
cdp.click_active_element = CDPM.click_active_element
|
605
605
|
cdp.click_if_visible = CDPM.click_if_visible
|
606
|
+
cdp.click_visible_elements = CDPM.click_visible_elements
|
606
607
|
cdp.mouse_click = CDPM.mouse_click
|
607
608
|
cdp.remove_element = CDPM.remove_element
|
608
609
|
cdp.remove_from_dom = CDPM.remove_from_dom
|
@@ -611,6 +612,7 @@ def uc_open_with_cdp_mode(driver, url=None):
|
|
611
612
|
cdp.send_keys = CDPM.send_keys
|
612
613
|
cdp.press_keys = CDPM.press_keys
|
613
614
|
cdp.type = CDPM.type
|
615
|
+
cdp.set_value = CDPM.set_value
|
614
616
|
cdp.evaluate = CDPM.evaluate
|
615
617
|
cdp.js_dumps = CDPM.js_dumps
|
616
618
|
cdp.maximize = CDPM.maximize
|
@@ -625,6 +627,11 @@ def uc_open_with_cdp_mode(driver, url=None):
|
|
625
627
|
cdp.gui_write = CDPM.gui_write
|
626
628
|
cdp.gui_click_x_y = CDPM.gui_click_x_y
|
627
629
|
cdp.gui_click_element = CDPM.gui_click_element
|
630
|
+
cdp.gui_drag_drop_points = CDPM.gui_drag_drop_points
|
631
|
+
cdp.gui_drag_and_drop = CDPM.gui_drag_and_drop
|
632
|
+
cdp.gui_hover_x_y = CDPM.gui_hover_x_y
|
633
|
+
cdp.gui_hover_element = CDPM.gui_hover_element
|
634
|
+
cdp.gui_hover_and_click = CDPM.gui_hover_and_click
|
628
635
|
cdp.internalize_links = CDPM.internalize_links
|
629
636
|
cdp.get_window = CDPM.get_window
|
630
637
|
cdp.get_element_attributes = CDPM.get_element_attributes
|
@@ -651,7 +658,9 @@ def uc_open_with_cdp_mode(driver, url=None):
|
|
651
658
|
cdp.get_window_rect = CDPM.get_window_rect
|
652
659
|
cdp.get_window_size = CDPM.get_window_size
|
653
660
|
cdp.nested_click = CDPM.nested_click
|
661
|
+
cdp.select_option_by_text = CDPM.select_option_by_text
|
654
662
|
cdp.flash = CDPM.flash
|
663
|
+
cdp.highlight = CDPM.highlight
|
655
664
|
cdp.focus = CDPM.focus
|
656
665
|
cdp.highlight_overlay = CDPM.highlight_overlay
|
657
666
|
cdp.get_window_position = CDPM.get_window_position
|
@@ -663,8 +672,11 @@ def uc_open_with_cdp_mode(driver, url=None):
|
|
663
672
|
cdp.is_element_present = CDPM.is_element_present
|
664
673
|
cdp.is_element_visible = CDPM.is_element_visible
|
665
674
|
cdp.assert_element_present = CDPM.assert_element_present
|
675
|
+
cdp.assert_element_absent = CDPM.assert_element_absent
|
666
676
|
cdp.assert_element = CDPM.assert_element
|
667
677
|
cdp.assert_element_visible = CDPM.assert_element
|
678
|
+
cdp.assert_element_not_visible = CDPM.assert_element_not_visible
|
679
|
+
cdp.assert_title = CDPM.assert_title
|
668
680
|
cdp.assert_text = CDPM.assert_text
|
669
681
|
cdp.assert_exact_text = CDPM.assert_exact_text
|
670
682
|
cdp.scroll_down = CDPM.scroll_down
|
@@ -680,6 +692,7 @@ def uc_open_with_cdp_mode(driver, url=None):
|
|
680
692
|
core_items.tab = cdp.tab
|
681
693
|
core_items.util = cdp.util
|
682
694
|
cdp.core = core_items
|
695
|
+
cdp.loop = cdp.get_event_loop()
|
683
696
|
driver.cdp = cdp
|
684
697
|
driver._is_using_cdp = True
|
685
698
|
|
@@ -944,48 +957,46 @@ def uc_gui_click_x_y(driver, x, y, timeframe=0.25):
|
|
944
957
|
connected = True
|
945
958
|
width_ratio = 1.0
|
946
959
|
if IS_WINDOWS:
|
947
|
-
|
948
|
-
driver.window_handles
|
949
|
-
except Exception:
|
950
|
-
connected = False
|
960
|
+
connected = driver.is_connected()
|
951
961
|
if (
|
952
962
|
not connected
|
953
963
|
and (
|
954
964
|
not hasattr(sb_config, "_saved_width_ratio")
|
955
965
|
or not sb_config._saved_width_ratio
|
956
966
|
)
|
967
|
+
and not __is_cdp_swap_needed(driver)
|
957
968
|
):
|
958
969
|
driver.reconnect(0.1)
|
959
|
-
|
960
|
-
if IS_WINDOWS and connected:
|
970
|
+
if IS_WINDOWS and not __is_cdp_swap_needed(driver):
|
961
971
|
window_rect = driver.get_window_rect()
|
962
972
|
width = window_rect["width"]
|
963
973
|
height = window_rect["height"]
|
964
974
|
win_x = window_rect["x"]
|
965
975
|
win_y = window_rect["y"]
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
)
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
driver.maximize_window()
|
974
|
-
win_width = driver.get_window_size()["width"]
|
975
|
-
width_ratio = round(float(scr_width) / float(win_width), 2)
|
976
|
-
width_ratio += 0.01
|
977
|
-
if width_ratio < 0.45 or width_ratio > 2.55:
|
978
|
-
width_ratio = 1.01
|
979
|
-
sb_config._saved_width_ratio = width_ratio
|
976
|
+
scr_width = pyautogui.size().width
|
977
|
+
driver.maximize_window()
|
978
|
+
win_width = driver.get_window_size()["width"]
|
979
|
+
width_ratio = round(float(scr_width) / float(win_width), 2) + 0.01
|
980
|
+
if width_ratio < 0.45 or width_ratio > 2.55:
|
981
|
+
width_ratio = 1.01
|
982
|
+
sb_config._saved_width_ratio = width_ratio
|
980
983
|
driver.minimize_window()
|
981
984
|
driver.set_window_rect(win_x, win_y, width, height)
|
982
|
-
elif (
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
985
|
+
elif IS_WINDOWS and __is_cdp_swap_needed(driver):
|
986
|
+
window_rect = driver.cdp.get_window_rect()
|
987
|
+
width = window_rect["width"]
|
988
|
+
height = window_rect["height"]
|
989
|
+
win_x = window_rect["x"]
|
990
|
+
win_y = window_rect["y"]
|
991
|
+
scr_width = pyautogui.size().width
|
992
|
+
driver.cdp.maximize()
|
993
|
+
win_width = driver.cdp.get_window_size()["width"]
|
994
|
+
width_ratio = round(float(scr_width) / float(win_width), 2) + 0.01
|
995
|
+
if width_ratio < 0.45 or width_ratio > 2.55:
|
996
|
+
width_ratio = 1.01
|
997
|
+
sb_config._saved_width_ratio = width_ratio
|
998
|
+
driver.cdp.minimize()
|
999
|
+
driver.cdp.set_window_rect(win_x, win_y, width, height)
|
989
1000
|
if IS_WINDOWS:
|
990
1001
|
x = x * width_ratio
|
991
1002
|
y = y * width_ratio
|
@@ -1090,6 +1101,21 @@ def _uc_gui_click_captcha(
|
|
1090
1101
|
sb_config._saved_width_ratio = width_ratio
|
1091
1102
|
driver.minimize_window()
|
1092
1103
|
driver.set_window_rect(win_x, win_y, width, height)
|
1104
|
+
elif IS_WINDOWS and __is_cdp_swap_needed(driver):
|
1105
|
+
window_rect = driver.cdp.get_window_rect()
|
1106
|
+
width = window_rect["width"]
|
1107
|
+
height = window_rect["height"]
|
1108
|
+
win_x = window_rect["x"]
|
1109
|
+
win_y = window_rect["y"]
|
1110
|
+
scr_width = pyautogui.size().width
|
1111
|
+
driver.cdp.maximize()
|
1112
|
+
win_width = driver.cdp.get_window_size()["width"]
|
1113
|
+
width_ratio = round(float(scr_width) / float(win_width), 2) + 0.01
|
1114
|
+
if width_ratio < 0.45 or width_ratio > 2.55:
|
1115
|
+
width_ratio = 1.01
|
1116
|
+
sb_config._saved_width_ratio = width_ratio
|
1117
|
+
driver.cdp.minimize()
|
1118
|
+
driver.cdp.set_window_rect(win_x, win_y, width, height)
|
1093
1119
|
if ctype == "cf_t":
|
1094
1120
|
if (
|
1095
1121
|
driver.is_element_present(".cf-turnstile-wrapper iframe")
|
@@ -1201,10 +1227,10 @@ def _uc_gui_click_captcha(
|
|
1201
1227
|
element = driver.wait_for_element_present(
|
1202
1228
|
selector, timeout=2.5
|
1203
1229
|
)
|
1204
|
-
x = i_x + element.rect["x"] +
|
1205
|
-
x +=
|
1206
|
-
y = i_y + element.rect["y"] +
|
1207
|
-
y +=
|
1230
|
+
x = i_x + element.rect["x"] + (element.rect["width"] / 2.0)
|
1231
|
+
x += 0.5
|
1232
|
+
y = i_y + element.rect["y"] + (element.rect["height"] / 2.0)
|
1233
|
+
y += 0.5
|
1208
1234
|
else:
|
1209
1235
|
x = (i_x + 34) * width_ratio
|
1210
1236
|
y = (i_y + 34) * width_ratio
|
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)
|
@@ -258,9 +260,11 @@ class CDPMethods():
|
|
258
260
|
self.loop.run_until_complete(self.page.wait())
|
259
261
|
return result
|
260
262
|
|
261
|
-
def __flash(self, element):
|
263
|
+
def __flash(self, element, *args, **kwargs):
|
262
264
|
return (
|
263
|
-
self.loop.run_until_complete(
|
265
|
+
self.loop.run_until_complete(
|
266
|
+
element.flash_async(*args, **kwargs)
|
267
|
+
)
|
264
268
|
)
|
265
269
|
|
266
270
|
def __focus(self, element):
|
@@ -292,11 +296,11 @@ class CDPMethods():
|
|
292
296
|
|
293
297
|
def __query_selector(self, element, selector):
|
294
298
|
selector = self.__convert_to_css_if_xpath(selector)
|
295
|
-
|
299
|
+
element2 = self.loop.run_until_complete(
|
296
300
|
element.query_selector_async(selector)
|
297
301
|
)
|
298
|
-
|
299
|
-
return
|
302
|
+
element2 = self.__add_sync_methods(element2)
|
303
|
+
return element2
|
300
304
|
|
301
305
|
def __query_selector_all(self, element, selector):
|
302
306
|
selector = self.__convert_to_css_if_xpath(selector)
|
@@ -467,6 +471,19 @@ class CDPMethods():
|
|
467
471
|
self.__slow_mode_pause_if_set()
|
468
472
|
self.loop.run_until_complete(self.page.wait())
|
469
473
|
|
474
|
+
def click_visible_elements(self, selector):
|
475
|
+
elements = self.select_all(selector)
|
476
|
+
for element in elements:
|
477
|
+
try:
|
478
|
+
position = element.get_position()
|
479
|
+
if (position.width != 0 or position.height != 0):
|
480
|
+
element.click()
|
481
|
+
time.sleep(0.0375)
|
482
|
+
self.__slow_mode_pause_if_set()
|
483
|
+
self.loop.run_until_complete(self.page.wait())
|
484
|
+
except Exception:
|
485
|
+
pass
|
486
|
+
|
470
487
|
def mouse_click(self, selector, timeout=settings.SMALL_TIMEOUT):
|
471
488
|
"""(Attempt simulating a mouse click)"""
|
472
489
|
self.__slow_mode_pause_if_set()
|
@@ -491,9 +508,45 @@ class CDPMethods():
|
|
491
508
|
element = self.find_element(parent_selector)
|
492
509
|
return element.query_selector(selector)
|
493
510
|
|
494
|
-
def
|
511
|
+
def select_option_by_text(self, dropdown_selector, option):
|
512
|
+
element = self.find_element(dropdown_selector)
|
513
|
+
options = element.query_selector_all("option")
|
514
|
+
for found_option in options:
|
515
|
+
if found_option.text.strip() == option.strip():
|
516
|
+
found_option.select_option()
|
517
|
+
return
|
518
|
+
raise Exception(
|
519
|
+
"Unable to find text option {%s} in dropdown {%s}!"
|
520
|
+
% (dropdown_selector, option)
|
521
|
+
)
|
522
|
+
|
523
|
+
def flash(
|
524
|
+
self,
|
525
|
+
selector, # The CSS Selector to flash
|
526
|
+
duration=1, # (seconds) flash duration
|
527
|
+
color="44CC88", # RGB hex flash color
|
528
|
+
pause=0, # (seconds) If 0, the next action starts during flash
|
529
|
+
):
|
495
530
|
"""Paint a quickly-vanishing dot over an element."""
|
496
|
-
self.
|
531
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
532
|
+
element = self.find_element(selector)
|
533
|
+
element.flash(duration=duration, color=color)
|
534
|
+
if pause and isinstance(pause, (int, float)):
|
535
|
+
time.sleep(pause)
|
536
|
+
|
537
|
+
def highlight(self, selector):
|
538
|
+
"""Highlight an element with multi-colors."""
|
539
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
540
|
+
element = self.find_element(selector)
|
541
|
+
element = self.find_element(selector)
|
542
|
+
element.flash(0.46, "44CC88")
|
543
|
+
time.sleep(0.15)
|
544
|
+
element.flash(0.42, "8844CC")
|
545
|
+
time.sleep(0.15)
|
546
|
+
element.flash(0.38, "CC8844")
|
547
|
+
time.sleep(0.15)
|
548
|
+
element.flash(0.30, "44CC88")
|
549
|
+
time.sleep(0.30)
|
497
550
|
|
498
551
|
def focus(self, selector):
|
499
552
|
self.find_element(selector).focus()
|
@@ -526,8 +579,9 @@ class CDPMethods():
|
|
526
579
|
self.find_element(selector).scroll_into_view()
|
527
580
|
|
528
581
|
def send_keys(self, selector, text, timeout=settings.SMALL_TIMEOUT):
|
529
|
-
element = self.select(selector)
|
530
582
|
self.__slow_mode_pause_if_set()
|
583
|
+
element = self.select(selector, timeout=timeout)
|
584
|
+
self.__add_light_pause()
|
531
585
|
if text.endswith("\n") or text.endswith("\r"):
|
532
586
|
text = text[:-1] + "\r\n"
|
533
587
|
element.send_keys(text)
|
@@ -536,8 +590,9 @@ class CDPMethods():
|
|
536
590
|
|
537
591
|
def press_keys(self, selector, text, timeout=settings.SMALL_TIMEOUT):
|
538
592
|
"""Similar to send_keys(), but presses keys at human speed."""
|
539
|
-
element = self.select(selector)
|
540
593
|
self.__slow_mode_pause_if_set()
|
594
|
+
element = self.select(selector, timeout=timeout)
|
595
|
+
self.__add_light_pause()
|
541
596
|
submit = False
|
542
597
|
if text.endswith("\n") or text.endswith("\r"):
|
543
598
|
submit = True
|
@@ -553,8 +608,9 @@ class CDPMethods():
|
|
553
608
|
|
554
609
|
def type(self, selector, text, timeout=settings.SMALL_TIMEOUT):
|
555
610
|
"""Similar to send_keys(), but clears the text field first."""
|
556
|
-
element = self.select(selector)
|
557
611
|
self.__slow_mode_pause_if_set()
|
612
|
+
element = self.select(selector, timeout=timeout)
|
613
|
+
self.__add_light_pause()
|
558
614
|
with suppress(Exception):
|
559
615
|
element.clear_input()
|
560
616
|
if text.endswith("\n") or text.endswith("\r"):
|
@@ -563,6 +619,42 @@ class CDPMethods():
|
|
563
619
|
self.__slow_mode_pause_if_set()
|
564
620
|
self.loop.run_until_complete(self.page.wait())
|
565
621
|
|
622
|
+
def set_value(self, selector, text, timeout=settings.SMALL_TIMEOUT):
|
623
|
+
"""Similar to send_keys(), but clears the text field first."""
|
624
|
+
self.__slow_mode_pause_if_set()
|
625
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
626
|
+
self.select(selector, timeout=timeout)
|
627
|
+
self.__add_light_pause()
|
628
|
+
press_enter = False
|
629
|
+
if text.endswith("\n"):
|
630
|
+
text = text[:-1]
|
631
|
+
press_enter = True
|
632
|
+
value = js_utils.escape_quotes_if_needed(re.escape(text))
|
633
|
+
css_selector = re.escape(selector)
|
634
|
+
css_selector = js_utils.escape_quotes_if_needed(css_selector)
|
635
|
+
set_value_script = (
|
636
|
+
"""m_elm = document.querySelector('%s');"""
|
637
|
+
"""m_elm.value = '%s';""" % (css_selector, value)
|
638
|
+
)
|
639
|
+
self.loop.run_until_complete(self.page.evaluate(set_value_script))
|
640
|
+
the_type = self.get_element_attribute(selector, "type")
|
641
|
+
if the_type == "range":
|
642
|
+
# Some input sliders need a mouse event to trigger listeners.
|
643
|
+
with suppress(Exception):
|
644
|
+
mouse_move_script = (
|
645
|
+
"""m_elm = document.querySelector('%s');"""
|
646
|
+
"""m_evt = new Event('mousemove');"""
|
647
|
+
"""m_elm.dispatchEvent(m_evt);""" % css_selector
|
648
|
+
)
|
649
|
+
self.loop.run_until_complete(
|
650
|
+
self.page.evaluate(mouse_move_script)
|
651
|
+
)
|
652
|
+
elif press_enter:
|
653
|
+
self.__add_light_pause()
|
654
|
+
self.send_keys(selector, "\n")
|
655
|
+
self.__slow_mode_pause_if_set()
|
656
|
+
self.loop.run_until_complete(self.page.wait())
|
657
|
+
|
566
658
|
def evaluate(self, expression):
|
567
659
|
"""Run a JavaScript expression and return the result."""
|
568
660
|
return self.loop.run_until_complete(
|
@@ -576,21 +668,30 @@ class CDPMethods():
|
|
576
668
|
)
|
577
669
|
|
578
670
|
def maximize(self):
|
579
|
-
|
580
|
-
|
581
|
-
)
|
671
|
+
if self.get_window()[1].window_state.value == "maximized":
|
672
|
+
return
|
673
|
+
elif self.get_window()[1].window_state.value == "minimized":
|
674
|
+
self.loop.run_until_complete(self.page.maximize())
|
675
|
+
time.sleep(0.0375)
|
676
|
+
return self.loop.run_until_complete(self.page.maximize())
|
582
677
|
|
583
678
|
def minimize(self):
|
584
|
-
|
585
|
-
self.page.minimize()
|
586
|
-
)
|
679
|
+
if self.get_window()[1].window_state.value != "minimized":
|
680
|
+
return self.loop.run_until_complete(self.page.minimize())
|
587
681
|
|
588
682
|
def medimize(self):
|
589
|
-
|
590
|
-
self.page.medimize()
|
591
|
-
|
683
|
+
if self.get_window()[1].window_state.value == "minimized":
|
684
|
+
self.loop.run_until_complete(self.page.medimize())
|
685
|
+
time.sleep(0.0375)
|
686
|
+
return self.loop.run_until_complete(self.page.medimize())
|
592
687
|
|
593
688
|
def set_window_rect(self, x, y, width, height):
|
689
|
+
if self.get_window()[1].window_state.value == "minimized":
|
690
|
+
self.loop.run_until_complete(
|
691
|
+
self.page.set_window_size(
|
692
|
+
left=x, top=y, width=width, height=height)
|
693
|
+
)
|
694
|
+
time.sleep(0.0375)
|
594
695
|
return self.loop.run_until_complete(
|
595
696
|
self.page.set_window_size(
|
596
697
|
left=x, top=y, width=width, height=height)
|
@@ -602,6 +703,7 @@ class CDPMethods():
|
|
602
703
|
width = settings.CHROME_START_WIDTH
|
603
704
|
height = settings.CHROME_START_HEIGHT
|
604
705
|
self.set_window_rect(x, y, width, height)
|
706
|
+
self.__add_light_pause()
|
605
707
|
|
606
708
|
def get_window(self):
|
607
709
|
return self.loop.run_until_complete(
|
@@ -785,7 +887,7 @@ class CDPMethods():
|
|
785
887
|
e_height = element_rect["height"]
|
786
888
|
e_x = element_rect["x"]
|
787
889
|
e_y = element_rect["y"]
|
788
|
-
return ((e_x + e_width / 2), (e_y + e_height / 2))
|
890
|
+
return ((e_x + e_width / 2.0) + 0.5, (e_y + e_height / 2.0) + 0.5)
|
789
891
|
|
790
892
|
def get_document(self):
|
791
893
|
return self.loop.run_until_complete(
|
@@ -798,6 +900,7 @@ class CDPMethods():
|
|
798
900
|
)
|
799
901
|
|
800
902
|
def get_element_attributes(self, selector):
|
903
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
801
904
|
return self.loop.run_until_complete(
|
802
905
|
self.page.js_dumps(
|
803
906
|
"""document.querySelector('%s')"""
|
@@ -1013,6 +1116,156 @@ class CDPMethods():
|
|
1013
1116
|
pyautogui.click(x=x, y=y)
|
1014
1117
|
|
1015
1118
|
def gui_click_x_y(self, x, y, timeframe=0.25):
|
1119
|
+
gui_lock = fasteners.InterProcessLock(
|
1120
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1121
|
+
)
|
1122
|
+
with gui_lock: # Prevent issues with multiple processes
|
1123
|
+
self.__install_pyautogui_if_missing()
|
1124
|
+
import pyautogui
|
1125
|
+
pyautogui = self.__get_configured_pyautogui(pyautogui)
|
1126
|
+
width_ratio = 1.0
|
1127
|
+
if shared_utils.is_windows():
|
1128
|
+
window_rect = self.get_window_rect()
|
1129
|
+
width = window_rect["width"]
|
1130
|
+
height = window_rect["height"]
|
1131
|
+
win_x = window_rect["x"]
|
1132
|
+
win_y = window_rect["y"]
|
1133
|
+
scr_width = pyautogui.size().width
|
1134
|
+
self.maximize()
|
1135
|
+
self.__add_light_pause()
|
1136
|
+
win_width = self.get_window_size()["width"]
|
1137
|
+
width_ratio = round(float(scr_width) / float(win_width), 2)
|
1138
|
+
width_ratio += 0.01
|
1139
|
+
if width_ratio < 0.45 or width_ratio > 2.55:
|
1140
|
+
width_ratio = 1.01
|
1141
|
+
sb_config._saved_width_ratio = width_ratio
|
1142
|
+
self.minimize()
|
1143
|
+
self.__add_light_pause()
|
1144
|
+
self.set_window_rect(win_x, win_y, width, height)
|
1145
|
+
self.__add_light_pause()
|
1146
|
+
x = x * width_ratio
|
1147
|
+
y = y * width_ratio
|
1148
|
+
self.bring_active_window_to_front()
|
1149
|
+
self.__gui_click_x_y(x, y, timeframe=timeframe, uc_lock=False)
|
1150
|
+
|
1151
|
+
def gui_click_element(self, selector, timeframe=0.25):
|
1152
|
+
self.__slow_mode_pause_if_set()
|
1153
|
+
x, y = self.get_gui_element_center(selector)
|
1154
|
+
self.__add_light_pause()
|
1155
|
+
self.gui_click_x_y(x, y, timeframe=timeframe)
|
1156
|
+
self.__slow_mode_pause_if_set()
|
1157
|
+
self.loop.run_until_complete(self.page.wait())
|
1158
|
+
|
1159
|
+
def __gui_drag_drop(self, x1, y1, x2, y2, timeframe=0.25, uc_lock=False):
|
1160
|
+
self.__install_pyautogui_if_missing()
|
1161
|
+
import pyautogui
|
1162
|
+
pyautogui = self.__get_configured_pyautogui(pyautogui)
|
1163
|
+
screen_width, screen_height = pyautogui.size()
|
1164
|
+
if x1 < 0 or y1 < 0 or x1 > screen_width or y1 > screen_height:
|
1165
|
+
raise Exception(
|
1166
|
+
"PyAutoGUI cannot drag-drop from point (%s, %s)"
|
1167
|
+
" outside screen. (Width: %s, Height: %s)"
|
1168
|
+
% (x1, y1, screen_width, screen_height)
|
1169
|
+
)
|
1170
|
+
if x2 < 0 or y2 < 0 or x2 > screen_width or y2 > screen_height:
|
1171
|
+
raise Exception(
|
1172
|
+
"PyAutoGUI cannot drag-drop to point (%s, %s)"
|
1173
|
+
" outside screen. (Width: %s, Height: %s)"
|
1174
|
+
% (x2, y2, screen_width, screen_height)
|
1175
|
+
)
|
1176
|
+
if uc_lock:
|
1177
|
+
gui_lock = fasteners.InterProcessLock(
|
1178
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1179
|
+
)
|
1180
|
+
with gui_lock: # Prevent issues with multiple processes
|
1181
|
+
pyautogui.moveTo(x1, y1, 0.25, pyautogui.easeOutQuad)
|
1182
|
+
self.__add_light_pause()
|
1183
|
+
if "--debug" in sys.argv:
|
1184
|
+
print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x1, y1))
|
1185
|
+
pyautogui.dragTo(x2, y2, button="left", duration=timeframe)
|
1186
|
+
else:
|
1187
|
+
# Called from a method where the gui_lock is already active
|
1188
|
+
pyautogui.moveTo(x1, y1, 0.25, pyautogui.easeOutQuad)
|
1189
|
+
self.__add_light_pause()
|
1190
|
+
if "--debug" in sys.argv:
|
1191
|
+
print(" <DEBUG> pyautogui.dragTo(%s, %s)" % (x2, y2))
|
1192
|
+
pyautogui.dragTo(x2, y2, button="left", duration=timeframe)
|
1193
|
+
|
1194
|
+
def gui_drag_drop_points(self, x1, y1, x2, y2, timeframe=0.35):
|
1195
|
+
gui_lock = fasteners.InterProcessLock(
|
1196
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1197
|
+
)
|
1198
|
+
with gui_lock: # Prevent issues with multiple processes
|
1199
|
+
self.__install_pyautogui_if_missing()
|
1200
|
+
import pyautogui
|
1201
|
+
pyautogui = self.__get_configured_pyautogui(pyautogui)
|
1202
|
+
width_ratio = 1.0
|
1203
|
+
if shared_utils.is_windows():
|
1204
|
+
window_rect = self.get_window_rect()
|
1205
|
+
width = window_rect["width"]
|
1206
|
+
height = window_rect["height"]
|
1207
|
+
win_x = window_rect["x"]
|
1208
|
+
win_y = window_rect["y"]
|
1209
|
+
scr_width = pyautogui.size().width
|
1210
|
+
self.maximize()
|
1211
|
+
self.__add_light_pause()
|
1212
|
+
win_width = self.get_window_size()["width"]
|
1213
|
+
width_ratio = round(float(scr_width) / float(win_width), 2)
|
1214
|
+
width_ratio += 0.01
|
1215
|
+
if width_ratio < 0.45 or width_ratio > 2.55:
|
1216
|
+
width_ratio = 1.01
|
1217
|
+
sb_config._saved_width_ratio = width_ratio
|
1218
|
+
self.minimize()
|
1219
|
+
self.__add_light_pause()
|
1220
|
+
self.set_window_rect(win_x, win_y, width, height)
|
1221
|
+
self.__add_light_pause()
|
1222
|
+
x1 = x1 * width_ratio
|
1223
|
+
y1 = y1 * width_ratio
|
1224
|
+
x2 = x2 * width_ratio
|
1225
|
+
y2 = y2 * width_ratio
|
1226
|
+
self.bring_active_window_to_front()
|
1227
|
+
self.__gui_drag_drop(
|
1228
|
+
x1, y1, x2, y2, timeframe=timeframe, uc_lock=False
|
1229
|
+
)
|
1230
|
+
self.__slow_mode_pause_if_set()
|
1231
|
+
self.loop.run_until_complete(self.page.wait())
|
1232
|
+
|
1233
|
+
def gui_drag_and_drop(self, drag_selector, drop_selector, timeframe=0.35):
|
1234
|
+
self.__slow_mode_pause_if_set()
|
1235
|
+
x1, y1 = self.get_gui_element_center(drag_selector)
|
1236
|
+
self.__add_light_pause()
|
1237
|
+
x2, y2 = self.get_gui_element_center(drop_selector)
|
1238
|
+
self.__add_light_pause()
|
1239
|
+
self.gui_drag_drop_points(x1, y1, x2, y2, timeframe=timeframe)
|
1240
|
+
|
1241
|
+
def __gui_hover_x_y(self, x, y, timeframe=0.25, uc_lock=False):
|
1242
|
+
self.__install_pyautogui_if_missing()
|
1243
|
+
import pyautogui
|
1244
|
+
pyautogui = self.__get_configured_pyautogui(pyautogui)
|
1245
|
+
screen_width, screen_height = pyautogui.size()
|
1246
|
+
if x < 0 or y < 0 or x > screen_width or y > screen_height:
|
1247
|
+
raise Exception(
|
1248
|
+
"PyAutoGUI cannot hover on point (%s, %s)"
|
1249
|
+
" outside screen. (Width: %s, Height: %s)"
|
1250
|
+
% (x, y, screen_width, screen_height)
|
1251
|
+
)
|
1252
|
+
if uc_lock:
|
1253
|
+
gui_lock = fasteners.InterProcessLock(
|
1254
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1255
|
+
)
|
1256
|
+
with gui_lock: # Prevent issues with multiple processes
|
1257
|
+
pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
|
1258
|
+
time.sleep(0.056)
|
1259
|
+
if "--debug" in sys.argv:
|
1260
|
+
print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x, y))
|
1261
|
+
else:
|
1262
|
+
# Called from a method where the gui_lock is already active
|
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
|
+
|
1268
|
+
def gui_hover_x_y(self, x, y, timeframe=0.25):
|
1016
1269
|
gui_lock = fasteners.InterProcessLock(
|
1017
1270
|
constants.MultiBrowser.PYAUTOGUILOCK
|
1018
1271
|
)
|
@@ -1041,6 +1294,7 @@ class CDPMethods():
|
|
1041
1294
|
else:
|
1042
1295
|
scr_width = pyautogui.size().width
|
1043
1296
|
self.maximize()
|
1297
|
+
self.__add_light_pause()
|
1044
1298
|
win_width = self.get_window_size()["width"]
|
1045
1299
|
width_ratio = round(float(scr_width) / float(win_width), 2)
|
1046
1300
|
width_ratio += 0.01
|
@@ -1048,6 +1302,7 @@ class CDPMethods():
|
|
1048
1302
|
width_ratio = 1.01
|
1049
1303
|
sb_config._saved_width_ratio = width_ratio
|
1050
1304
|
self.set_window_rect(win_x, win_y, width, height)
|
1305
|
+
self.__add_light_pause()
|
1051
1306
|
self.bring_active_window_to_front()
|
1052
1307
|
elif (
|
1053
1308
|
shared_utils.is_windows()
|
@@ -1059,19 +1314,29 @@ class CDPMethods():
|
|
1059
1314
|
if shared_utils.is_windows():
|
1060
1315
|
x = x * width_ratio
|
1061
1316
|
y = y * width_ratio
|
1062
|
-
self.
|
1317
|
+
self.__gui_hover_x_y(x, y, timeframe=timeframe, uc_lock=False)
|
1063
1318
|
return
|
1064
1319
|
self.bring_active_window_to_front()
|
1065
|
-
self.
|
1320
|
+
self.__gui_hover_x_y(x, y, timeframe=timeframe, uc_lock=False)
|
1066
1321
|
|
1067
|
-
def
|
1322
|
+
def gui_hover_element(self, selector, timeframe=0.25):
|
1068
1323
|
self.__slow_mode_pause_if_set()
|
1069
1324
|
x, y = self.get_gui_element_center(selector)
|
1070
1325
|
self.__add_light_pause()
|
1071
|
-
self.
|
1326
|
+
self.__gui_hover_x_y(x, y, timeframe=timeframe)
|
1072
1327
|
self.__slow_mode_pause_if_set()
|
1073
1328
|
self.loop.run_until_complete(self.page.wait())
|
1074
1329
|
|
1330
|
+
def gui_hover_and_click(self, hover_selector, click_selector):
|
1331
|
+
gui_lock = fasteners.InterProcessLock(
|
1332
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1333
|
+
)
|
1334
|
+
with gui_lock:
|
1335
|
+
self.gui_hover_element(hover_selector)
|
1336
|
+
time.sleep(0.15)
|
1337
|
+
self.gui_hover_element(click_selector)
|
1338
|
+
self.click(click_selector)
|
1339
|
+
|
1075
1340
|
def internalize_links(self):
|
1076
1341
|
"""All `target="_blank"` links become `target="_self"`.
|
1077
1342
|
This prevents those links from opening in a new tab."""
|
@@ -1079,24 +1344,30 @@ class CDPMethods():
|
|
1079
1344
|
|
1080
1345
|
def is_checked(self, selector):
|
1081
1346
|
"""Return True if checkbox (or radio button) is checked."""
|
1347
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1082
1348
|
self.find_element(selector, timeout=settings.SMALL_TIMEOUT)
|
1083
1349
|
return self.get_element_attribute(selector, "checked")
|
1084
1350
|
|
1085
1351
|
def is_selected(self, selector):
|
1352
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1086
1353
|
return self.is_checked(selector)
|
1087
1354
|
|
1088
1355
|
def check_if_unchecked(self, selector):
|
1356
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1089
1357
|
if not self.is_checked(selector):
|
1090
1358
|
self.click(selector)
|
1091
1359
|
|
1092
1360
|
def select_if_unselected(self, selector):
|
1361
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1093
1362
|
self.check_if_unchecked(selector)
|
1094
1363
|
|
1095
1364
|
def uncheck_if_checked(self, selector):
|
1365
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1096
1366
|
if self.is_checked(selector):
|
1097
1367
|
self.click(selector)
|
1098
1368
|
|
1099
1369
|
def unselect_if_selected(self, selector):
|
1370
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1100
1371
|
self.uncheck_if_checked(selector)
|
1101
1372
|
|
1102
1373
|
def is_element_present(self, selector):
|
@@ -1105,32 +1376,22 @@ class CDPMethods():
|
|
1105
1376
|
return True
|
1106
1377
|
except Exception:
|
1107
1378
|
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
1379
|
|
1117
1380
|
def is_element_visible(self, selector):
|
1118
1381
|
selector = self.__convert_to_css_if_xpath(selector)
|
1119
1382
|
element = None
|
1120
1383
|
if ":contains(" not in selector:
|
1121
1384
|
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
|
-
)
|
1385
|
+
element = self.select(selector, timeout=0.01)
|
1129
1386
|
except Exception:
|
1130
1387
|
return False
|
1131
1388
|
if not element:
|
1132
1389
|
return False
|
1133
|
-
|
1390
|
+
try:
|
1391
|
+
position = element.get_position()
|
1392
|
+
return (position.width != 0 or position.height != 0)
|
1393
|
+
except Exception:
|
1394
|
+
return False
|
1134
1395
|
else:
|
1135
1396
|
with suppress(Exception):
|
1136
1397
|
tag_name = selector.split(":contains(")[0].split(" ")[-1]
|
@@ -1160,9 +1421,66 @@ class CDPMethods():
|
|
1160
1421
|
raise Exception("Element {%s} not found!" % selector)
|
1161
1422
|
return True
|
1162
1423
|
|
1424
|
+
def assert_element_absent(self, selector, timeout=settings.SMALL_TIMEOUT):
|
1425
|
+
start_ms = time.time() * 1000.0
|
1426
|
+
stop_ms = start_ms + (timeout * 1000.0)
|
1427
|
+
for i in range(int(timeout * 10)):
|
1428
|
+
if not self.is_element_present(selector):
|
1429
|
+
return True
|
1430
|
+
now_ms = time.time() * 1000.0
|
1431
|
+
if now_ms >= stop_ms:
|
1432
|
+
break
|
1433
|
+
time.sleep(0.1)
|
1434
|
+
plural = "s"
|
1435
|
+
if timeout == 1:
|
1436
|
+
plural = ""
|
1437
|
+
raise Exception(
|
1438
|
+
"Element {%s} was still present after %s second%s!"
|
1439
|
+
% (selector, timeout, plural)
|
1440
|
+
)
|
1441
|
+
|
1442
|
+
def assert_element_not_visible(
|
1443
|
+
self, selector, timeout=settings.SMALL_TIMEOUT
|
1444
|
+
):
|
1445
|
+
start_ms = time.time() * 1000.0
|
1446
|
+
stop_ms = start_ms + (timeout * 1000.0)
|
1447
|
+
for i in range(int(timeout * 10)):
|
1448
|
+
if not self.is_element_present(selector):
|
1449
|
+
return True
|
1450
|
+
elif not self.is_element_visible(selector):
|
1451
|
+
return True
|
1452
|
+
now_ms = time.time() * 1000.0
|
1453
|
+
if now_ms >= stop_ms:
|
1454
|
+
break
|
1455
|
+
time.sleep(0.1)
|
1456
|
+
plural = "s"
|
1457
|
+
if timeout == 1:
|
1458
|
+
plural = ""
|
1459
|
+
raise Exception(
|
1460
|
+
"Element {%s} was still visible after %s second%s!"
|
1461
|
+
% (selector, timeout, plural)
|
1462
|
+
)
|
1463
|
+
|
1464
|
+
def assert_title(self, title):
|
1465
|
+
expected = title.strip()
|
1466
|
+
actual = self.get_title().strip()
|
1467
|
+
error = (
|
1468
|
+
"Expected page title [%s] does not match the actual title [%s]!"
|
1469
|
+
)
|
1470
|
+
try:
|
1471
|
+
if expected != actual:
|
1472
|
+
raise Exception(error % (expected, actual))
|
1473
|
+
except Exception:
|
1474
|
+
time.sleep(2)
|
1475
|
+
expected = title.strip()
|
1476
|
+
actual = self.get_title().strip()
|
1477
|
+
if expected != actual:
|
1478
|
+
raise Exception(error % (expected, actual))
|
1479
|
+
|
1163
1480
|
def assert_text(
|
1164
1481
|
self, text, selector="html", timeout=settings.SMALL_TIMEOUT
|
1165
1482
|
):
|
1483
|
+
text = text.strip()
|
1166
1484
|
element = None
|
1167
1485
|
try:
|
1168
1486
|
element = self.select(selector, timeout=timeout)
|
@@ -1180,6 +1498,7 @@ class CDPMethods():
|
|
1180
1498
|
def assert_exact_text(
|
1181
1499
|
self, text, selector="html", timeout=settings.SMALL_TIMEOUT
|
1182
1500
|
):
|
1501
|
+
text = text.strip()
|
1183
1502
|
element = None
|
1184
1503
|
try:
|
1185
1504
|
element = self.select(selector, timeout=timeout)
|
seleniumbase/core/sb_driver.py
CHANGED
@@ -198,6 +198,11 @@ class DriverMethods():
|
|
198
198
|
In CDP Mode, the CDP-Driver controls the web browser.
|
199
199
|
The CDP-Driver can be connected while WebDriver isn't.
|
200
200
|
"""
|
201
|
+
if shared_utils.is_windows():
|
202
|
+
return (
|
203
|
+
not hasattr(self.driver, "_is_connected")
|
204
|
+
or self.driver._is_connected
|
205
|
+
)
|
201
206
|
try:
|
202
207
|
self.driver.window_handles
|
203
208
|
return True
|
@@ -238,6 +243,14 @@ class DriverMethods():
|
|
238
243
|
return js_utils.get_user_agent(self.driver, *args, **kwargs)
|
239
244
|
|
240
245
|
def highlight(self, *args, **kwargs):
|
246
|
+
if self.__is_cdp_swap_needed():
|
247
|
+
selector = None
|
248
|
+
if "selector" in kwargs:
|
249
|
+
selector = kwargs["selector"]
|
250
|
+
else:
|
251
|
+
selector = args[0]
|
252
|
+
self.driver.cdp.highlight(selector)
|
253
|
+
return
|
241
254
|
if "scroll" in kwargs:
|
242
255
|
kwargs.pop("scroll")
|
243
256
|
w_args = kwargs.copy()
|
@@ -1592,6 +1592,9 @@ class BaseCase(unittest.TestCase):
|
|
1592
1592
|
def click_link_text(self, link_text, timeout=None):
|
1593
1593
|
"""This method clicks link text on a page."""
|
1594
1594
|
self.__check_scope()
|
1595
|
+
if self.__is_cdp_swap_needed():
|
1596
|
+
self.cdp.find_element(link_text).click()
|
1597
|
+
return
|
1595
1598
|
self.__skip_if_esc()
|
1596
1599
|
if not timeout:
|
1597
1600
|
timeout = settings.SMALL_TIMEOUT
|
@@ -2197,6 +2200,9 @@ class BaseCase(unittest.TestCase):
|
|
2197
2200
|
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
|
2198
2201
|
timeout = self.__get_new_timeout(timeout)
|
2199
2202
|
selector, by = self.__recalculate_selector(selector, by)
|
2203
|
+
if self.__is_cdp_swap_needed():
|
2204
|
+
self.cdp.click_visible_elements(selector)
|
2205
|
+
return
|
2200
2206
|
self.wait_for_ready_state_complete()
|
2201
2207
|
if self.__needs_minimum_wait():
|
2202
2208
|
time.sleep(0.12)
|
@@ -2637,6 +2643,9 @@ class BaseCase(unittest.TestCase):
|
|
2637
2643
|
original_selector = selector
|
2638
2644
|
original_by = by
|
2639
2645
|
selector, by = self.__recalculate_selector(selector, by)
|
2646
|
+
if self.__is_cdp_swap_needed():
|
2647
|
+
self.cdp.gui_hover_element(selector)
|
2648
|
+
return
|
2640
2649
|
self.wait_for_element_visible(
|
2641
2650
|
original_selector, by=original_by, timeout=timeout
|
2642
2651
|
)
|
@@ -2679,6 +2688,9 @@ class BaseCase(unittest.TestCase):
|
|
2679
2688
|
click_selector, click_by = self.__recalculate_selector(
|
2680
2689
|
click_selector, click_by
|
2681
2690
|
)
|
2691
|
+
if self.__is_cdp_swap_needed():
|
2692
|
+
self.cdp.gui_hover_and_click(hover_selector, click_selector)
|
2693
|
+
return
|
2682
2694
|
dropdown_element = self.wait_for_element_visible(
|
2683
2695
|
original_selector, by=original_by, timeout=timeout
|
2684
2696
|
)
|
@@ -3105,6 +3117,9 @@ class BaseCase(unittest.TestCase):
|
|
3105
3117
|
timeout = settings.SMALL_TIMEOUT
|
3106
3118
|
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
|
3107
3119
|
timeout = self.__get_new_timeout(timeout)
|
3120
|
+
if self.__is_cdp_swap_needed():
|
3121
|
+
self.cdp.select_option_by_text(dropdown_selector, option)
|
3122
|
+
return
|
3108
3123
|
self.__select_option(
|
3109
3124
|
dropdown_selector,
|
3110
3125
|
option,
|
@@ -3419,8 +3434,8 @@ class BaseCase(unittest.TestCase):
|
|
3419
3434
|
if self.__is_cdp_swap_needed():
|
3420
3435
|
return self.cdp.get_gui_element_center(selector)
|
3421
3436
|
element_rect = self.get_gui_element_rect(selector, by=by)
|
3422
|
-
x =
|
3423
|
-
y =
|
3437
|
+
x = element_rect["x"] + (element_rect["width"] / 2.0) + 0.5
|
3438
|
+
y = element_rect["y"] + (element_rect["height"] / 2.0) + 0.5
|
3424
3439
|
return (x, y)
|
3425
3440
|
|
3426
3441
|
def get_window_rect(self):
|
@@ -5959,6 +5974,9 @@ class BaseCase(unittest.TestCase):
|
|
5959
5974
|
scroll - the option to scroll to the element first (Default: True)
|
5960
5975
|
timeout - the time to wait for the element to appear """
|
5961
5976
|
self.__check_scope()
|
5977
|
+
if self.__is_cdp_swap_needed():
|
5978
|
+
self.cdp.highlight(selector)
|
5979
|
+
return
|
5962
5980
|
self._check_browser()
|
5963
5981
|
self.__skip_if_esc()
|
5964
5982
|
if isinstance(selector, WebElement):
|
@@ -7676,6 +7694,9 @@ class BaseCase(unittest.TestCase):
|
|
7676
7694
|
but then the title switches over to the actual page title.
|
7677
7695
|
In Recorder Mode, this assertion is skipped because the Recorder
|
7678
7696
|
changes the page title to the selector of the hovered element."""
|
7697
|
+
if self.__is_cdp_swap_needed():
|
7698
|
+
self.cdp.assert_title(title)
|
7699
|
+
return
|
7679
7700
|
self.wait_for_ready_state_complete()
|
7680
7701
|
expected = title.strip()
|
7681
7702
|
actual = self.get_page_title().strip()
|
@@ -8166,7 +8187,7 @@ class BaseCase(unittest.TestCase):
|
|
8166
8187
|
timeout = self.__get_new_timeout(timeout)
|
8167
8188
|
selector, by = self.__recalculate_selector(selector, by, xp_ok=False)
|
8168
8189
|
if self.__is_cdp_swap_needed():
|
8169
|
-
self.cdp.
|
8190
|
+
self.cdp.set_value(selector, text)
|
8170
8191
|
return
|
8171
8192
|
self.wait_for_ready_state_complete()
|
8172
8193
|
self.wait_for_element_present(selector, by=by, timeout=timeout)
|
@@ -8932,6 +8953,9 @@ class BaseCase(unittest.TestCase):
|
|
8932
8953
|
timeout = self.__get_new_timeout(timeout)
|
8933
8954
|
original_selector = selector
|
8934
8955
|
selector, by = self.__recalculate_selector(selector, by)
|
8956
|
+
if self.__is_cdp_swap_needed():
|
8957
|
+
self.cdp.assert_element_absent(selector)
|
8958
|
+
return True
|
8935
8959
|
return page_actions.wait_for_element_absent(
|
8936
8960
|
self.driver,
|
8937
8961
|
selector,
|
@@ -9966,6 +9990,9 @@ class BaseCase(unittest.TestCase):
|
|
9966
9990
|
timeout = settings.SMALL_TIMEOUT
|
9967
9991
|
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
|
9968
9992
|
timeout = self.__get_new_timeout(timeout)
|
9993
|
+
if self.__is_cdp_swap_needed():
|
9994
|
+
self.cdp.find_element(link_text)
|
9995
|
+
return
|
9969
9996
|
self.wait_for_link_text_visible(link_text, timeout=timeout)
|
9970
9997
|
if self.demo_mode:
|
9971
9998
|
a_t = "ASSERT LINK TEXT"
|
@@ -10065,6 +10092,9 @@ class BaseCase(unittest.TestCase):
|
|
10065
10092
|
timeout = settings.SMALL_TIMEOUT
|
10066
10093
|
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
|
10067
10094
|
timeout = self.__get_new_timeout(timeout)
|
10095
|
+
if self.__is_cdp_swap_needed():
|
10096
|
+
self.cdp.assert_element_absent(selector)
|
10097
|
+
return True
|
10068
10098
|
self.wait_for_element_absent(selector, by=by, timeout=timeout)
|
10069
10099
|
return True
|
10070
10100
|
|
@@ -10083,6 +10113,9 @@ class BaseCase(unittest.TestCase):
|
|
10083
10113
|
timeout = self.__get_new_timeout(timeout)
|
10084
10114
|
original_selector = selector
|
10085
10115
|
selector, by = self.__recalculate_selector(selector, by)
|
10116
|
+
if self.__is_cdp_swap_needed():
|
10117
|
+
self.cdp.assert_element_not_visible(selector)
|
10118
|
+
return True
|
10086
10119
|
return page_actions.wait_for_element_not_visible(
|
10087
10120
|
self.driver,
|
10088
10121
|
selector,
|
@@ -93,9 +93,9 @@ def is_cdp_swap_needed(driver):
|
|
93
93
|
For other webdriver methods, SeleniumBase will reconnect first.
|
94
94
|
"""
|
95
95
|
return (
|
96
|
-
driver
|
97
|
-
|
98
|
-
|
96
|
+
hasattr(driver, "is_cdp_mode_active")
|
97
|
+
and driver.is_cdp_mode_active()
|
98
|
+
and hasattr(driver, "is_connected")
|
99
99
|
and not driver.is_connected()
|
100
100
|
)
|
101
101
|
|
@@ -5,6 +5,7 @@ import logging
|
|
5
5
|
import pathlib
|
6
6
|
import secrets
|
7
7
|
import typing
|
8
|
+
from contextlib import suppress
|
8
9
|
from . import cdp_util as util
|
9
10
|
from ._contradict import ContraDict
|
10
11
|
from .config import PathLike
|
@@ -768,6 +769,11 @@ class Element:
|
|
768
769
|
Gets the text contents of this element and child nodes, concatenated.
|
769
770
|
Note: This includes text in the form of script content, (text nodes).
|
770
771
|
"""
|
772
|
+
with suppress(Exception):
|
773
|
+
if self.node.node_name.lower() in ["input", "textarea"]:
|
774
|
+
input_node = self.node.shadow_roots[0].children[0].children[0]
|
775
|
+
if input_node:
|
776
|
+
return input_node.node_value
|
771
777
|
text_nodes = util.filter_recurse_all(
|
772
778
|
self.node, lambda n: n.node_type == 3
|
773
779
|
)
|
@@ -776,6 +782,11 @@ class Element:
|
|
776
782
|
@property
|
777
783
|
def text_all(self):
|
778
784
|
"""Same as text(). Kept for backwards compatibility."""
|
785
|
+
with suppress(Exception):
|
786
|
+
if self.node.node_name.lower() in ["input", "textarea"]:
|
787
|
+
input_node = self.node.shadow_roots[0].children[0].children[0]
|
788
|
+
if input_node:
|
789
|
+
return input_node.node_value
|
779
790
|
text_nodes = util.filter_recurse_all(
|
780
791
|
self.node, lambda n: n.node_type == 3
|
781
792
|
)
|
@@ -868,7 +879,11 @@ class Element:
|
|
868
879
|
path.write_bytes(data_bytes)
|
869
880
|
return str(path)
|
870
881
|
|
871
|
-
async def flash_async(
|
882
|
+
async def flash_async(
|
883
|
+
self,
|
884
|
+
duration: typing.Union[float, int] = 0.5,
|
885
|
+
color: typing.Optional[str] = "EE4488",
|
886
|
+
):
|
872
887
|
"""
|
873
888
|
Displays for a short time a red dot on the element.
|
874
889
|
(Only if the element itself is visible)
|
@@ -892,11 +907,12 @@ class Element:
|
|
892
907
|
style = (
|
893
908
|
"position:absolute;z-index:99999999;padding:0;margin:0;"
|
894
909
|
"left:{:.1f}px; top: {:.1f}px; opacity:0.7;"
|
895
|
-
"width:8px;height:8px;border-radius:50%;background:#
|
910
|
+
"width:8px;height:8px;border-radius:50%;background:#{};"
|
896
911
|
"animation:show-pointer-ani {:.2f}s ease 1;"
|
897
912
|
).format(
|
898
913
|
pos.center[0] - 4, # -4 to account for drawn circle itself (w,h)
|
899
914
|
pos.center[1] - 4,
|
915
|
+
color,
|
900
916
|
duration,
|
901
917
|
)
|
902
918
|
script = (
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: seleniumbase
|
3
|
-
Version: 4.32.
|
3
|
+
Version: 4.32.10a1
|
4
4
|
Summary: A complete web automation framework for end-to-end testing.
|
5
5
|
Home-page: https://github.com/seleniumbase/SeleniumBase
|
6
6
|
Author: Michael Mintz
|
@@ -67,7 +67,7 @@ Requires-Dist: certifi>=2024.8.30
|
|
67
67
|
Requires-Dist: exceptiongroup>=1.2.2
|
68
68
|
Requires-Dist: filelock>=3.16.1
|
69
69
|
Requires-Dist: fasteners>=0.19
|
70
|
-
Requires-Dist: mycdp>=1.0
|
70
|
+
Requires-Dist: mycdp>=1.1.0
|
71
71
|
Requires-Dist: pynose>=1.5.3
|
72
72
|
Requires-Dist: platformdirs>=4.3.6
|
73
73
|
Requires-Dist: typing-extensions>=4.12.2
|
@@ -117,9 +117,9 @@ Requires-Dist: pyreadline3>=3.5.3; platform_system == "Windows"
|
|
117
117
|
Requires-Dist: setuptools~=70.2; python_version < "3.10"
|
118
118
|
Requires-Dist: urllib3<2,>=1.26.20; python_version < "3.10"
|
119
119
|
Requires-Dist: websockets~=13.1; python_version < "3.9"
|
120
|
-
Requires-Dist: setuptools>=
|
120
|
+
Requires-Dist: setuptools>=75.5.0; python_version >= "3.10"
|
121
121
|
Requires-Dist: urllib3<2.3.0,>=1.26.20; python_version >= "3.10"
|
122
|
-
Requires-Dist: websockets>=14.
|
122
|
+
Requires-Dist: websockets>=14.1; python_version >= "3.9"
|
123
123
|
Provides-Extra: allure
|
124
124
|
Requires-Dist: allure-pytest>=2.13.5; extra == "allure"
|
125
125
|
Requires-Dist: allure-python-commons>=2.13.5; extra == "allure"
|
@@ -3,7 +3,7 @@ sbase/__main__.py,sha256=G0bVB1-DM4PGwQ1KyOupaWCs4ePbChZNNWuX2htim5U,647
|
|
3
3
|
sbase/steps.py,sha256=bKT_u5bJkKzYWEuAXi9NVVRYYxQRCM1_YJUrNFFRVPY,42865
|
4
4
|
seleniumbase/__init__.py,sha256=OtJh8nGKL4xtZpw8KPqmn7Q6R-86t4cWUDyVF5MbMTo,2398
|
5
5
|
seleniumbase/__main__.py,sha256=dn1p6dgCchmcH1zzTzzQvFwwdQQqnTGH6ULV9m4hv24,654
|
6
|
-
seleniumbase/__version__.py,sha256=
|
6
|
+
seleniumbase/__version__.py,sha256=mB07opo9QE8Mwmi2PHdixrHjmflPaAc2Gc-yYAxT6ps,49
|
7
7
|
seleniumbase/behave/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
8
|
seleniumbase/behave/behave_helper.py,sha256=elkl8P9eLulRAioLstE9baYNM9N_PHBmAOcajX-pH_Y,24198
|
9
9
|
seleniumbase/behave/behave_sb.py,sha256=-hza7Nx2U41mSObYiPMi48v3JlPh3sJO3yzP0kqZ1Gk,59174
|
@@ -36,7 +36,7 @@ seleniumbase/console_scripts/sb_print.py,sha256=tNy-bMDgwHJO3bZxMpmo9weSE8uhbH0C
|
|
36
36
|
seleniumbase/console_scripts/sb_recorder.py,sha256=1oAA4wFzVboNhIFDwJLD3jgy9RuoavywKQG7R67bNZE,10908
|
37
37
|
seleniumbase/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
38
38
|
seleniumbase/core/application_manager.py,sha256=e_0sjtI8cjY5BNyZj1QBR0j6_oCScxGmSXYEpcYwuZE,576
|
39
|
-
seleniumbase/core/browser_launcher.py,sha256=
|
39
|
+
seleniumbase/core/browser_launcher.py,sha256=WjW--f7w0hUc8Y86tsUen4s2-bh8aXlhd3ad2G2Ws1Q,219040
|
40
40
|
seleniumbase/core/capabilities_parser.py,sha256=meIS2uHapTCq2ldfNAToC7r0cKmZDRXuYNKExM1GHDY,6038
|
41
41
|
seleniumbase/core/colored_traceback.py,sha256=DrRWfg7XEnKcgY59Xj7Jdk09H-XqHYBSUpB-DiZt6iY,2020
|
42
42
|
seleniumbase/core/create_db_tables.sql,sha256=VWPtrdiW_HQ6yETHjqTu-VIrTwvd8I8o1NfBeaVSHpU,972
|
@@ -50,8 +50,8 @@ seleniumbase/core/proxy_helper.py,sha256=cXhu8ErK9Vjdm82RMaQj7hEq_yUWizSp6LyiD50
|
|
50
50
|
seleniumbase/core/recorder_helper.py,sha256=fNGjbapXmEsht54x1o6Igk198QdnPxDDnjUOzQxNhNQ,25055
|
51
51
|
seleniumbase/core/report_helper.py,sha256=AIl6Qava2yW1uSzbLpJBlPlYDz0KE-rVhogh8hsGWBo,12201
|
52
52
|
seleniumbase/core/s3_manager.py,sha256=bkeI8I4y19ebWuQG1oEZV5qJbotC6eN8vin31OCNWJk,3521
|
53
|
-
seleniumbase/core/sb_cdp.py,sha256=
|
54
|
-
seleniumbase/core/sb_driver.py,sha256
|
53
|
+
seleniumbase/core/sb_cdp.py,sha256=IwK8KeDdO1vCwzqocbok7hWDFY6qOf-aB_tL_WMVMSE,59423
|
54
|
+
seleniumbase/core/sb_driver.py,sha256=Yd9BpjnOUuZqYRGmsERVKc1W-e8qwTt7SAytLatkXPA,12070
|
55
55
|
seleniumbase/core/session_helper.py,sha256=s9zD3PVZEWVzG2h81cCUskbNWLfdjC_LwwQjKptHCak,558
|
56
56
|
seleniumbase/core/settings_parser.py,sha256=KokVXpCiGZhJ-D4Bo-hizPz5r-iefzWoiTANu9zNaq4,7504
|
57
57
|
seleniumbase/core/style_sheet.py,sha256=tPpJ1xl6Kuw425Z-3Y0OgVRLGXk_ciBDREZjXk_NuPE,11395
|
@@ -65,14 +65,14 @@ seleniumbase/extensions/disable_csp.zip,sha256=YMifIIgEBiLrEFrS1sfW4Exh4br1V4oK1
|
|
65
65
|
seleniumbase/extensions/recorder.zip,sha256=OOyzF-Ize2cSRu1CqhzSAq5vusI9hqLLd2OIApUHesI,11918
|
66
66
|
seleniumbase/extensions/sbase_ext.zip,sha256=3s1N8zrVaMz8RQEOIoBzC3KDjtmHwVZRvVsX25Odr_s,8175
|
67
67
|
seleniumbase/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
68
|
-
seleniumbase/fixtures/base_case.py,sha256=
|
68
|
+
seleniumbase/fixtures/base_case.py,sha256=vzgC_qp8nT1-6i7UMLB8KMiqrAyJuvw61oQJDLk0EdU,712816
|
69
69
|
seleniumbase/fixtures/constants.py,sha256=XYYMpB-ZDI756LvfhSK9RxdqQ_qO9fJVXgBqBYlQNkk,13609
|
70
70
|
seleniumbase/fixtures/css_to_xpath.py,sha256=9ouDB1xl4MJ2os6JOgTIAyHKOQfuxtxvXC3O5hSnEKA,1954
|
71
71
|
seleniumbase/fixtures/errors.py,sha256=KyxuEVx_e3MPhVrJfNIa_3ltMpbCFxfy_jxK8RFNTns,555
|
72
72
|
seleniumbase/fixtures/js_utils.py,sha256=5o4CTLcCyd717lJ_atOYcC6kPRiZFx-LJIlixRrP_cE,51061
|
73
73
|
seleniumbase/fixtures/page_actions.py,sha256=fOCb2NB2PpEaE8gpAVu-73VjwLzfwP1R9HsRkix_z6s,66634
|
74
74
|
seleniumbase/fixtures/page_utils.py,sha256=5m7iXpikLs80TJoRO6_gEfXE1AKeQgcH1aFbR8o1C9A,12034
|
75
|
-
seleniumbase/fixtures/shared_utils.py,sha256=
|
75
|
+
seleniumbase/fixtures/shared_utils.py,sha256=MwCrt58YAv_XrfNfYjo_quUTWfYArlnf-aexSZGIyRs,7271
|
76
76
|
seleniumbase/fixtures/unittest_helper.py,sha256=sfZ92rZeBAn_sF_yQ3I6_I7h3lyU5-cV_UMegBNoEm8,1294
|
77
77
|
seleniumbase/fixtures/words.py,sha256=FOA4mAYvl3EPVpBTvgvK6YwCL8BdlRCmed685kEe7Vg,7827
|
78
78
|
seleniumbase/fixtures/xpath_to_css.py,sha256=lML56k656fElXJ4NJF07r35FjctrbgQkXUotNk7A-as,8876
|
@@ -119,7 +119,7 @@ seleniumbase/undetected/cdp_driver/browser.py,sha256=mGmpWuR206yYJoBPTNslru8CbEp
|
|
119
119
|
seleniumbase/undetected/cdp_driver/cdp_util.py,sha256=Erfb62fzpvGr0QIIJOiqvGkyoyBakHOIwgbrQ7dqUgM,16625
|
120
120
|
seleniumbase/undetected/cdp_driver/config.py,sha256=Rjvde7V-XJ0ihZdTmOmHEVWSuDWm3SprQ3njg8SN3Go,12087
|
121
121
|
seleniumbase/undetected/cdp_driver/connection.py,sha256=sOTUGjbUqKA2hPvDcRCdqw1VQjVGJs7mbgVvzS7ldtE,23360
|
122
|
-
seleniumbase/undetected/cdp_driver/element.py,sha256=
|
122
|
+
seleniumbase/undetected/cdp_driver/element.py,sha256=wEHtuF9oiny7cb4QMMjdtD5Yf1z3WOs2_NHEWcscusM,40289
|
123
123
|
seleniumbase/undetected/cdp_driver/tab.py,sha256=wUNF8GHrbhaUqg57A9ZTYHKhNPjZIcOHwrz3pj1SAg0,50526
|
124
124
|
seleniumbase/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
125
125
|
seleniumbase/utilities/selenium_grid/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -135,9 +135,9 @@ seleniumbase/utilities/selenium_grid/start-grid-hub.bat,sha256=Ftq-GrAKRYH2ssDPr
|
|
135
135
|
seleniumbase/utilities/selenium_grid/start-grid-hub.sh,sha256=KADv0RUHONLL2_I443QFK8PryBpDmKn5Gy0s4o0vDSM,106
|
136
136
|
seleniumbase/utilities/selenium_ide/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
137
137
|
seleniumbase/utilities/selenium_ide/convert_ide.py,sha256=pZFnqEJQEKZPyNFjkLD29s2HPQgCrWW9XJWpCPhWOoM,31691
|
138
|
-
seleniumbase-4.32.
|
139
|
-
seleniumbase-4.32.
|
140
|
-
seleniumbase-4.32.
|
141
|
-
seleniumbase-4.32.
|
142
|
-
seleniumbase-4.32.
|
143
|
-
seleniumbase-4.32.
|
138
|
+
seleniumbase-4.32.10a1.dist-info/LICENSE,sha256=odSYtWibXBnQ1gBg6CnDZ82n8kLF_if5-2nbqnEyD8k,1085
|
139
|
+
seleniumbase-4.32.10a1.dist-info/METADATA,sha256=rXnleRderXsIB3mEybQOL3r-Kes-dpZtM9rCeJ_v_A8,86464
|
140
|
+
seleniumbase-4.32.10a1.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
|
141
|
+
seleniumbase-4.32.10a1.dist-info/entry_points.txt,sha256=CNrh2EKNaHYEhO6pP1RJyVLB99LkDDYX7TnUK8xfjqk,623
|
142
|
+
seleniumbase-4.32.10a1.dist-info/top_level.txt,sha256=4N97aBOQ8ETCnDnokBsWb07lJfTaq3C1ZzYRxvLMxqU,19
|
143
|
+
seleniumbase-4.32.10a1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|