diff --git a/examples/cdp_mode/ReadMe.md b/examples/cdp_mode/ReadMe.md index 5480986d4e7..d039f40ed15 100644 --- a/examples/cdp_mode/ReadMe.md +++ b/examples/cdp_mode/ReadMe.md @@ -143,7 +143,7 @@ with SB(uc=True, test=True, locale_code="en", ad_block=True) as sb: sb.sleep(3) location = "Anaheim, CA, USA" sb.cdp.press_keys("input#searchbox", location) - sb.sleep(1) + sb.sleep(2) sb.cdp.click("div#suggestion-list ul li a") sb.sleep(1) sb.cdp.click('div.hotel-card-footer button') diff --git a/examples/cdp_mode/raw_easyjet.py b/examples/cdp_mode/raw_easyjet.py index 518cfe8270d..b071a2edbce 100644 --- a/examples/cdp_mode/raw_easyjet.py +++ b/examples/cdp_mode/raw_easyjet.py @@ -20,7 +20,7 @@ sb.sleep(1.2) sb.cdp.click('input[name="when"]') sb.sleep(1.2) - sb.cdp.click('[data-testid="month"] button[aria-disabled="false"]') + sb.cdp.click('[data-testid="month"]:last-of-type [aria-disabled="false"]') sb.sleep(1.2) sb.cdp.click('[data-testid="month"]:last-of-type [aria-disabled="false"]') sb.sleep(1.2) diff --git a/examples/cdp_mode/raw_hyatt.py b/examples/cdp_mode/raw_hyatt.py index f6867823305..86bb270fe31 100644 --- a/examples/cdp_mode/raw_hyatt.py +++ b/examples/cdp_mode/raw_hyatt.py @@ -12,7 +12,7 @@ sb.sleep(3) location = "Anaheim, CA, USA" sb.cdp.press_keys("input#searchbox", location) - sb.sleep(1) + sb.sleep(2) sb.cdp.click("div#suggestion-list ul li a") sb.sleep(1) sb.cdp.click('div.hotel-card-footer button') diff --git a/examples/cdp_mode/raw_southwest.py b/examples/cdp_mode/raw_southwest.py index 226b2870684..ba3f36f687c 100644 --- a/examples/cdp_mode/raw_southwest.py +++ b/examples/cdp_mode/raw_southwest.py @@ -7,13 +7,17 @@ origin = "DEN" destination = "PHX" sb.cdp.gui_click_element("input#originationAirportCode") - sb.sleep(0.2) + sb.sleep(0.5) + sb.uc_gui_press_keys(" " + "\n") + sb.sleep(0.5) + sb.cdp.gui_click_element("input#originationAirportCode") + sb.sleep(0.5) sb.uc_gui_press_keys(origin + "\n") sb.sleep(0.5) sb.cdp.gui_click_element("h1.heading") sb.sleep(0.5) sb.cdp.gui_click_element("input#destinationAirportCode") - sb.sleep(0.2) + sb.sleep(0.5) sb.uc_gui_press_keys(destination + "\n") sb.sleep(0.5) sb.cdp.gui_click_element("h1.heading") diff --git a/help_docs/method_summary.md b/help_docs/method_summary.md index 62c8a41f3b6..f235ce45891 100644 --- a/help_docs/method_summary.md +++ b/help_docs/method_summary.md @@ -231,6 +231,8 @@ self.get_gui_element_rect(selector, by="css selector") self.get_gui_element_center(selector, by="css selector") +self.get_screen_rect() + self.get_window_rect() self.get_window_size() @@ -245,6 +247,10 @@ self.set_window_position(x, y) self.maximize_window() +self.minimize_window() + +self.reset_window_size() + self.switch_to_frame(frame="iframe", timeout=None) self.switch_to_default_content() diff --git a/mkdocs_build/requirements.txt b/mkdocs_build/requirements.txt index c92906028a9..1f41bafb9e3 100644 --- a/mkdocs_build/requirements.txt +++ b/mkdocs_build/requirements.txt @@ -3,7 +3,7 @@ regex>=2024.11.6 pymdown-extensions>=10.12 -pipdeptree>=2.23.4 +pipdeptree>=2.24.0 python-dateutil>=2.8.2 Markdown==3.7 markdown2==2.5.1 @@ -20,7 +20,7 @@ lxml==5.3.0 pyquery==2.0.1 readtime==3.0.0 mkdocs==1.6.1 -mkdocs-material==9.5.44 +mkdocs-material==9.5.46 mkdocs-exclude-search==0.6.6 mkdocs-simple-hooks==0.1.5 mkdocs-material-extensions==1.3.1 diff --git a/requirements.txt b/requirements.txt index cd629526eb7..9039a533d6b 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ pip>=24.3.1 packaging>=24.2 setuptools~=70.2;python_version<"3.10" -setuptools>=75.5.0;python_version>="3.10" -wheel>=0.45.0 +setuptools>=75.6.0;python_version>="3.10" +wheel>=0.45.1 attrs>=24.2.0 certifi>=2024.8.30 exceptiongroup>=1.2.2 @@ -37,7 +37,7 @@ trio==0.27.0 trio-websocket==0.11.1 wsproto==1.2.0 websocket-client==1.8.0 -selenium==4.26.1 +selenium==4.27.1 cssselect==1.2.0 sortedcontainers==2.4.0 execnet==2.1.1 @@ -48,7 +48,8 @@ pytest==8.3.3 pytest-html==2.0.1 pytest-metadata==3.1.1 pytest-ordering==0.6 -pytest-rerunfailures==14.0 +pytest-rerunfailures==14.0;python_version<"3.9" +pytest-rerunfailures==15.0;python_version>="3.9" pytest-xdist==3.6.1 parameterized==0.9.0 behave==1.2.6 @@ -64,7 +65,7 @@ rich==13.9.4 # ("pip install -r requirements.txt" also installs this, but "pip install -e ." won't.) coverage>=7.6.1;python_version<"3.9" -coverage>=7.6.7;python_version>="3.9" +coverage>=7.6.8;python_version>="3.9" pytest-cov>=5.0.0;python_version<"3.9" pytest-cov>=6.0.0;python_version>="3.9" flake8==5.0.4;python_version<"3.9" diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index e2bf5cb5ee1..425f1365c70 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "4.32.12" +__version__ = "4.33.0" diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index bf962e98576..5283b6dc75f 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -216,8 +216,10 @@ def extend_driver(driver): driver.get_text = DM.get_text driver.get_active_element_css = DM.get_active_element_css driver.get_locale_code = DM.get_locale_code + driver.get_screen_rect = DM.get_screen_rect driver.get_origin = DM.get_origin driver.get_user_agent = DM.get_user_agent + driver.get_cookie_string = DM.get_cookie_string driver.highlight = DM.highlight driver.highlight_click = DM.highlight_click driver.highlight_if_visible = DM.highlight_if_visible @@ -234,6 +236,7 @@ def extend_driver(driver): driver.switch_to_window = DM.switch_to_window driver.switch_to_tab = DM.switch_to_tab driver.switch_to_frame = DM.switch_to_frame + driver.reset_window_size = DM.reset_window_size if hasattr(driver, "proxy"): driver.set_wire_proxy = DM.set_wire_proxy return driver @@ -2447,10 +2450,8 @@ def _set_firefox_options( firefox_arg_list = firefox_arg.split(",") for firefox_arg_item in firefox_arg_list: firefox_arg_item = firefox_arg_item.strip() - if not firefox_arg_item.startswith("--"): - if firefox_arg_item.startswith("-"): - firefox_arg_item = "-" + firefox_arg_item - else: + if not firefox_arg_item.startswith("-"): + if firefox_arg_item.count(os.sep) == 0: firefox_arg_item = "--" + firefox_arg_item if len(firefox_arg_item) >= 3: options.add_argument(firefox_arg_item) diff --git a/seleniumbase/core/sb_cdp.py b/seleniumbase/core/sb_cdp.py index da22407a648..ace9190bbe7 100644 --- a/seleniumbase/core/sb_cdp.py +++ b/seleniumbase/core/sb_cdp.py @@ -551,7 +551,7 @@ def click_visible_elements(self, selector, limit=0): if (width != 0 or height != 0): element.click() click_count += 1 - time.sleep(0.042) + time.sleep(0.044) self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.wait()) except Exception: @@ -668,10 +668,10 @@ def press_keys(self, selector, text, timeout=settings.SMALL_TIMEOUT): text = text[:-1] for key in text: element.send_keys(key) - time.sleep(0.042) + time.sleep(0.044) if submit: element.send_keys("\r\n") - time.sleep(0.042) + time.sleep(0.044) self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.wait()) @@ -741,7 +741,7 @@ def maximize(self): return elif self.get_window()[1].window_state.value == "minimized": self.loop.run_until_complete(self.page.maximize()) - time.sleep(0.042) + time.sleep(0.044) return self.loop.run_until_complete(self.page.maximize()) def minimize(self): @@ -751,7 +751,7 @@ def minimize(self): def medimize(self): if self.get_window()[1].window_state.value == "minimized": self.loop.run_until_complete(self.page.medimize()) - time.sleep(0.042) + time.sleep(0.044) return self.loop.run_until_complete(self.page.medimize()) def set_window_rect(self, x, y, width, height): @@ -760,7 +760,7 @@ def set_window_rect(self, x, y, width, height): self.page.set_window_size( left=x, top=y, width=width, height=height) ) - time.sleep(0.042) + time.sleep(0.044) return self.loop.run_until_complete( self.page.set_window_size( left=x, top=y, width=width, height=height) @@ -1125,7 +1125,7 @@ def gui_press_key(self, key): ) with gui_lock: pyautogui.press(key) - time.sleep(0.042) + time.sleep(0.044) self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.wait()) @@ -1139,7 +1139,7 @@ def gui_press_keys(self, keys): with gui_lock: for key in keys: pyautogui.press(key) - time.sleep(0.042) + time.sleep(0.044) self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.wait()) diff --git a/seleniumbase/core/sb_driver.py b/seleniumbase/core/sb_driver.py index d01de1639b5..35147607c86 100644 --- a/seleniumbase/core/sb_driver.py +++ b/seleniumbase/core/sb_driver.py @@ -1,6 +1,7 @@ """Add new methods to extend the driver""" from contextlib import suppress from selenium.webdriver.remote.webelement import WebElement +from seleniumbase.config import settings from seleniumbase.fixtures import js_utils from seleniumbase.fixtures import page_actions from seleniumbase.fixtures import page_utils @@ -189,6 +190,8 @@ def is_alert_present(self): return False def is_online(self): + if self.__is_cdp_swap_needed(): + return self.driver.cdp.evaluate("navigator.onLine;") return self.driver.execute_script("return navigator.onLine;") def is_connected(self): @@ -231,17 +234,35 @@ def get_text(self, *args, **kwargs): return page_actions.get_text(self.driver, *args, **kwargs) def get_active_element_css(self, *args, **kwargs): + if self.__is_cdp_swap_needed(): + return self.driver.cdp.get_active_element_css() return js_utils.get_active_element_css(self.driver, *args, **kwargs) def get_locale_code(self, *args, **kwargs): + if self.__is_cdp_swap_needed(): + return self.driver.cdp.get_locale_code() return js_utils.get_locale_code(self.driver, *args, **kwargs) + def get_screen_rect(self, *args, **kwargs): + if self.__is_cdp_swap_needed(): + return self.driver.cdp.get_screen_rect() + return js_utils.get_screen_rect(self.driver, *args, **kwargs) + def get_origin(self, *args, **kwargs): + if self.__is_cdp_swap_needed(): + return self.driver.cdp.get_origin() return js_utils.get_origin(self.driver, *args, **kwargs) def get_user_agent(self, *args, **kwargs): + if self.__is_cdp_swap_needed(): + return self.driver.cdp.get_user_agent() return js_utils.get_user_agent(self.driver, *args, **kwargs) + def get_cookie_string(self, *args, **kwargs): + if self.__is_cdp_swap_needed(): + return self.driver.cdp.get_cookie_string() + return js_utils.get_cookie_string(self.driver, *args, **kwargs) + def highlight(self, *args, **kwargs): if self.__is_cdp_swap_needed(): selector = None @@ -312,6 +333,16 @@ def switch_to_frame(self, frame="iframe"): iframe = self.locator(frame) self.driver.switch_to.frame(iframe) + def reset_window_size(self): + if self.__is_cdp_swap_needed(): + self.driver.cdp.reset_window_size() + return + x = settings.WINDOW_START_X + y = settings.WINDOW_START_Y + width = settings.CHROME_START_WIDTH + height = settings.CHROME_START_HEIGHT + self.driver.set_window_rect(x, y, width, height) + def set_wire_proxy(self, string): """Set a proxy server for selenium-wire mode ("--wire") Examples: (ONLY avilable if using selenium-wire mode!) diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index b4ef0da46fe..a29c9d97819 100644 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -3452,6 +3452,13 @@ def get_gui_element_center(self, selector, by="css selector"): y = element_rect["y"] + (element_rect["height"] / 2.0) + 0.5 return (x, y) + def get_screen_rect(self): + self.__check_scope() + if self.__is_cdp_swap_needed(): + return self.cdp.get_screen_rect() + self._check_browser() + return self.driver.get_screen_rect() + def get_window_rect(self): self.__check_scope() if self.__is_cdp_swap_needed(): @@ -3475,6 +3482,9 @@ def get_window_position(self): def set_window_rect(self, x, y, width, height): self.__check_scope() + if self.__is_cdp_swap_needed(): + self.cdp.set_window_rect(x, y, width, height) + return self._check_browser() self.driver.set_window_rect(x, y, width, height) self.__demo_mode_pause_if_active(tiny=True) @@ -3493,10 +3503,35 @@ def set_window_position(self, x, y): def maximize_window(self): self.__check_scope() + if self.__is_cdp_swap_needed(): + self.cdp.maximize() + return self._check_browser() self.driver.maximize_window() self.__demo_mode_pause_if_active(tiny=True) + def minimize_window(self): + self.__check_scope() + if self.__is_cdp_swap_needed(): + self.cdp.minimize() + return + self._check_browser() + self.driver.minimize_window() + self.__demo_mode_pause_if_active(tiny=True) + + def reset_window_size(self): + self.__check_scope() + if self.__is_cdp_swap_needed(): + self.cdp.reset_window_size() + return + self._check_browser() + x = settings.WINDOW_START_X + y = settings.WINDOW_START_Y + width = settings.CHROME_START_WIDTH + height = settings.CHROME_START_HEIGHT + self.set_window_rect(x, y, width, height) + self.__demo_mode_pause_if_active(tiny=True) + def switch_to_frame(self, frame="iframe", timeout=None): """Wait for an iframe to appear, and switch to it. This should be usable as a drop-in replacement for driver.switch_to.frame(). diff --git a/seleniumbase/fixtures/js_utils.py b/seleniumbase/fixtures/js_utils.py index bf69abbced6..98db57bdb82 100644 --- a/seleniumbase/fixtures/js_utils.py +++ b/seleniumbase/fixtures/js_utils.py @@ -1188,6 +1188,10 @@ def get_locale_code(driver): return driver.execute_script(script) +def get_screen_rect(driver): + return driver.execute_script("return window.screen;") + + def get_origin(driver): return driver.execute_script("return window.location.origin;") @@ -1196,6 +1200,10 @@ def get_user_agent(driver): return driver.execute_script("return navigator.userAgent;") +def get_cookie_string(driver): + return driver.execute_script("return document.cookie;") + + def get_scroll_distance_to_element(driver, element): try: scroll_position = driver.execute_script("return window.scrollY;") diff --git a/seleniumbase/plugins/sb_manager.py b/seleniumbase/plugins/sb_manager.py index 71af2f7cc4d..3c39eca7c15 100644 --- a/seleniumbase/plugins/sb_manager.py +++ b/seleniumbase/plugins/sb_manager.py @@ -275,12 +275,15 @@ def SB( collect_only = ("--co" in sys_argv or "--collect-only" in sys_argv) all_scripts = (hasattr(sb_config, "all_scripts") and sb_config.all_scripts) do_log_folder_setup = False # The first "test=True" run does it + inner_test = False if ( (hasattr(sb_config, "is_behave") and sb_config.is_behave) or (hasattr(sb_config, "is_pytest") and sb_config.is_pytest) or (hasattr(sb_config, "is_nosetest") and sb_config.is_nosetest) ): existing_runner = True + if test: + inner_test = True test = False # Already using a test runner. Skip extra test steps. elif test is None and "--test" in sys_argv: test = True @@ -1222,7 +1225,10 @@ def SB( sb._has_failure = True exception = e test_passed = False - if not test_name: + if (test or inner_test) and not test_name: + print(e) + return + elif not test_name: raise else: the_traceback = traceback.format_exc().strip() diff --git a/setup.py b/setup.py index ca5948598d0..0f6788c33cf 100755 --- a/setup.py +++ b/setup.py @@ -150,8 +150,8 @@ 'pip>=24.3.1', 'packaging>=24.2', 'setuptools~=70.2;python_version<"3.10"', # Newer ones had issues - 'setuptools>=75.5.0;python_version>="3.10"', - 'wheel>=0.45.0', + 'setuptools>=75.6.0;python_version>="3.10"', + 'wheel>=0.45.1', 'attrs>=24.2.0', "certifi>=2024.8.30", "exceptiongroup>=1.2.2", @@ -186,7 +186,7 @@ 'trio-websocket==0.11.1', 'wsproto==1.2.0', 'websocket-client==1.8.0', - 'selenium==4.26.1', + 'selenium==4.27.1', 'cssselect==1.2.0', "sortedcontainers==2.4.0", 'execnet==2.1.1', @@ -197,7 +197,8 @@ "pytest-html==2.0.1", # Newer ones had issues 'pytest-metadata==3.1.1', "pytest-ordering==0.6", - 'pytest-rerunfailures==14.0', + 'pytest-rerunfailures==14.0;python_version<"3.9"', + 'pytest-rerunfailures==15.0;python_version>="3.9"', 'pytest-xdist==3.6.1', 'parameterized==0.9.0', "behave==1.2.6", @@ -222,7 +223,7 @@ # Usage: coverage run -m pytest; coverage html; coverage report "coverage": [ 'coverage>=7.6.1;python_version<"3.9"', - 'coverage>=7.6.7;python_version>="3.9"', + 'coverage>=7.6.8;python_version>="3.9"', 'pytest-cov>=5.0.0;python_version<"3.9"', 'pytest-cov>=6.0.0;python_version>="3.9"', ],