From cffb6e0cc705e4bd16d87785f8f796bf148b25cb Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Wed, 19 Feb 2025 20:42:02 -0500 Subject: [PATCH 1/6] Add `element.gui_click()` to CDP Mode --- seleniumbase/core/sb_cdp.py | 40 ++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/seleniumbase/core/sb_cdp.py b/seleniumbase/core/sb_cdp.py index 59a6a7e5..2b0003df 100644 --- a/seleniumbase/core/sb_cdp.py +++ b/seleniumbase/core/sb_cdp.py @@ -56,6 +56,9 @@ class CDPMethods(): element, *args, **kwargs ) element.focus = lambda: self.__focus(element) + element.gui_click = ( + lambda *args, **kwargs: self.__gui_click(element, *args, **kwargs) + ) element.highlight_overlay = lambda: self.__highlight_overlay(element) element.mouse_click = lambda: self.__mouse_click(element) element.mouse_drag = ( @@ -426,6 +429,39 @@ class CDPMethods(): self.loop.run_until_complete(element.focus_async()) ) + def __gui_click(self, element, timeframe=None): + element.scroll_into_view() + self.__add_light_pause() + position = element.get_position() + x = position.x + y = position.y + e_width = position.width + e_height = position.height + # Relative to window + element_rect = {"height": e_height, "width": e_width, "x": x, "y": y} + window_rect = self.get_window_rect() + w_bottom_y = window_rect["y"] + window_rect["height"] + viewport_height = window_rect["innerHeight"] + x = window_rect["x"] + element_rect["x"] + y = w_bottom_y - viewport_height + element_rect["y"] + y_scroll_offset = window_rect["pageYOffset"] + y = y - y_scroll_offset + x = x + window_rect["scrollX"] + y = y + window_rect["scrollY"] + # Relative to screen + element_rect = {"height": e_height, "width": e_width, "x": x, "y": y} + e_width = element_rect["width"] + e_height = element_rect["height"] + e_x = element_rect["x"] + e_y = element_rect["y"] + x, y = ((e_x + e_width / 2.0) + 0.5), ((e_y + e_height / 2.0) + 0.5) + if not timeframe or not isinstance(timeframe, (int, float)): + timeframe = 0.25 + if timeframe > 3: + timeframe = 3 + self.gui_click_x_y(x, y, timeframe=timeframe) + return self.loop.run_until_complete(self.page.wait()) + def __highlight_overlay(self, element): return ( self.loop.run_until_complete(element.highlight_overlay_async()) @@ -461,9 +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.wait()) def __query_selector(self, element, selector): selector = self.__convert_to_css_if_xpath(selector) From b83c61865d4e7fdf93529b211846617feaa36a45 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Wed, 19 Feb 2025 20:43:06 -0500 Subject: [PATCH 2/6] Make updates for pynose compatibility --- seleniumbase/plugins/base_plugin.py | 27 +++++++++++-------------- seleniumbase/plugins/selenium_plugin.py | 1 + 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/seleniumbase/plugins/base_plugin.py b/seleniumbase/plugins/base_plugin.py index 2c4f945a..7ceb7568 100644 --- a/seleniumbase/plugins/base_plugin.py +++ b/seleniumbase/plugins/base_plugin.py @@ -208,7 +208,6 @@ class Base(Plugin): self.duration = float(0) self.page_results_list = [] self.test_count = 0 - self.import_error = False log_path = constants.Logs.LATEST + "/" archive_logs = options.archive_logs log_helper.log_folder_setup(log_path, archive_logs) @@ -238,6 +237,7 @@ class Base(Plugin): ) else: variables = {} + test.test.test_id = test.id() test.test.is_nosetest = True test.test.environment = self.options.environment test.test.env = self.options.environment # Add a shortened version @@ -263,17 +263,16 @@ class Base(Plugin): ) log_helper.clear_empty_logs() if self.report_on: - if not self.import_error: - report_helper.add_bad_page_log_file(self.page_results_list) - report_log_path = report_helper.archive_new_report_logs() - report_helper.build_report( - report_log_path, - self.page_results_list, - self.successes, - self.failures, - self.options.browser, - self.show_report, - ) + report_helper.add_bad_page_log_file(self.page_results_list) + report_log_path = report_helper.archive_new_report_logs() + report_helper.build_report( + report_log_path, + self.page_results_list, + self.successes, + self.failures, + self.options.browser, + self.show_report, + ) def addSuccess(self, test, capt): if self.report_on: @@ -293,9 +292,6 @@ class Base(Plugin): "%.2fs" % (float(time.time()) - float(self.start_time)) ) if test.id() == "nose.failure.Failure.runTest": - print(">>> ERROR: Could not locate tests to run!") - print(">>> The Test Report WILL NOT be generated!") - self.import_error = True return self.failures.append(test.id()) self.page_results_list.append( @@ -314,6 +310,7 @@ class Base(Plugin): test._log_fail_data() sb_config._excinfo_tb = err log_path = None + source = None if hasattr(sb_config, "_test_logpath"): log_path = sb_config._test_logpath if hasattr(sb_config, "_last_page_source"): diff --git a/seleniumbase/plugins/selenium_plugin.py b/seleniumbase/plugins/selenium_plugin.py index ba2fc7b5..20d48b2e 100644 --- a/seleniumbase/plugins/selenium_plugin.py +++ b/seleniumbase/plugins/selenium_plugin.py @@ -1309,6 +1309,7 @@ class SeleniumBrowser(Plugin): test.test.dashboard = False test.test._multithreaded = False test.test._reuse_session = False + sb_config.recorder_mode = test.test.recorder_mode sb_config.no_screenshot = test.test.no_screenshot_after_test if test.test.servername != "localhost": # Using Selenium Grid From 70692765deeb3a19ce84be2dc1df54387b1696ef Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Wed, 19 Feb 2025 20:43:40 -0500 Subject: [PATCH 3/6] Update examples --- examples/cdp_mode/raw_cf.py | 4 ++-- examples/cdp_mode/raw_elal.py | 2 ++ examples/test_usefixtures.py | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/cdp_mode/raw_cf.py b/examples/cdp_mode/raw_cf.py index 0d64a2d2..cb4652f9 100644 --- a/examples/cdp_mode/raw_cf.py +++ b/examples/cdp_mode/raw_cf.py @@ -1,14 +1,14 @@ """Using CDP Mode with PyAutoGUI to bypass CAPTCHAs.""" from seleniumbase import SB -with SB(uc=True, test=True, locale_code="en") as sb: +with SB(uc=True, test=True, locale_code="en", incognito=True) as sb: url = "https://www.cloudflare.com/login" sb.activate_cdp_mode(url) sb.sleep(3) sb.uc_gui_handle_captcha() # PyAutoGUI press Tab and Spacebar sb.sleep(2) -with SB(uc=True, test=True, locale_code="en") as sb: +with SB(uc=True, test=True, locale_code="en", incognito=True) as sb: url = "https://www.cloudflare.com/login" sb.activate_cdp_mode(url) sb.sleep(2) diff --git a/examples/cdp_mode/raw_elal.py b/examples/cdp_mode/raw_elal.py index f5003db4..110b1e2b 100644 --- a/examples/cdp_mode/raw_elal.py +++ b/examples/cdp_mode/raw_elal.py @@ -26,6 +26,8 @@ with SB(uc=True, test=True, locale_code="en") as sb: print("*** Lowest Price: ***") lowest_price = sorted(prices)[0] print(lowest_price) + sb.cdp.scroll_down(12) + sb.sleep(1) sb.cdp.find_element_by_text(lowest_price).click() sb.sleep(1) search_cell = 'button[aria-label*="Search.cell.buttonTitle"]' diff --git a/examples/test_usefixtures.py b/examples/test_usefixtures.py index c0853996..1c767610 100644 --- a/examples/test_usefixtures.py +++ b/examples/test_usefixtures.py @@ -4,6 +4,9 @@ import pytest @pytest.mark.usefixtures("sb") class Test_UseFixtures: def test_usefixtures_on_class(self): + if not hasattr(self, "sb"): + print("This test is for pytest only!") + return sb = self.sb sb.open("https://seleniumbase.io/realworld/login") sb.type("#username", "demo_user") From 58455b2ea4124d44895ea17efbfbf1dadc867dbb Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Wed, 19 Feb 2025 20:44:07 -0500 Subject: [PATCH 4/6] Update the docs --- examples/cdp_mode/ReadMe.md | 1 + mkdocs_build/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/cdp_mode/ReadMe.md b/examples/cdp_mode/ReadMe.md index 79e96643..97b7edec 100644 --- a/examples/cdp_mode/ReadMe.md +++ b/examples/cdp_mode/ReadMe.md @@ -507,6 +507,7 @@ element.clear_input() element.click() element.flash(duration=0.5, color="EE4488") element.focus() +element.gui_click(timeframe=0.25) element.highlight_overlay() element.mouse_click() element.mouse_drag(destination) diff --git a/mkdocs_build/requirements.txt b/mkdocs_build/requirements.txt index fc5cc0f1..cbf662fa 100644 --- a/mkdocs_build/requirements.txt +++ b/mkdocs_build/requirements.txt @@ -14,7 +14,7 @@ pathspec==0.12.1 Babel==2.17.0 paginate==0.5.7 mkdocs==1.6.1 -mkdocs-material==9.6.4 +mkdocs-material==9.6.5 mkdocs-exclude-search==0.6.6 mkdocs-simple-hooks==0.1.5 mkdocs-material-extensions==1.3.1 From d42fe1a83387a13c6fd6007eb4747eace5ab6123 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Wed, 19 Feb 2025 20:44:34 -0500 Subject: [PATCH 5/6] 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 2c05747d..8763830f 100755 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ filelock~=3.16.1;python_version<"3.9" filelock>=3.17.0;python_version>="3.9" fasteners>=0.19 mycdp>=1.1.0 -pynose>=1.5.3 +pynose>=1.5.4 platformdirs>=4.3.6 typing-extensions>=4.12.2 sbvirtualdisplay>=1.4.0 diff --git a/setup.py b/setup.py index 76871d76..e4890264 100755 --- a/setup.py +++ b/setup.py @@ -161,7 +161,7 @@ setup( 'filelock>=3.17.0;python_version>="3.9"', 'fasteners>=0.19', "mycdp>=1.1.0", - "pynose>=1.5.3", + "pynose>=1.5.4", 'platformdirs>=4.3.6', 'typing-extensions>=4.12.2', "sbvirtualdisplay>=1.4.0", From 978094d06e06ff65f2cf19a1d468631439e2971e Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Wed, 19 Feb 2025 20:44:50 -0500 Subject: [PATCH 6/6] Version 4.34.17 --- seleniumbase/__version__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index a304fe85..ae694d9e 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "4.34.16" +__version__ = "4.34.17"