diff --git a/examples/cdp_mode/ReadMe.md b/examples/cdp_mode/ReadMe.md index e72cc373738..8b13bf59fd6 100644 --- a/examples/cdp_mode/ReadMe.md +++ b/examples/cdp_mode/ReadMe.md @@ -506,6 +506,8 @@ sb.cdp.gui_click_and_hold(selector, timeframe=0.35) sb.cdp.gui_hover_x_y(x, y) sb.cdp.gui_hover_element(selector) sb.cdp.gui_hover_and_click(hover_selector, click_selector) +sb.cdp.hover_element(selector) +sb.cdp.hover_and_click(hover_selector, click_selector) sb.cdp.internalize_links() sb.cdp.is_checked(selector) sb.cdp.is_selected(selector) diff --git a/examples/cdp_mode/raw_canvas.py b/examples/cdp_mode/raw_canvas.py index 32c9f98f1e9..50a5ad20bd4 100644 --- a/examples/cdp_mode/raw_canvas.py +++ b/examples/cdp_mode/raw_canvas.py @@ -29,7 +29,7 @@ def get_canvas_pixel_colors_at_top_left(sb): url = "https://seleniumbase.io/other/canvas" sb.activate_cdp_mode(url) sb.assert_title_contains("Canvas") - sb.cdp.gui_click_with_offset("canvas", 0, 0, center=True) + sb.cdp.click_with_offset("canvas", 0, 0, center=True) sb.sleep(1) sb.uc_gui_press_key("ENTER") sb.sleep(0.5) diff --git a/examples/cdp_mode/raw_demo_site.py b/examples/cdp_mode/raw_demo_site.py index 2c0dda51404..5864d66540a 100644 --- a/examples/cdp_mode/raw_demo_site.py +++ b/examples/cdp_mode/raw_demo_site.py @@ -20,7 +20,7 @@ # Hover & click a dropdown element and assert results sb.cdp.assert_text("Automation Practice", "h3") - sb.cdp.gui_hover_and_click("#myDropdown", "#dropOption2") + sb.cdp.hover_and_click("#myDropdown", "#dropOption2") sb.cdp.assert_text("Link Two Selected", "h3") # Click a button and then verify the expected results diff --git a/examples/cdp_mode/raw_handle_alerts.py b/examples/cdp_mode/raw_handle_alerts.py index 098dcc57298..eefd6fa52b9 100644 --- a/examples/cdp_mode/raw_handle_alerts.py +++ b/examples/cdp_mode/raw_handle_alerts.py @@ -4,15 +4,15 @@ with SB(uc=True, test=True) as sb: url = "https://the-internet.herokuapp.com/javascript_alerts" sb.activate_cdp_mode(url) - sb.cdp.gui_click_element('button[onclick="jsAlert()"]') + sb.click('button[onclick="jsAlert()"]') sb.sleep(1) sb.uc_gui_press_key("\n") # Accept Alert sb.sleep(1) - sb.cdp.gui_click_element('button[onclick="jsConfirm()"]') + sb.click('button[onclick="jsConfirm()"]') sb.sleep(1) sb.uc_gui_press_key("ESC") # Dismiss Alert sb.sleep(1) - sb.cdp.gui_click_element('button[onclick="jsPrompt()"]') + sb.click('button[onclick="jsPrompt()"]') sb.sleep(1) sb.uc_gui_write("Here is my prompt answer\n") sb.sleep(1) diff --git a/help_docs/cdp_mode_methods.md b/help_docs/cdp_mode_methods.md index edac3b7a530..e35d12d8b75 100644 --- a/help_docs/cdp_mode_methods.md +++ b/help_docs/cdp_mode_methods.md @@ -144,6 +144,8 @@ sb.cdp.gui_click_and_hold(selector, timeframe=0.35) sb.cdp.gui_hover_x_y(x, y) sb.cdp.gui_hover_element(selector) sb.cdp.gui_hover_and_click(hover_selector, click_selector) +sb.cdp.hover_element(selector) +sb.cdp.hover_and_click(hover_selector, click_selector) sb.cdp.internalize_links() sb.cdp.is_checked(selector) sb.cdp.is_selected(selector) diff --git a/help_docs/method_summary.md b/help_docs/method_summary.md index 760974b421f..cfeec754250 100644 --- a/help_docs/method_summary.md +++ b/help_docs/method_summary.md @@ -111,6 +111,7 @@ self.is_element_in_an_iframe(selector, by="css selector") self.switch_to_frame_of_element(selector, by="css selector") self.hover(selector, by="css selector", timeout=None) # Duplicates: +# self.hover_element(selector, by="css selector", timeout=None) # self.hover_on_element(selector, by="css selector", timeout=None) # self.hover_over_element(selector, by="css selector", timeout=None) self.hover_and_click( diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index c8b8f28188d..542e47542cb 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "4.45.5" +__version__ = "4.45.6" diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index 61fb611f1e1..ad7b0a9763f 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -846,6 +846,8 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs): cdp.gui_hover_x_y = CDPM.gui_hover_x_y cdp.gui_hover_element = CDPM.gui_hover_element cdp.gui_hover_and_click = CDPM.gui_hover_and_click + cdp.hover_element = CDPM.hover_element + cdp.hover_and_click = CDPM.hover_and_click cdp.internalize_links = CDPM.internalize_links cdp.open_new_window = CDPM.open_new_window cdp.switch_to_window = CDPM.switch_to_window @@ -2736,6 +2738,7 @@ def _set_chrome_options( chrome_options.add_argument("--disable-save-password-bubble") chrome_options.add_argument("--disable-single-click-autofill") chrome_options.add_argument("--allow-file-access-from-files") + chrome_options.add_argument("--disable-component-update") chrome_options.add_argument("--disable-prompt-on-repost") chrome_options.add_argument("--dns-prefetch-disable") chrome_options.add_argument("--disable-translate") @@ -2764,6 +2767,7 @@ def _set_chrome_options( included_disabled_features.append("OptimizationHints") included_disabled_features.append("OptimizationHintsFetching") included_disabled_features.append("Translate") + included_disabled_features.append("ComponentUpdater") included_disabled_features.append("OptimizationTargetPrediction") included_disabled_features.append("OptimizationGuideModelDownloading") included_disabled_features.append("DownloadBubble") @@ -4789,6 +4793,7 @@ def get_local_driver( if devtools and not headless: edge_options.add_argument("--auto-open-devtools-for-tabs") edge_options.add_argument("--allow-file-access-from-files") + edge_options.add_argument("--disable-component-update") edge_options.add_argument("--allow-insecure-localhost") edge_options.add_argument("--allow-running-insecure-content") if user_agent: @@ -4847,6 +4852,7 @@ def get_local_driver( included_disabled_features.append("OptimizationHints") included_disabled_features.append("OptimizationHintsFetching") included_disabled_features.append("Translate") + included_disabled_features.append("ComponentUpdater") included_disabled_features.append("OptimizationTargetPrediction") included_disabled_features.append("OptimizationGuideModelDownloading") included_disabled_features.append("InsecureDownloadWarnings") diff --git a/seleniumbase/core/sb_cdp.py b/seleniumbase/core/sb_cdp.py index 8e0dc607292..c9f1d91f999 100644 --- a/seleniumbase/core/sb_cdp.py +++ b/seleniumbase/core/sb_cdp.py @@ -2384,6 +2384,31 @@ def gui_hover_element(self, selector, timeframe=0.25): self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.wait()) + def hover_element(self, selector, timeframe=0.25): + element = self.select(selector) + gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) + with gui_lock: + self.bring_active_window_to_front() + self.sleep(0.02) + element.mouse_move() + self.sleep(timeframe) + + def hover_and_click(self, hover_selector, click_selector): + if getattr(sb_config, "_cdp_mobile_mode", None): + self.select(click_selector).click() + return + hover_element = self.select(hover_selector) + gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) + with gui_lock: + self.bring_active_window_to_front() + self.sleep(0.02) + hover_element.mouse_move() + self.sleep(0.25) + try: + self.click(click_selector, timeout=0.5) + except Exception: + self.select(click_selector, timeout=2).click() + def gui_hover_and_click(self, hover_selector, click_selector): if getattr(sb_config, "_cdp_mobile_mode", None): self.select(click_selector).click() diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index 447ef349646..3b34af66930 100644 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -1637,14 +1637,14 @@ def get_partial_link_text_attribute( def click_link_text(self, link_text, timeout=None): """This method clicks link text on a page.""" self.__check_scope() - if self.__is_cdp_swap_needed(): - self.cdp.find_element(link_text, timeout=timeout).click() - return - self.__skip_if_esc() if not timeout: 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.find_element(link_text, timeout=timeout).click() + return + self.__skip_if_esc() link_text = self.__get_type_checked_text(link_text) if self.__is_cdp_swap_needed(): self.cdp.click_link(link_text) @@ -2717,7 +2717,7 @@ def hover(self, selector, by="css selector", timeout=None): original_by = by selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): - self.cdp.gui_hover_element(selector) + self.cdp.hover_element(selector) return self.wait_for_element_visible( original_selector, by=original_by, timeout=timeout @@ -2762,7 +2762,7 @@ def hover_and_click( click_selector, click_by ) if self.__is_cdp_swap_needed(): - self.cdp.gui_hover_and_click(hover_selector, click_selector) + self.cdp.hover_and_click(hover_selector, click_selector) return dropdown_element = self.wait_for_element_visible( original_selector, by=original_by, timeout=timeout @@ -9291,81 +9291,42 @@ def input( self, selector, text, by="css selector", timeout=None, retry=False ): """Same as self.update_text()""" - self.__check_scope() - if not timeout: - timeout = settings.LARGE_TIMEOUT - if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT: - timeout = self.__get_new_timeout(timeout) - selector, by = self.__recalculate_selector(selector, by) self.update_text(selector, text, by=by, timeout=timeout, retry=retry) def fill( self, selector, text, by="css selector", timeout=None, retry=False ): """Same as self.update_text()""" - self.__check_scope() - if not timeout: - timeout = settings.LARGE_TIMEOUT - if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT: - timeout = self.__get_new_timeout(timeout) - selector, by = self.__recalculate_selector(selector, by) self.update_text(selector, text, by=by, timeout=timeout, retry=retry) def write( self, selector, text, by="css selector", timeout=None, retry=False ): """Same as self.update_text()""" - self.__check_scope() - if not timeout: - timeout = settings.LARGE_TIMEOUT - if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT: - timeout = self.__get_new_timeout(timeout) - selector, by = self.__recalculate_selector(selector, by) self.update_text(selector, text, by=by, timeout=timeout, retry=retry) def click_link(self, link_text, timeout=None): """Same as self.click_link_text()""" - self.__check_scope() - if not timeout: - timeout = settings.SMALL_TIMEOUT - if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: - timeout = self.__get_new_timeout(timeout) self.click_link_text(link_text, timeout=timeout) def click_partial_link(self, partial_link_text, timeout=None): """Same as self.click_partial_link_text()""" - self.__check_scope() - if not timeout: - timeout = settings.SMALL_TIMEOUT - if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: - timeout = self.__get_new_timeout(timeout) self.click_partial_link_text(partial_link_text, timeout=timeout) def right_click(self, selector, by="css selector", timeout=None): """Same as self.context_click()""" - self.__check_scope() - if not timeout: - timeout = settings.SMALL_TIMEOUT - if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: - timeout = self.__get_new_timeout(timeout) self.context_click(selector, by=by, timeout=timeout) + def hover_element(self, selector, by="css selector", timeout=None): + """Same as self.hover()""" + return self.hover(selector, by=by, timeout=timeout) + def hover_on_element(self, selector, by="css selector", timeout=None): """Same as self.hover()""" - self.__check_scope() - if not timeout: - timeout = settings.SMALL_TIMEOUT - if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: - timeout = self.__get_new_timeout(timeout) return self.hover(selector, by=by, timeout=timeout) def hover_over_element(self, selector, by="css selector", timeout=None): """Same as self.hover()""" - self.__check_scope() - if not timeout: - timeout = settings.SMALL_TIMEOUT - if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: - timeout = self.__get_new_timeout(timeout) return self.hover(selector, by=by, timeout=timeout) def wait_for_element_visible( diff --git a/seleniumbase/undetected/cdp_driver/config.py b/seleniumbase/undetected/cdp_driver/config.py index 866ea2bdd69..06cedd944f5 100644 --- a/seleniumbase/undetected/cdp_driver/config.py +++ b/seleniumbase/undetected/cdp_driver/config.py @@ -207,6 +207,7 @@ def __init__( "--disable-top-sites", "--disable-translate", "--dns-prefetch-disable", + "--disable-component-update", "--disable-renderer-backgrounding", "--disable-dev-shm-usage", ] @@ -264,7 +265,7 @@ def __call__(self): "OptimizationTargetPrediction,OptimizationGuideModelDownloading," "SidePanelPinning,UserAgentClientHint,PrivacySandboxSettings4," "OptimizationHintsFetching,InterestFeedContentSuggestions," - "Bluetooth,WebBluetooth,UnifiedWebBluetooth," + "Bluetooth,WebBluetooth,UnifiedWebBluetooth,ComponentUpdater," "DisableLoadExtensionCommandLineSwitch," "WebAuthentication,PasskeyAuth" ] diff --git a/seleniumbase/undetected/cdp_driver/element.py b/seleniumbase/undetected/cdp_driver/element.py index 3da6ededcec..023c651bf0a 100644 --- a/seleniumbase/undetected/cdp_driver/element.py +++ b/seleniumbase/undetected/cdp_driver/element.py @@ -501,8 +501,8 @@ async def mouse_click_async( logger.warning("Could not calculate box model for %s", self) return logger.debug("Clicking on location: %.2f, %.2f" % center) - await asyncio.gather( - self.flash_async(0.25), + asyncio.create_task(self.flash_async(0.25)) + asyncio.create_task( self._tab.send( cdp.input_.dispatch_mouse_event( "mousePressed", @@ -514,6 +514,8 @@ async def mouse_click_async( click_count=1, ) ), + ) + asyncio.create_task( self._tab.send( cdp.input_.dispatch_mouse_event( "mouseReleased", @@ -558,11 +560,13 @@ async def mouse_click_with_offset_async( logger.debug("Clicking on location: %.2f, %.2f" % center_pos) else: logger.debug("Clicking on location: %.2f, %.2f" % (x_pos, y_pos)) - await asyncio.gather( + asyncio.create_task( self.flash_async( - x_offset=x_offset - int(width / 2), - y_offset=y_offset - int(height / 2), + x_offset=x_offset - (width / 2), + y_offset=y_offset - (height / 2), ), + ) + asyncio.create_task( self._tab.send( cdp.input_.dispatch_mouse_event( "mousePressed", @@ -573,7 +577,9 @@ async def mouse_click_with_offset_async( buttons=buttons, click_count=1, ) - ), + ) + ) + asyncio.create_task( self._tab.send( cdp.input_.dispatch_mouse_event( "mouseReleased", @@ -602,16 +608,13 @@ async def mouse_move_async(self): *center, self, ) - await self._tab.send( - cdp.input_.dispatch_mouse_event( - "mouseMoved", x=center[0], y=center[1] - ) - ) - await self._tab.sleep(0.05) - await self._tab.send( - cdp.input_.dispatch_mouse_event( - "mouseReleased", x=center[0], y=center[1] - ) + await asyncio.gather( + self._tab.send( + cdp.input_.dispatch_mouse_event( + "mouseMoved", x=center[0], y=center[1] + ) + ), + self._tab.sleep(0.05), ) async def mouse_drag_async(