From 24317c15fbe1ea640ff70a938468fbc85223dccd Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 20 Feb 2025 14:50:04 -0500 Subject: [PATCH] Update CDP Mode --- examples/cdp_mode/ReadMe.md | 2 + seleniumbase/core/browser_launcher.py | 21 +++++- seleniumbase/core/sb_cdp.py | 102 ++++++++++++++------------ seleniumbase/fixtures/base_case.py | 19 +++-- 4 files changed, 89 insertions(+), 55 deletions(-) diff --git a/examples/cdp_mode/ReadMe.md b/examples/cdp_mode/ReadMe.md index 97b7edec..31d4bdea 100644 --- a/examples/cdp_mode/ReadMe.md +++ b/examples/cdp_mode/ReadMe.md @@ -470,6 +470,8 @@ sb.cdp.is_exact_text_visible(text, selector="body") sb.cdp.wait_for_text(text, selector="body", timeout=None) sb.cdp.wait_for_text_not_visible(text, selector="body", timeout=None) sb.cdp.wait_for_element_visible(selector, timeout=None) +sb.cdp.wait_for_element_not_visible(selector, timeout=None) +sb.cdp.wait_for_element_absent(selector, timeout=None) sb.cdp.assert_element(selector, timeout=None) sb.cdp.assert_element_visible(selector, timeout=None) sb.cdp.assert_element_present(selector, timeout=None) diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index 4fac3296..79fb720f 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -723,6 +723,8 @@ def uc_open_with_cdp_mode(driver, url=None): cdp.wait_for_text = CDPM.wait_for_text cdp.wait_for_text_not_visible = CDPM.wait_for_text_not_visible cdp.wait_for_element_visible = CDPM.wait_for_element_visible + cdp.wait_for_element_not_visible = CDPM.wait_for_element_not_visible + cdp.wait_for_element_absent = CDPM.wait_for_element_absent cdp.assert_element = CDPM.assert_element cdp.assert_element_visible = CDPM.assert_element_visible cdp.assert_element_present = CDPM.assert_element_present @@ -1628,9 +1630,19 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None): ): driver.uc_open_with_disconnect(driver.current_url, 3.8) with suppress(Exception): + if "--debug" in sys.argv: + if sb_config._saved_cf_tab_count == 1: + print(' pyautogui.press("\\t")') + else: + print( + ' pyautogui.press("\\t") * %s' + % sb_config._saved_cf_tab_count + ) for i in range(sb_config._saved_cf_tab_count): pyautogui.press("\t") time.sleep(0.027) + if "--debug" in sys.argv: + print(' pyautogui.press(" ")') pyautogui.press(" ") else: driver.disconnect() @@ -2310,7 +2322,14 @@ def _set_chrome_options( and not enable_3d_apis ): chrome_options.add_argument("--disable-gpu") - if not IS_LINUX and is_using_uc(undetectable, browser_name): + if ( + (not IS_LINUX and is_using_uc(undetectable, browser_name)) + or ( + IS_MAC + and binary_location + and "chrome-headless-shell" in binary_location + ) + ): chrome_options.add_argument("--disable-dev-shm-usage") chrome_options.add_argument("--disable-application-cache") if IS_LINUX: diff --git a/seleniumbase/core/sb_cdp.py b/seleniumbase/core/sb_cdp.py index 2b0003df..d8388022 100644 --- a/seleniumbase/core/sb_cdp.py +++ b/seleniumbase/core/sb_cdp.py @@ -497,7 +497,7 @@ class CDPMethods(): element.send_keys("\r\n") time.sleep(0.044) self.__slow_mode_pause_if_set() - return self.loop.run_until_complete(self.page.wait()) + return self.loop.run_until_complete(self.page.sleep(0.025)) def __query_selector(self, element, selector): selector = self.__convert_to_css_if_xpath(selector) @@ -864,7 +864,7 @@ class CDPMethods(): text = text[:-1] + "\r\n" element.send_keys(text) self.__slow_mode_pause_if_set() - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.sleep(0.025)) def press_keys(self, selector, text, timeout=None): """Similar to send_keys(), but presses keys at human speed.""" @@ -884,7 +884,7 @@ class CDPMethods(): element.send_keys("\r\n") time.sleep(0.044) self.__slow_mode_pause_if_set() - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.sleep(0.025)) def type(self, selector, text, timeout=None): """Similar to send_keys(), but clears the text field first.""" @@ -899,7 +899,7 @@ class CDPMethods(): text = text[:-1] + "\r\n" element.send_keys(text) self.__slow_mode_pause_if_set() - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.sleep(0.025)) def set_value(self, selector, text, timeout=None): """Similar to send_keys(), but clears the text field first.""" @@ -937,7 +937,7 @@ class CDPMethods(): self.__add_light_pause() self.send_keys(selector, "\n") self.__slow_mode_pause_if_set() - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.sleep(0.025)) def evaluate(self, expression): """Run a JavaScript expression and return the result.""" @@ -1377,7 +1377,7 @@ class CDPMethods(): pyautogui.press(key) time.sleep(0.044) self.__slow_mode_pause_if_set() - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.sleep(0.025)) def gui_press_keys(self, keys): self.__install_pyautogui_if_missing() @@ -1392,7 +1392,7 @@ class CDPMethods(): pyautogui.press(key) time.sleep(0.044) self.__slow_mode_pause_if_set() - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.sleep(0.025)) def gui_write(self, text): self.__install_pyautogui_if_missing() @@ -1405,7 +1405,7 @@ class CDPMethods(): self.__make_sure_pyautogui_lock_is_writable() pyautogui.write(text) self.__slow_mode_pause_if_set() - self.loop.run_until_complete(self.page.wait()) + self.loop.run_until_complete(self.page.sleep(0.025)) def __gui_click_x_y(self, x, y, timeframe=0.25, uc_lock=False): self.__install_pyautogui_if_missing() @@ -1820,6 +1820,50 @@ class CDPMethods(): time.sleep(0.1) raise Exception("Element {%s} was not visible!" % selector) + def wait_for_element_not_visible(self, selector, timeout=None): + """Wait for element to not be visible on page. (May still be in DOM)""" + if not timeout: + timeout = settings.SMALL_TIMEOUT + start_ms = time.time() * 1000.0 + stop_ms = start_ms + (timeout * 1000.0) + for i in range(int(timeout * 10)): + if not self.is_element_present(selector): + return True + elif not self.is_element_visible(selector): + return True + now_ms = time.time() * 1000.0 + if now_ms >= stop_ms: + break + time.sleep(0.1) + plural = "s" + if timeout == 1: + plural = "" + raise Exception( + "Element {%s} was still visible after %s second%s!" + % (selector, timeout, plural) + ) + + def wait_for_element_absent(self, selector, timeout=None): + """Wait for element to not be present in the DOM.""" + if not timeout: + timeout = settings.SMALL_TIMEOUT + start_ms = time.time() * 1000.0 + stop_ms = start_ms + (timeout * 1000.0) + for i in range(int(timeout * 10)): + if not self.is_element_present(selector): + return True + now_ms = time.time() * 1000.0 + if now_ms >= stop_ms: + break + time.sleep(0.1) + plural = "s" + if timeout == 1: + plural = "" + raise Exception( + "Element {%s} was still present after %s second%s!" + % (selector, timeout, plural) + ) + def assert_element(self, selector, timeout=None): """Same as assert_element_visible()""" self.assert_element_visible(selector, timeout=timeout) @@ -1851,47 +1895,13 @@ class CDPMethods(): def assert_element_absent(self, selector, timeout=None): """Assert element is not present in the DOM.""" - if not timeout: - timeout = settings.SMALL_TIMEOUT - start_ms = time.time() * 1000.0 - stop_ms = start_ms + (timeout * 1000.0) - for i in range(int(timeout * 10)): - if not self.is_element_present(selector): - return True - now_ms = time.time() * 1000.0 - if now_ms >= stop_ms: - break - time.sleep(0.1) - plural = "s" - if timeout == 1: - plural = "" - raise Exception( - "Element {%s} was still present after %s second%s!" - % (selector, timeout, plural) - ) + self.wait_for_element_absent(selector, timeout=timeout) + return True def assert_element_not_visible(self, selector, timeout=None): """Assert element is not visible on page. (May still be in DOM)""" - if not timeout: - timeout = settings.SMALL_TIMEOUT - start_ms = time.time() * 1000.0 - stop_ms = start_ms + (timeout * 1000.0) - for i in range(int(timeout * 10)): - if not self.is_element_present(selector): - return True - elif not self.is_element_visible(selector): - return True - now_ms = time.time() * 1000.0 - if now_ms >= stop_ms: - break - time.sleep(0.1) - plural = "s" - if timeout == 1: - plural = "" - raise Exception( - "Element {%s} was still visible after %s second%s!" - % (selector, timeout, plural) - ) + self.wait_for_element_not_visible(selector, timeout=timeout) + return True def assert_element_attribute(self, selector, attribute, value=None): attributes = self.get_element_attributes(selector) diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index 8c6c57d0..5a1dd8c5 100644 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -9124,7 +9124,7 @@ class BaseCase(unittest.TestCase): original_selector = selector selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): - self.cdp.assert_element_absent(selector) + self.cdp.wait_for_element_absent(selector, timeout=timeout) return True return page_actions.wait_for_element_absent( self.driver, @@ -9585,7 +9585,7 @@ class BaseCase(unittest.TestCase): self.assert_elements_present(selector, by=by, timeout=timeout) return True if self.__is_cdp_swap_needed(): - self.cdp.assert_element_present(selector) + self.cdp.assert_element_present(selector, timeout=timeout) return True if self.__is_shadow_selector(selector): self.__assert_shadow_element_present(selector) @@ -9662,7 +9662,7 @@ class BaseCase(unittest.TestCase): if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) if self.__is_cdp_swap_needed(): - self.cdp.assert_element(selector) + self.cdp.assert_element(selector, timeout=timeout) return True if isinstance(selector, list): self.assert_elements(selector, by=by, timeout=timeout) @@ -9955,7 +9955,7 @@ class BaseCase(unittest.TestCase): messenger_post, selector, by ) elif self.__is_cdp_swap_needed(): - self.cdp.assert_text(text, selector) + self.cdp.assert_text(text, selector, timeout=timeout) return True elif not self.is_connected(): self.connect() @@ -10005,7 +10005,7 @@ class BaseCase(unittest.TestCase): original_selector = selector selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): - self.cdp.assert_exact_text(text, selector) + self.cdp.assert_exact_text(text, selector, timeout=timeout) return True if self.__is_shadow_selector(selector): self.__assert_exact_shadow_text_visible(text, selector, timeout) @@ -10245,6 +10245,9 @@ class BaseCase(unittest.TestCase): timeout = self.__get_new_timeout(timeout) original_selector = selector selector, by = self.__recalculate_selector(selector, by) + if self.__is_cdp_swap_needed(): + self.cdp.wait_for_element_absent(selector, timeout=timeout) + return True return page_actions.wait_for_element_absent( self.driver, selector, @@ -10267,7 +10270,7 @@ class BaseCase(unittest.TestCase): if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) if self.__is_cdp_swap_needed(): - self.cdp.assert_element_absent(selector) + self.cdp.assert_element_absent(selector, timeout=timeout) return True self.wait_for_element_absent(selector, by=by, timeout=timeout) return True @@ -10288,7 +10291,7 @@ class BaseCase(unittest.TestCase): original_selector = selector selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): - self.cdp.assert_element_not_visible(selector) + self.cdp.wait_for_element_not_visible(selector, timeout=timeout) return True return page_actions.wait_for_element_not_visible( self.driver, @@ -10310,7 +10313,7 @@ class BaseCase(unittest.TestCase): if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) if self.__is_cdp_swap_needed(): - self.cdp.assert_element_not_visible(selector) + self.cdp.assert_element_not_visible(selector, timeout=timeout) return True self.wait_for_element_not_visible(selector, by=by, timeout=timeout) if self.recorder_mode and self.__current_url_is_recordable():