From 914027e71a4f3c29e0f9e6fbb633c30e4b6bcda5 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Wed, 25 Dec 2024 00:28:16 -0500 Subject: [PATCH] Update CDP Mode --- seleniumbase/core/log_helper.py | 17 +++- seleniumbase/core/sb_cdp.py | 128 +++++++++++++++++--------- seleniumbase/fixtures/base_case.py | 17 +++- seleniumbase/fixtures/page_actions.py | 9 +- 4 files changed, 121 insertions(+), 50 deletions(-) diff --git a/seleniumbase/core/log_helper.py b/seleniumbase/core/log_helper.py index ceb9c531a29..bd1ebe25be9 100644 --- a/seleniumbase/core/log_helper.py +++ b/seleniumbase/core/log_helper.py @@ -15,6 +15,11 @@ py311_patch2 = constants.PatchPy311.PATCH +def __is_cdp_swap_needed(driver): + """If the driver is disconnected, use a CDP method when available.""" + return shared_utils.is_cdp_swap_needed(driver) + + def log_screenshot(test_logpath, driver, screenshot=None, get=False): screenshot_name = settings.SCREENSHOT_NAME screenshot_path = os.path.join(test_logpath, screenshot_name) @@ -356,7 +361,11 @@ def log_page_source(test_logpath, driver, source=None): page_source = source else: try: - page_source = driver.page_source + page_source = None + if __is_cdp_swap_needed(driver): + page_source = driver.cdp.get_page_source() + else: + page_source = driver.page_source page_source = get_html_source_with_base_href(driver, page_source) except Exception: source = constants.Warnings.PAGE_SOURCE_UNDEFINED @@ -448,7 +457,11 @@ def get_test_name(test): def get_last_page(driver): try: - last_page = driver.current_url + last_page = None + if __is_cdp_swap_needed(driver): + last_page = driver.cdp.get_current_url() + else: + last_page = driver.current_url except Exception: last_page = "[WARNING! Browser Not Open!]" if len(last_page) < 5: diff --git a/seleniumbase/core/sb_cdp.py b/seleniumbase/core/sb_cdp.py index 7cde2ee2f39..65a36be93e1 100644 --- a/seleniumbase/core/sb_cdp.py +++ b/seleniumbase/core/sb_cdp.py @@ -127,14 +127,14 @@ def get_event_loop(self): def add_handler(self, event, handler): self.page.add_handler(event, handler) - def find_element( - self, selector, best_match=False, timeout=settings.SMALL_TIMEOUT - ): + def find_element(self, selector, best_match=False, timeout=None): """Similar to select(), but also finds elements by text content. When using text-based searches, if best_match=False, then will find the first element with the text. If best_match=True, then if multiple elements have that text, then will use the element with the closest text-length to the text being searched for.""" + if not timeout: + timeout = settings.SMALL_TIMEOUT self.__add_light_pause() selector = self.__convert_to_css_if_xpath(selector) early_failure = False @@ -167,12 +167,12 @@ def find_element( self.__slow_mode_pause_if_set() return element - def find_element_by_text( - self, text, tag_name=None, timeout=settings.SMALL_TIMEOUT - ): + def find_element_by_text(self, text, tag_name=None, timeout=None): """Returns an element by matching text. Optionally, provide a tag_name to narrow down the search to an element with the given tag. (Eg: a, button, div, script, span)""" + if not timeout: + timeout = settings.SMALL_TIMEOUT self.__add_light_pause() time_now = time.time() self.assert_text(text, timeout=timeout) @@ -218,7 +218,9 @@ def find_element_by_text( % (text, tag_name, timeout, plural) ) - def find_all(self, selector, timeout=settings.SMALL_TIMEOUT): + def find_all(self, selector, timeout=None): + if not timeout: + timeout = settings.SMALL_TIMEOUT self.__add_light_pause() selector = self.__convert_to_css_if_xpath(selector) elements = self.loop.run_until_complete( @@ -272,8 +274,10 @@ def find_elements_by_text(self, text, tag_name=None): updated_elements.append(element) return updated_elements - def select(self, selector, timeout=settings.SMALL_TIMEOUT): + def select(self, selector, timeout=None): """Similar to find_element(), but without text-based search.""" + if not timeout: + timeout = settings.SMALL_TIMEOUT self.__add_light_pause() selector = self.__convert_to_css_if_xpath(selector) if (":contains(" in selector): @@ -307,7 +311,9 @@ def select(self, selector, timeout=settings.SMALL_TIMEOUT): self.__slow_mode_pause_if_set() return element - def select_all(self, selector, timeout=settings.SMALL_TIMEOUT): + def select_all(self, selector, timeout=None): + if not timeout: + timeout = settings.SMALL_TIMEOUT self.__add_light_pause() selector = self.__convert_to_css_if_xpath(selector) elements = self.loop.run_until_complete( @@ -319,10 +325,14 @@ def select_all(self, selector, timeout=settings.SMALL_TIMEOUT): updated_elements.append(element) return updated_elements - def find_elements(self, selector, timeout=settings.SMALL_TIMEOUT): + def find_elements(self, selector, timeout=None): + if not timeout: + timeout = settings.SMALL_TIMEOUT return self.select_all(selector, timeout=timeout) - def find_visible_elements(self, selector, timeout=settings.SMALL_TIMEOUT): + def find_visible_elements(self, selector, timeout=None): + if not timeout: + timeout = settings.SMALL_TIMEOUT visible_elements = [] elements = self.select_all(selector, timeout=timeout) for element in elements: @@ -587,12 +597,12 @@ def load_cookies(self, *args, **kwargs): driver.cookies.load(*args, **kwargs) ) - def clear_cookies(self, *args, **kwargs): + def clear_cookies(self): driver = self.driver if hasattr(driver, "cdp_base"): driver = driver.cdp_base return self.loop.run_until_complete( - driver.cookies.clear(*args, **kwargs) + driver.cookies.clear() ) def sleep(self, seconds): @@ -616,7 +626,9 @@ def get_active_element_css(self): self.page.evaluate(js_code) ) - def click(self, selector, timeout=settings.SMALL_TIMEOUT): + def click(self, selector, timeout=None): + if not timeout: + timeout = settings.SMALL_TIMEOUT self.__slow_mode_pause_if_set() element = self.find_element(selector, timeout=timeout) element.scroll_into_view() @@ -672,8 +684,10 @@ def click_visible_elements(self, selector, limit=0): except Exception: break - def mouse_click(self, selector, timeout=settings.SMALL_TIMEOUT): + def mouse_click(self, selector, timeout=None): """(Attempt simulating a mouse click)""" + if not timeout: + timeout = settings.SMALL_TIMEOUT self.__slow_mode_pause_if_set() element = self.find_element(selector, timeout=timeout) element.scroll_into_view() @@ -771,7 +785,9 @@ def remove_elements(self, selector): with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(js_code)) - def send_keys(self, selector, text, timeout=settings.SMALL_TIMEOUT): + def send_keys(self, selector, text, timeout=None): + if not timeout: + timeout = settings.SMALL_TIMEOUT self.__slow_mode_pause_if_set() element = self.select(selector, timeout=timeout) element.scroll_into_view() @@ -781,8 +797,10 @@ def send_keys(self, selector, text, timeout=settings.SMALL_TIMEOUT): self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.wait()) - def press_keys(self, selector, text, timeout=settings.SMALL_TIMEOUT): + def press_keys(self, selector, text, timeout=None): """Similar to send_keys(), but presses keys at human speed.""" + if not timeout: + timeout = settings.SMALL_TIMEOUT self.__slow_mode_pause_if_set() element = self.select(selector, timeout=timeout) element.scroll_into_view() @@ -799,8 +817,10 @@ def press_keys(self, selector, text, timeout=settings.SMALL_TIMEOUT): self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.wait()) - def type(self, selector, text, timeout=settings.SMALL_TIMEOUT): + def type(self, selector, text, timeout=None): """Similar to send_keys(), but clears the text field first.""" + if not timeout: + timeout = settings.SMALL_TIMEOUT self.__slow_mode_pause_if_set() element = self.select(selector, timeout=timeout) element.scroll_into_view() @@ -812,8 +832,10 @@ def type(self, selector, text, timeout=settings.SMALL_TIMEOUT): self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.wait()) - def set_value(self, selector, text, timeout=settings.SMALL_TIMEOUT): + def set_value(self, selector, text, timeout=None): """Similar to send_keys(), but clears the text field first.""" + if not timeout: + timeout = settings.SMALL_TIMEOUT self.__slow_mode_pause_if_set() selector = self.__convert_to_css_if_xpath(selector) element = self.select(selector, timeout=timeout) @@ -1036,7 +1058,9 @@ def get_window_position(self): coordinates["y"] = y if y else 0 return coordinates - def get_element_rect(self, selector, timeout=settings.SMALL_TIMEOUT): + def get_element_rect(self, selector, timeout=None): + if not timeout: + timeout = settings.SMALL_TIMEOUT selector = self.__convert_to_css_if_xpath(selector) self.select(selector, timeout=timeout) self.__add_light_pause() @@ -1049,23 +1073,29 @@ def get_element_rect(self, selector, timeout=settings.SMALL_TIMEOUT): ) return coordinates - def get_element_size(self, selector): - element_rect = self.get_element_rect(selector) + def get_element_size(self, selector, timeout=None): + if not timeout: + timeout = settings.SMALL_TIMEOUT + element_rect = self.get_element_rect(selector, timeout=timeout) coordinates = {} coordinates["width"] = element_rect["width"] coordinates["height"] = element_rect["height"] return coordinates - def get_element_position(self, selector): - element_rect = self.get_element_rect(selector) + def get_element_position(self, selector, timeout=None): + if not timeout: + timeout = settings.SMALL_TIMEOUT + element_rect = self.get_element_rect(selector, timeout=timeout) coordinates = {} coordinates["x"] = element_rect["x"] coordinates["y"] = element_rect["y"] return coordinates - def get_gui_element_rect(self, selector): + def get_gui_element_rect(self, selector, timeout=None): """(Coordinates are relative to the screen. Not the window.)""" - element_rect = self.get_element_rect(selector) + if not timeout: + timeout = settings.SMALL_TIMEOUT + element_rect = self.get_element_rect(selector, timeout=timeout) e_width = element_rect["width"] e_height = element_rect["height"] window_rect = self.get_window_rect() @@ -1079,9 +1109,11 @@ def get_gui_element_rect(self, selector): y = y + window_rect["scrollY"] return ({"height": e_height, "width": e_width, "x": x, "y": y}) - def get_gui_element_center(self, selector): + def get_gui_element_center(self, selector, timeout=None): """(Coordinates are relative to the screen. Not the window.)""" - element_rect = self.get_gui_element_rect(selector) + if not timeout: + timeout = settings.SMALL_TIMEOUT + element_rect = self.get_gui_element_rect(selector, timeout=timeout) e_width = element_rect["width"] e_height = element_rect["height"] e_x = element_rect["x"] @@ -1629,9 +1661,9 @@ def is_element_visible(self, selector): return True return False - def wait_for_element_visible( - self, selector, timeout=settings.SMALL_TIMEOUT - ): + def wait_for_element_visible(self, selector, timeout=None): + if not timeout: + timeout = settings.SMALL_TIMEOUT try: self.select(selector, timeout=timeout) except Exception: @@ -1642,8 +1674,10 @@ def wait_for_element_visible( time.sleep(0.1) raise Exception("Element {%s} was not visible!" % selector) - def assert_element(self, selector, timeout=settings.SMALL_TIMEOUT): + def assert_element(self, selector, timeout=None): """Same as assert_element_visible()""" + if not timeout: + timeout = settings.SMALL_TIMEOUT try: self.select(selector, timeout=timeout) except Exception: @@ -1654,8 +1688,10 @@ def assert_element(self, selector, timeout=settings.SMALL_TIMEOUT): time.sleep(0.1) raise Exception("Element {%s} was not visible!" % selector) - def assert_element_visible(self, selector, timeout=settings.SMALL_TIMEOUT): + def assert_element_visible(self, selector, timeout=None): """Same as assert_element()""" + if not timeout: + timeout = settings.SMALL_TIMEOUT try: self.select(selector, timeout=timeout) except Exception: @@ -1666,16 +1702,20 @@ def assert_element_visible(self, selector, timeout=settings.SMALL_TIMEOUT): time.sleep(0.1) raise Exception("Element {%s} was not visible!" % selector) - def assert_element_present(self, selector, timeout=settings.SMALL_TIMEOUT): + def assert_element_present(self, selector, timeout=None): """Assert element is present in the DOM. (Visibility NOT required)""" + if not timeout: + timeout = settings.SMALL_TIMEOUT try: self.select(selector, timeout=timeout) except Exception: raise Exception("Element {%s} was not found!" % selector) return True - def assert_element_absent(self, selector, timeout=settings.SMALL_TIMEOUT): + 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)): @@ -1693,10 +1733,10 @@ def assert_element_absent(self, selector, timeout=settings.SMALL_TIMEOUT): % (selector, timeout, plural) ) - def assert_element_not_visible( - self, selector, timeout=settings.SMALL_TIMEOUT - ): + 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)): @@ -1791,9 +1831,9 @@ def assert_url_contains(self, substring): if expected not in actual: raise Exception(error % (expected, actual)) - def assert_text( - self, text, selector="body", timeout=settings.SMALL_TIMEOUT - ): + def assert_text(self, text, selector="body", timeout=None): + if not timeout: + timeout = settings.SMALL_TIMEOUT start_ms = time.time() * 1000.0 stop_ms = start_ms + (timeout * 1000.0) text = text.strip() @@ -1816,9 +1856,9 @@ def assert_text( % (text, selector, element.text_all) ) - def assert_exact_text( - self, text, selector="body", timeout=settings.SMALL_TIMEOUT - ): + def assert_exact_text(self, text, selector="body", timeout=None): + if not timeout: + timeout = settings.SMALL_TIMEOUT start_ms = time.time() * 1000.0 stop_ms = start_ms + (timeout * 1000.0) text = text.strip() diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index d93fa380625..e8092ba0583 100644 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -4492,7 +4492,8 @@ def save_page_source(self, name, folder=None): @Params name - The file name to save the current page's HTML to. folder - The folder to save the file to. (Default = current folder)""" - self.wait_for_ready_state_complete() + if not self.__is_cdp_swap_needed(): + self.wait_for_ready_state_complete() return page_actions.save_page_source(self.driver, name, folder) def save_cookies(self, name="cookies.txt"): @@ -4550,6 +4551,9 @@ def load_cookies(self, name="cookies.txt", expiry=False): def delete_all_cookies(self): """Deletes all cookies in the web browser. Does NOT delete the saved cookies file.""" + if self.__is_cdp_swap_needed(): + self.cdp.clear_cookies() + return self.wait_for_ready_state_complete() self.driver.delete_all_cookies() if self.recorder_mode: @@ -4561,7 +4565,6 @@ def delete_all_cookies(self): def delete_saved_cookies(self, name="cookies.txt"): """Deletes the cookies file from the "saved_cookies" folder. Does NOT delete the cookies from the web browser.""" - self.wait_for_ready_state_complete() if name.endswith("/"): raise Exception("Invalid filename for Cookies!") if "/" in name: @@ -4600,14 +4603,20 @@ def get_saved_cookies(self, name="cookies.txt"): return json.loads(json_cookies) def get_cookie(self, name): + self.__check_scope() + self._check_browser() return self.driver.get_cookie(name) def get_cookies(self): + self.__check_scope() + self._check_browser() return self.driver.get_cookies() def get_cookie_string(self): + self.__check_scope() if self.__is_cdp_swap_needed(): return self.cdp.get_cookie_string() + self._check_browser() return self.execute_script("return document.cookie;") def add_cookie(self, cookie_dict, expiry=False): @@ -4622,6 +4631,8 @@ def add_cookie(self, cookie_dict, expiry=False): If expiry > 0: Set "expiry" to expiry minutes in the future. If expiry == True: Set "expiry" to 24 hours in the future. """ + self.__check_scope() + self._check_browser() cookie = cookie_dict if "domain" in cookie: origin = self.get_origin() @@ -4646,6 +4657,8 @@ def add_cookies(self, cookies, expiry=False): If expiry > 0: Set "expiry" to expiry minutes in the future. If expiry == True: Set "expiry" to 24 hours in the future. """ + self.__check_scope() + self._check_browser() origin = self.get_origin() trim_origin = origin.split("://")[-1] for cookie in cookies: diff --git a/seleniumbase/fixtures/page_actions.py b/seleniumbase/fixtures/page_actions.py index f754ad2b334..bb5bf055f0b 100644 --- a/seleniumbase/fixtures/page_actions.py +++ b/seleniumbase/fixtures/page_actions.py @@ -1342,7 +1342,8 @@ def save_page_source(driver, name, folder=None): """ from seleniumbase.core import log_helper - _reconnect_if_disconnected(driver) + if not __is_cdp_swap_needed(driver): + _reconnect_if_disconnected(driver) # If disconnected without CDP if not name.endswith(".html"): name = name + ".html" if folder: @@ -1353,7 +1354,11 @@ def save_page_source(driver, name, folder=None): html_file_path = os.path.join(file_path, name) else: html_file_path = name - page_source = driver.page_source + page_source = None + if __is_cdp_swap_needed(driver): + page_source = driver.cdp.get_page_source() + else: + page_source = driver.page_source html_file = codecs.open(html_file_path, "w+", "utf-8") rendered_source = log_helper.get_html_source_with_base_href( driver, page_source