From dc86046193ba25b4cd21117a3bfb6e24c3c416e4 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Sat, 9 Mar 2024 18:07:23 -0500 Subject: [PATCH 1/4] Add methods and update UC Mode --- help_docs/method_summary.md | 15 +++++++++++++++ seleniumbase/core/browser_launcher.py | 17 ++++++++++++++--- seleniumbase/core/sb_driver.py | 19 +++++++++++++++++++ seleniumbase/fixtures/base_case.py | 13 +++++++++++++ seleniumbase/fixtures/page_actions.py | 21 +++++++++++++++++++++ seleniumbase/undetected/__init__.py | 22 ++++++++++++++++++++++ 6 files changed, 104 insertions(+), 3 deletions(-) diff --git a/help_docs/method_summary.md b/help_docs/method_summary.md index 5ab81a3a3c3..5be4f135548 100644 --- a/help_docs/method_summary.md +++ b/help_docs/method_summary.md @@ -370,6 +370,8 @@ self.highlight_type(selector, text, by="css selector", loops=3, scroll=True, tim # self.highlight_update_text( # selector, text, by="css selector", loops=3, scroll=True, timeout=None) +self.highlight_if_visible(selector, by="css selector", loops=4, scroll=True) + self.highlight(selector, by="css selector", loops=4, scroll=True, timeout=None) self.press_up_arrow(selector="html", times=1, by="css selector") @@ -742,6 +744,7 @@ self.generate_traffic_chain(pages, loops=1) self.get_element(selector, by="css selector", timeout=None) # Duplicates: +# self.wait_for_selector(selector, by="css selector", timeout=None) # self.locator(selector, by="css selector", timeout=None) # self.wait_for_element_present(selector, by="css selector", timeout=None) @@ -957,6 +960,12 @@ driver.assert_exact_text(text, selector) driver.wait_for_element(selector) +driver.wait_for_element_visible(selector) + +driver.wait_for_element_present(selector) + +driver.wait_for_selector(selector) + driver.wait_for_text(text, selector) driver.wait_for_exact_text(text, selector) @@ -991,6 +1000,8 @@ driver.highlight(selector) driver.highlight_click(selector) +driver.highlight_if_visible(selector) + driver.sleep(seconds) driver.locator(selector) @@ -1015,6 +1026,10 @@ driver.uc_open_with_reconnect(url, reconnect_time=None) driver.reconnect(timeout) +driver.disconnect() + +driver.connect() + driver.uc_click( selector, by="css selector", timeout=settings.SMALL_TIMEOUT, reconnect_time=None) diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index 6481f7f0a71..fe2ef6fce25 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -169,6 +169,9 @@ def extend_driver(driver): driver.assert_text = DM.assert_text driver.assert_exact_text = DM.assert_exact_text driver.wait_for_element = DM.wait_for_element + driver.wait_for_element_visible = DM.wait_for_element_visible + driver.wait_for_element_present = DM.wait_for_element_present + driver.wait_for_selector = DM.wait_for_selector driver.wait_for_text = DM.wait_for_text driver.wait_for_exact_text = DM.wait_for_exact_text driver.wait_for_and_accept_alert = DM.wait_for_and_accept_alert @@ -186,6 +189,7 @@ def extend_driver(driver): driver.get_user_agent = DM.get_user_agent driver.highlight = DM.highlight driver.highlight_click = DM.highlight_click + driver.highlight_if_visible = DM.highlight_if_visible driver.sleep = time.sleep driver.get_attribute = DM.get_attribute driver.get_page_source = DM.get_page_source @@ -458,27 +462,34 @@ def uc_click( by = "css selector" except Exception: pass - element = driver.wait_for_element(selector, by=by, timeout=timeout) + element = driver.wait_for_selector(selector, by=by, timeout=timeout) + if not element.tag_name == "span": # Element must be "visible" + element = driver.wait_for_element(selector, by=by, timeout=timeout) try: element.uc_click( driver, selector, by=by, reconnect_time=reconnect_time ) except ElementClickInterceptedException: + time.sleep(0.16) driver.js_click(selector, by=by, timeout=timeout) + if not reconnect_time: + driver.reconnect(0.1) + else: + driver.reconnect(reconnect_time) def uc_switch_to_frame(driver, frame, reconnect_time=None): from selenium.webdriver.remote.webelement import WebElement if isinstance(frame, WebElement): if not reconnect_time: - driver.reconnect(0.15) + driver.reconnect(0.1) else: driver.reconnect(reconnect_time) driver.switch_to.frame(frame) else: iframe = driver.locator(frame) if not reconnect_time: - driver.reconnect(0.15) + driver.reconnect(0.1) else: driver.reconnect(reconnect_time) driver.switch_to.frame(iframe) diff --git a/seleniumbase/core/sb_driver.py b/seleniumbase/core/sb_driver.py index 1c183167cb4..f5054173fd8 100644 --- a/seleniumbase/core/sb_driver.py +++ b/seleniumbase/core/sb_driver.py @@ -97,6 +97,15 @@ def assert_exact_text(self, *args, **kwargs): def wait_for_element(self, *args, **kwargs): return page_actions.wait_for_element(self.driver, *args, **kwargs) + def wait_for_element_visible(self, *args, **kwargs): + return page_actions.wait_for_element(self.driver, *args, **kwargs) + + def wait_for_element_present(self, *args, **kwargs): + return page_actions.wait_for_selector(self.driver, *args, **kwargs) + + def wait_for_selector(self, *args, **kwargs): + return page_actions.wait_for_selector(self.driver, *args, **kwargs) + def wait_for_text(self, *args, **kwargs): return page_actions.wait_for_text(self.driver, *args, **kwargs) @@ -147,6 +156,8 @@ def get_user_agent(self, *args, **kwargs): return js_utils.get_user_agent(self.driver, *args, **kwargs) def highlight(self, *args, **kwargs): + if "scroll" in kwargs: + kwargs.pop("scroll") w_args = kwargs.copy() if "loops" in w_args: w_args.pop("loops") @@ -161,8 +172,16 @@ def highlight_click(self, *args, **kwargs): self.highlight(*args, **kwargs) if "loops" in kwargs: kwargs.pop("loops") + if "scroll" in kwargs: + kwargs.pop("scroll") page_actions.click(self.driver, *args, **kwargs) + def highlight_if_visible( + self, selector, by="css selector", loops=4, scroll=True + ): + if self.is_element_visible(selector, by=by): + self.highlight(selector, by=by, loops=loops, scroll=scroll) + def switch_to_frame(self, frame): if isinstance(frame, WebElement): self.driver.switch_to.frame(frame) diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index da5c0fd192f..b838a4ca27b 100644 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -5601,6 +5601,14 @@ def highlight_type( self.__highlight(selector, by=by, loops=loops, scroll=scroll) self.update_text(selector, text, by=by) + def highlight_if_visible( + self, selector, by="css selector", loops=4, scroll=True, + ): + """Highlights the element if the element is visible.""" + self.__check_scope() + if self.is_element_visible(selector, by=by): + self.__highlight(selector, by=by, loops=loops, scroll=scroll) + def __highlight( self, selector, by="css selector", loops=None, scroll=True ): @@ -9089,6 +9097,11 @@ def locator(self, selector, by="css selector", timeout=None): The element does not need be visible (it may be hidden).""" return self.wait_for_element_present(selector, by=by, timeout=timeout) + def wait_for_selector(self, selector, by="css selector", timeout=None): + """Same as wait_for_element_present() - returns the element. + The element does not need be visible (it may be hidden).""" + return self.wait_for_element_present(selector, by=by, timeout=timeout) + def wait_for_query_selector( self, selector, by="css selector", timeout=None ): diff --git a/seleniumbase/fixtures/page_actions.py b/seleniumbase/fixtures/page_actions.py index 33eb3f1a84b..fc69a491801 100644 --- a/seleniumbase/fixtures/page_actions.py +++ b/seleniumbase/fixtures/page_actions.py @@ -1752,6 +1752,27 @@ def wait_for_element( ) +def wait_for_selector( + driver, + selector, + by="css selector", + timeout=settings.LARGE_TIMEOUT, +): + original_selector = None + if page_utils.is_valid_by(by): + original_selector = selector + elif page_utils.is_valid_by(selector): + original_selector = by + selector, by = page_utils.recalculate_selector(selector, by) + return wait_for_element_present( + driver=driver, + selector=selector, + by=by, + timeout=timeout, + original_selector=original_selector, + ) + + def wait_for_text( driver, text, diff --git a/seleniumbase/undetected/__init__.py b/seleniumbase/undetected/__init__.py index 4c8dd9c8d55..7e58bc3786f 100644 --- a/seleniumbase/undetected/__init__.py +++ b/seleniumbase/undetected/__init__.py @@ -442,6 +442,28 @@ def reconnect(self, timeout=0.1): except Exception: pass + def disconnect(self): + """Stops the chromedriver service that runs in the background. + To use driver methods again, you MUST call driver.connect()""" + if hasattr(self, "service"): + try: + self.service.stop() + except Exception: + pass + + def connect(self): + """Starts the chromedriver service that runs in the background + and recreates the session.""" + if hasattr(self, "service"): + try: + self.service.start() + except Exception: + pass + try: + self.start_session() + except Exception: + pass + def start_session(self, capabilities=None): if not capabilities: capabilities = self.options.to_capabilities() From f1c91d8aa7b9c16eaed8cabc834b882999d43582 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Sat, 9 Mar 2024 18:07:56 -0500 Subject: [PATCH 2/4] 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 d5648bb8a5f..fec2d8440d3 100755 --- a/requirements.txt +++ b/requirements.txt @@ -39,7 +39,7 @@ pluggy==1.2.0;python_version<"3.8" pluggy==1.4.0;python_version>="3.8" py==1.11.0 pytest==7.4.4;python_version<"3.8" -pytest==8.0.2;python_version>="3.8" +pytest==8.1.1;python_version>="3.8" pytest-html==2.0.1 pytest-metadata==3.0.0;python_version<"3.8" pytest-metadata==3.1.1;python_version>="3.8" diff --git a/setup.py b/setup.py index 2c5bf5ecc0f..2fdf090015d 100755 --- a/setup.py +++ b/setup.py @@ -187,7 +187,7 @@ 'pluggy==1.4.0;python_version>="3.8"', "py==1.11.0", 'pytest==7.4.4;python_version<"3.8"', - 'pytest==8.0.2;python_version>="3.8"', + 'pytest==8.1.1;python_version>="3.8"', "pytest-html==2.0.1", # Newer ones had issues 'pytest-metadata==3.0.0;python_version<"3.8"', 'pytest-metadata==3.1.1;python_version>="3.8"', From 516d9b2679021a2171db350e515028fd04f49111 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Sat, 9 Mar 2024 18:10:01 -0500 Subject: [PATCH 3/4] Update examples --- examples/raw_bing_captcha.py | 2 +- examples/raw_cdp_logging.py | 4 +++- examples/raw_nopecha.py | 7 ++++--- examples/raw_robot.py | 17 +++++++++++++++++ examples/raw_uc_mode.py | 4 +++- examples/test_3d_apis.py | 2 +- 6 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 examples/raw_robot.py diff --git a/examples/raw_bing_captcha.py b/examples/raw_bing_captcha.py index 29a02a79cca..01266b3b860 100644 --- a/examples/raw_bing_captcha.py +++ b/examples/raw_bing_captcha.py @@ -9,4 +9,4 @@ sb.activate_demo_mode() # See asserts as they happen sb.assert_element("svg#success-icon") sb.assert_text("Success!", "span#success-text") - sb.highlight("div#success", loops=10) + sb.highlight("div#success") diff --git a/examples/raw_cdp_logging.py b/examples/raw_cdp_logging.py index b9e3ed8286e..bfbff0bad42 100644 --- a/examples/raw_cdp_logging.py +++ b/examples/raw_cdp_logging.py @@ -3,7 +3,9 @@ driver = Driver(uc=True, log_cdp=True) try: - driver.get("https://seleniumbase.io/apps/invisible_recaptcha") + driver.uc_open_with_reconnect("https://seleniumbase.io/apps/turnstile") + driver.uc_switch_to_frame("iframe") + driver.uc_click("span.mark") driver.sleep(3) pprint(driver.get_log("performance")) finally: diff --git a/examples/raw_nopecha.py b/examples/raw_nopecha.py index f6cb024536d..53a4f7d2068 100644 --- a/examples/raw_nopecha.py +++ b/examples/raw_nopecha.py @@ -1,17 +1,18 @@ from seleniumbase import SB with SB(uc=True, test=True) as sb: - sb.driver.uc_open_with_reconnect("https://nopecha.com/demo/turnstile", 5) + sb.driver.uc_open_with_reconnect("https://nopecha.com/demo/turnstile", 4) sb.driver.uc_switch_to_frame("#example-container5 iframe") - sb.driver.uc_click("span.mark") + sb.driver.uc_click("span.mark", reconnect_time=1) if sb.is_element_visible("#example-container0 iframe"): sb.switch_to_frame("#example-container0 iframe") if not sb.is_element_visible("circle.success-circle"): - sb.driver.uc_click("span.mark") + sb.driver.uc_click("span.mark", reconnect_time=1) sb.switch_to_frame("#example-container0 iframe") sb.assert_element("circle.success-circle") sb.switch_to_parent_frame() + sb.switch_to_frame("#example-container5 iframe") sb.assert_element("svg#success-icon", timeout=3) sb.switch_to_parent_frame() diff --git a/examples/raw_robot.py b/examples/raw_robot.py new file mode 100644 index 00000000000..3a20b04b42f --- /dev/null +++ b/examples/raw_robot.py @@ -0,0 +1,17 @@ +from seleniumbase import SB + +with SB(enable_3d_apis=True, test=True) as sb: + sb.open("threejs.org/examples/#webgl_animation_skinning_morph") + sb.switch_to_frame("iframe#viewer") + sb.set_text_content("#info p", "Hi, I'm Michael Mintz") + sb.add_css_style("#info p{zoom: 2.54}") + sb.sleep(0.8) + sb.click('button:contains("Wave")') + sb.highlight("#info p") + sb.select_option_by_text("select", "Idle") + sb.click('button:contains("ThumbsUp")') + sb.set_text_content("#info p", "I created SeleniumBase") + sb.highlight("#info p") + sb.sleep(0.8) + sb.click('button:contains("Jump")') + sb.sleep(1.5) diff --git a/examples/raw_uc_mode.py b/examples/raw_uc_mode.py index 13e050bc233..560cba94719 100644 --- a/examples/raw_uc_mode.py +++ b/examples/raw_uc_mode.py @@ -7,5 +7,7 @@ if not sb.is_text_visible("Username", '[for="user_login"]'): sb.driver.uc_open_with_reconnect(url, 4) sb.assert_text("Username", '[for="user_login"]', timeout=3) - sb.highlight('label[for="user_login"]', loops=3) + sb.assert_element('label[for="user_login"]') + sb.highlight('button:contains("Sign in")') + sb.highlight('h1:contains("GitLab.com")') sb.post_message("SeleniumBase wasn't detected", duration=4) diff --git a/examples/test_3d_apis.py b/examples/test_3d_apis.py index c0f7fdd8ce1..83113d6d317 100644 --- a/examples/test_3d_apis.py +++ b/examples/test_3d_apis.py @@ -1,5 +1,5 @@ from seleniumbase import BaseCase -BaseCase.main(__name__, __file__) +BaseCase.main(__name__, __file__, "--enable-3d-apis") class ThreeJSTests(BaseCase): From aaae4b4320fbe3a310db6ec770dbdbe1d66a2fbc Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Sat, 9 Mar 2024 18:10:54 -0500 Subject: [PATCH 4/4] Version 4.24.5 --- seleniumbase/__version__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index 7a030c2f44f..e84a6e5d446 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "4.24.4" +__version__ = "4.24.5"