From cd731339f5014a578550a73d0f25bbe03f64dd2a Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 7 Nov 2024 22:20:45 -0500 Subject: [PATCH 1/5] Update CDP Mode --- seleniumbase/core/browser_launcher.py | 6 +++++ seleniumbase/core/sb_cdp.py | 35 ++++++++++++++++++++++++--- seleniumbase/fixtures/base_case.py | 19 +++++++++++---- 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index 14e34da36ec..1f4897c780f 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -625,6 +625,7 @@ def uc_open_with_cdp_mode(driver, url=None): cdp.internalize_links = CDPM.internalize_links cdp.get_window = CDPM.get_window cdp.get_element_attributes = CDPM.get_element_attributes + cdp.get_element_attribute = CDPM.get_element_attribute cdp.get_element_html = CDPM.get_element_html cdp.get_element_rect = CDPM.get_element_rect cdp.get_element_size = CDPM.get_element_size @@ -651,6 +652,11 @@ def uc_open_with_cdp_mode(driver, url=None): cdp.focus = CDPM.focus cdp.highlight_overlay = CDPM.highlight_overlay cdp.get_window_position = CDPM.get_window_position + cdp.check_if_unchecked = CDPM.check_if_unchecked + cdp.uncheck_if_checked = CDPM.uncheck_if_checked + cdp.select_if_unselected = CDPM.select_if_unselected + cdp.unselect_if_selected = CDPM.unselect_if_selected + cdp.is_checked = CDPM.is_checked cdp.is_element_present = CDPM.is_element_present cdp.is_element_visible = CDPM.is_element_visible cdp.assert_element_present = CDPM.assert_element_present diff --git a/seleniumbase/core/sb_cdp.py b/seleniumbase/core/sb_cdp.py index f920764d9f7..b8afadd113b 100644 --- a/seleniumbase/core/sb_cdp.py +++ b/seleniumbase/core/sb_cdp.py @@ -1,6 +1,5 @@ """Add CDP methods to extend the driver""" import fasteners -import math import os import re import sys @@ -771,10 +770,12 @@ def get_gui_element_rect(self, selector): window_rect = self.get_window_rect() w_bottom_y = window_rect["y"] + window_rect["height"] viewport_height = window_rect["innerHeight"] - x = math.ceil(window_rect["x"] + element_rect["x"]) - y = math.ceil(w_bottom_y - viewport_height + element_rect["y"]) + x = window_rect["x"] + element_rect["x"] + y = w_bottom_y - viewport_height + element_rect["y"] y_scroll_offset = window_rect["pageYOffset"] - y = int(y - y_scroll_offset) + y = y - y_scroll_offset + x = x + window_rect["scrollX"] + y = y + window_rect["scrollY"] return ({"height": e_height, "width": e_width, "x": x, "y": y}) def get_gui_element_center(self, selector): @@ -804,6 +805,10 @@ def get_element_attributes(self, selector): ) ) + def get_element_attribute(self, selector, attribute): + attributes = self.get_element_attributes(selector) + return attributes[attribute] + def get_element_html(self, selector): selector = self.__convert_to_css_if_xpath(selector) return self.loop.run_until_complete( @@ -1033,6 +1038,28 @@ def internalize_links(self): This prevents those links from opening in a new tab.""" self.set_attributes('[target="_blank"]', "target", "_self") + def is_checked(self, selector): + """Return True if checkbox (or radio button) is checked.""" + self.find_element(selector, timeout=settings.SMALL_TIMEOUT) + return self.get_element_attribute(selector, "checked") + + def is_selected(self, selector): + return self.is_checked(selector) + + def check_if_unchecked(self, selector): + if not self.is_checked(selector): + self.click(selector) + + def select_if_unselected(self, selector): + self.check_if_unchecked(selector) + + def uncheck_if_checked(self, selector): + if self.is_checked(selector): + self.click(selector) + + def unselect_if_selected(self, selector): + self.uncheck_if_checked(selector) + def is_element_present(self, selector): try: self.select(selector, timeout=0.01) diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index c1f3fb801fc..9685ebb3b90 100644 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -1884,6 +1884,8 @@ def get_attribute( if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT: timeout = self.__get_new_timeout(timeout) selector, by = self.__recalculate_selector(selector, by) + if self.__is_cdp_swap_needed(): + return self.cdp.get_element_attribute(selector) self.wait_for_ready_state_complete() time.sleep(0.01) if self.__is_shadow_selector(selector): @@ -2460,16 +2462,14 @@ def is_checked(self, selector, by="css selector", timeout=None): if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) selector, by = self.__recalculate_selector(selector, by) + if self.__is_cdp_swap_needed(): + return self.cdp.is_checked(selector) kind = self.get_attribute(selector, "type", by=by, timeout=timeout) if kind != "checkbox" and kind != "radio": raise Exception("Expecting a checkbox or a radio button element!") - is_checked = self.get_attribute( + return self.get_attribute( selector, "checked", by=by, timeout=timeout, hard_fail=False ) - if is_checked: - return True - else: # (NoneType) - return False def is_selected(self, selector, by="css selector", timeout=None): """Same as is_checked()""" @@ -2479,6 +2479,9 @@ def check_if_unchecked(self, selector, by="css selector"): """If a checkbox or radio button is not checked, will check it.""" self.__check_scope() selector, by = self.__recalculate_selector(selector, by) + if self.__is_cdp_swap_needed(): + self.cdp.check_if_unchecked(selector) + return if not self.is_checked(selector, by=by): if self.is_element_visible(selector, by=by): self.click(selector, by=by) @@ -2515,6 +2518,9 @@ def uncheck_if_checked(self, selector, by="css selector"): """If a checkbox is checked, will uncheck it.""" self.__check_scope() selector, by = self.__recalculate_selector(selector, by) + if self.__is_cdp_swap_needed(): + self.cdp.uncheck_if_checked(selector) + return if self.is_checked(selector, by=by): if self.is_element_visible(selector, by=by): self.click(selector, by=by) @@ -6087,6 +6093,9 @@ def scroll_to(self, selector, by="css selector", timeout=None): timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) + if self.__is_cdp_swap_needed(): + self.cdp.scroll_into_view(selector) + return if self.demo_mode or self.slow_mode: self.slow_scroll_to(selector, by=by, timeout=timeout) return From fd394c7d6bfa17dbed098317f4d8178ab95a50a4 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 7 Nov 2024 22:21:51 -0500 Subject: [PATCH 2/5] Add new CDP Mode examples --- examples/cdp_mode/raw_planetmc.py | 8 ++++++++ examples/cdp_mode/raw_wsform.py | 8 ++++++++ 2 files changed, 16 insertions(+) create mode 100644 examples/cdp_mode/raw_planetmc.py create mode 100644 examples/cdp_mode/raw_wsform.py diff --git a/examples/cdp_mode/raw_planetmc.py b/examples/cdp_mode/raw_planetmc.py new file mode 100644 index 00000000000..ec9637de017 --- /dev/null +++ b/examples/cdp_mode/raw_planetmc.py @@ -0,0 +1,8 @@ +from seleniumbase import SB + +with SB(uc=True, test=True, locale_code="en") as sb: + url = "www.planetminecraft.com/account/sign_in/" + sb.activate_cdp_mode(url) + sb.sleep(1) + sb.cdp.gui_click_element("#turnstile-widget") + sb.sleep(2) diff --git a/examples/cdp_mode/raw_wsform.py b/examples/cdp_mode/raw_wsform.py new file mode 100644 index 00000000000..8abed99808b --- /dev/null +++ b/examples/cdp_mode/raw_wsform.py @@ -0,0 +1,8 @@ +from seleniumbase import SB + +with SB(uc=True, test=True, locale_code="en") as sb: + url = "https://wsform.com/demo/" + sb.activate_cdp_mode(url) + sb.scroll_into_view("div.grid") + sb.uc_gui_click_captcha() + sb.sleep(1) From 849b595cbbe52027bb1713f98b651071063c5569 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 7 Nov 2024 22:22:18 -0500 Subject: [PATCH 3/5] Update the docs --- examples/cdp_mode/ReadMe.md | 7 +++++++ mkdocs_build/requirements.txt | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/examples/cdp_mode/ReadMe.md b/examples/cdp_mode/ReadMe.md index 95127478fe2..dc059a18f57 100644 --- a/examples/cdp_mode/ReadMe.md +++ b/examples/cdp_mode/ReadMe.md @@ -364,12 +364,19 @@ sb.cdp.get_gui_element_center(selector) sb.cdp.get_document() sb.cdp.get_flattened_document() sb.cdp.get_element_attributes(selector) +sb.cdp.get_element_attribute(selector, attribute) sb.cdp.get_element_html(selector) sb.cdp.set_locale(locale) sb.cdp.set_attributes(selector, attribute, value) sb.cdp.gui_click_x_y(x, y) sb.cdp.gui_click_element(selector) sb.cdp.internalize_links() +sb.cdp.is_checked(selector) +sb.cdp.is_selected(selector) +sb.cdp.check_if_unchecked(selector) +sb.cdp.select_if_unselected(selector) +sb.cdp.uncheck_if_checked(selector) +sb.cdp.unselect_if_selected(selector) sb.cdp.is_element_present(selector) sb.cdp.is_element_visible(selector) sb.cdp.assert_element(selector) diff --git a/mkdocs_build/requirements.txt b/mkdocs_build/requirements.txt index d7f531cbb31..c92906028a9 100644 --- a/mkdocs_build/requirements.txt +++ b/mkdocs_build/requirements.txt @@ -1,7 +1,7 @@ # mkdocs dependencies for generating the seleniumbase.io website # Minimum Python version: 3.9 (for generating docs only) -regex>=2024.9.11 +regex>=2024.11.6 pymdown-extensions>=10.12 pipdeptree>=2.23.4 python-dateutil>=2.8.2 From a8b640ebb12c54d4ec2e170d0e8e0972751c5d20 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 7 Nov 2024 22:22:36 -0500 Subject: [PATCH 4/5] Refresh Python dependencies --- requirements.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 6773ca43f29..572d07dae98 100755 --- a/requirements.txt +++ b/requirements.txt @@ -22,7 +22,7 @@ pyyaml>=6.0.2 pygments>=2.18.0 pyreadline3>=3.5.3;platform_system=="Windows" tabcompleter>=1.4.0 -pdbp>=1.6.0 +pdbp>=1.6.1 idna==3.10 chardet==5.2.0 charset-normalizer==3.4.0 diff --git a/setup.py b/setup.py index 111de78a14e..04ecc51fa0a 100755 --- a/setup.py +++ b/setup.py @@ -171,7 +171,7 @@ 'pygments>=2.18.0', 'pyreadline3>=3.5.3;platform_system=="Windows"', "tabcompleter>=1.4.0", - "pdbp>=1.6.0", + "pdbp>=1.6.1", "idna==3.10", 'chardet==5.2.0', 'charset-normalizer==3.4.0', From 4f86de90b5950fbfe5eddf884e5c8bbf1e3e4882 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 7 Nov 2024 22:22:48 -0500 Subject: [PATCH 5/5] Version 4.32.8 --- seleniumbase/__version__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index bdf522609f0..ebeb01572dd 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "4.32.7" +__version__ = "4.32.8"