diff --git a/README.md b/README.md index 6f7e53c1c8f..ec897569ec5 100755 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ pytest my_first_test.py ``` -SeleniumBase Test +SeleniumBase Test > ``pytest`` uses ``--chrome`` by default unless set differently. @@ -361,10 +361,10 @@ COMMANDS: ▶️ Here's sample output from a chromedriver download. (click to expand) ```bash -*** chromedriver to download = 116.0.5845.96 (Latest Stable) +*** chromedriver to download = 121.0.6167.85 (Latest Stable) Downloading chromedriver-mac-arm64.zip from: -https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/116.0.5845.96/mac-arm64/chromedriver-mac-arm64.zip ... +https://storage.googleapis.com/chrome-for-testing-public/121.0.6167.85/mac-arm64/chromedriver-mac-arm64.zip ... Download Complete! Extracting ['chromedriver'] from chromedriver-mac-arm64.zip ... @@ -373,8 +373,8 @@ Unzip Complete! The file [chromedriver] was saved to: /Users/michael/github/SeleniumBase/seleniumbase/drivers/chromedriver -Making [chromedriver 116.0.5845.96] executable ... -[chromedriver 116.0.5845.96] is now ready for use! +Making [chromedriver 121.0.6167.85] executable ... +[chromedriver 121.0.6167.85] is now ready for use! ``` @@ -392,7 +392,7 @@ cd examples/ pytest my_first_test.py ``` -SeleniumBase Test +SeleniumBase Test

Here's the code for my_first_test.py:

diff --git a/examples/dialog_boxes/dialog_box_tour.py b/examples/dialog_boxes/dialog_box_tour.py index 4785511714c..2e061732cc7 100644 --- a/examples/dialog_boxes/dialog_box_tour.py +++ b/examples/dialog_boxes/dialog_box_tour.py @@ -87,7 +87,10 @@ def test_dialog_boxes(self): self.open("https://seleniumbase.io/help_docs/ReadMe/") self.highlight("h1") - self.highlight_click('a:contains("Running Example Tests")') + self.slow_scroll_to('article p a[href*="/examples/ReadMe/"]') + zoom_in = 'article p a[href*="/examples/ReadMe/"]{zoom: 1.8;}' + self.add_css_style(zoom_in) + self.highlight_click('article p a[href*="/examples/ReadMe/"]') self.highlight("h1") self.set_jqc_theme("bootstrap", color="green", width="52%") diff --git a/examples/migration/raw_selenium/ReadMe.md b/examples/migration/raw_selenium/ReadMe.md index 57a5459fd21..904d843bcf3 100644 --- a/examples/migration/raw_selenium/ReadMe.md +++ b/examples/migration/raw_selenium/ReadMe.md @@ -70,9 +70,9 @@ With raw Selenium, that requires more code:

💡 SeleniumBase has its own Recorder / Test Generator for creating tests from manual browser actions.

-

💡 SeleniumBase comes with test case management software, ("CasePlans"), for organizing tests and step descriptions.

+

💡 SeleniumBase comes with test case management software, ("Case Plans"), for organizing tests and step descriptions.

-

💡 SeleniumBase includes tools for building data apps, ("ChartMaker"), which can generate JavaScript from Python.

+

💡 SeleniumBase includes tools for building data apps, ("Chart Maker"), which can generate JavaScript from Python.

diff --git a/help_docs/ReadMe.md b/help_docs/ReadMe.md index 885c2f9a54b..228431ef4da 100644 --- a/help_docs/ReadMe.md +++ b/help_docs/ReadMe.md @@ -1,6 +1,6 @@ -

SeleniumBase

+

SeleniumBase

## [](https://github.com/seleniumbase/SeleniumBase/) Help Docs @@ -33,10 +33,13 @@ 🛂 Dialog Boxes
🔴 Recorder | -🚝 Migrate +💻 Device Farm
🎞️ Slides | -📶 Charts +📶 Chart Maker +
+🎖️ GUI | +👤 UC Mode

-------- @@ -57,19 +60,20 @@
The Dashboard
Recorder Mode
pytest Commander
+
Method Summary
Syntax Formats
Behave BDD
Behave Commander
Mobile Device Testing
-
Method Summary (API Ref)
Case Plans
+
Chart Maker
Language Translations
Language Locale Codes
JS Package Manager
-
Tour Examples
+
Tour Maker
Presentation Maker
-
Chart Maker
Handling iframes
+
Undetected Mode (UC Mode)
MySQL Installation Overview
Using the Selenium Grid
Browser Desired Capabilities
diff --git a/mkdocs_build/requirements.txt b/mkdocs_build/requirements.txt index ba2fb5034e6..624886a6dd9 100644 --- a/mkdocs_build/requirements.txt +++ b/mkdocs_build/requirements.txt @@ -3,7 +3,7 @@ regex>=2023.12.25 pymdown-extensions>=10.7 -pipdeptree>=2.13.2 +pipdeptree>=2.14.0 python-dateutil>=2.8.2 Markdown==3.5.2 markdown2==2.4.12 diff --git a/requirements.txt b/requirements.txt index ee49877abdb..97bfbf5f14f 100755 --- a/requirements.txt +++ b/requirements.txt @@ -42,7 +42,7 @@ pytest==7.4.4;python_version<"3.8" pytest==8.0.0;python_version>="3.8" pytest-html==2.0.1 pytest-metadata==3.0.0;python_version<"3.8" -pytest-metadata==3.1.0;python_version>="3.8" +pytest-metadata==3.1.1;python_version>="3.8" pytest-ordering==0.6 pytest-rerunfailures==13.0 pytest-xdist==3.5.0 diff --git a/seleniumbase/ReadMe.md b/seleniumbase/ReadMe.md index 2b8f160fb62..a4827dc9a99 100644 --- a/seleniumbase/ReadMe.md +++ b/seleniumbase/ReadMe.md @@ -1,4 +1,4 @@ -SeleniumBase +SeleniumBase

Framework Folders

diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index 6e641d13521..361c8bdcbf2 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "4.23.3" +__version__ = "4.23.4" diff --git a/seleniumbase/behave/behave_sb.py b/seleniumbase/behave/behave_sb.py index c3208df0023..bd54740ee1f 100644 --- a/seleniumbase/behave/behave_sb.py +++ b/seleniumbase/behave/behave_sb.py @@ -367,7 +367,7 @@ def get_configured_sb(context): # Handle: -D variables="{'KEY':'VALUE','KEY2':'VALUE2'}" if low_key == "variables": variables = userdata[key] - if variables and type(variables) is str and len(variables) > 0: + if variables and isinstance(variables, str) and len(variables) > 0: bad_input = False if ( not variables.startswith("{") @@ -377,7 +377,7 @@ def get_configured_sb(context): else: try: variables = ast.literal_eval(variables) - if not type(variables) is dict: + if not isinstance(variables, dict): bad_input = True except Exception: bad_input = True diff --git a/seleniumbase/console_scripts/sb_install.py b/seleniumbase/console_scripts/sb_install.py index 7cc890d6fb9..2ee596cdaf2 100644 --- a/seleniumbase/console_scripts/sb_install.py +++ b/seleniumbase/console_scripts/sb_install.py @@ -498,6 +498,12 @@ def main(override=None, intel_for_uc=None, force_uc=None): "https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/" "%s/%s/%s" % (use_version, platform_code, file_name) ) + major_ch_ver = use_version.split(".")[0] + if major_ch_ver.isnumeric() and int(major_ch_ver) >= 121: + download_url = ( + "https://storage.googleapis.com/chrome-for-testing-public/" + "%s/%s/%s" % (use_version, platform_code, file_name) + ) url_request = None if not found_chromedriver: url_req = requests_get(last) diff --git a/seleniumbase/console_scripts/sb_print.py b/seleniumbase/console_scripts/sb_print.py index a4f6e7c47d1..7df43817acb 100644 --- a/seleniumbase/console_scripts/sb_print.py +++ b/seleniumbase/console_scripts/sb_print.py @@ -213,7 +213,7 @@ def main(): first_paren = line.find("(") line1 = line[:first_paren + 1] line2 = new_ws + line[first_paren + 1:] - if not ("):") in line2: + if ("):") not in line2: new_sb_lines.append(line1) if get_width(line2) + w > console_width: if line2.count('", "') == 1: @@ -353,7 +353,7 @@ def main(): new_ws = line[0:whitespace] + " " line1 = line.split('("')[0] + "(" line2 = new_ws + '"' + line.split('("')[1] - if not ("):") in line2: + if ("):") not in line2: new_sb_lines.append(line1) if get_width(line2) + w > console_width: if line2.count('" in self.') == 1: @@ -382,7 +382,7 @@ def main(): new_ws = line[0:whitespace] + " " line1 = line.split("('")[0] + "(" line2 = new_ws + "'" + line.split("('")[1] - if not ("):") in line2: + if ("):") not in line2: new_sb_lines.append(line1) if get_width(line2) + w > console_width: if line2.count("' in self.") == 1: @@ -444,7 +444,7 @@ def main(): continue new_sb_lines.append(line2) continue - if line.count("(self.") == 1 and not ("):") in line: + if line.count("(self.") == 1 and ("):") not in line: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split("(self.")[0] + "(" @@ -500,7 +500,7 @@ def main(): else: new_sb_lines.append(line2) continue - if line.count(" % ") == 1 and not ("):") in line: + if line.count(" % ") == 1 and ("):") not in line: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split(" % ")[0] + " \\" @@ -509,7 +509,7 @@ def main(): new_sb_lines.append(line1) new_sb_lines.append(line2) continue - if line.count(" = ") == 1 and not (" # ") in line: + if line.count(" = ") == 1 and (" # ") not in line: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split(" = ")[0] + " = (" diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index 218bebcda7a..d48626c8781 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -361,9 +361,9 @@ def uc_special_open_if_cf( driver.switch_to.window(driver.window_handles[-1]) uc_metrics = {} if ( - type(device_width) is int - and type(device_height) is int - and type(device_pixel_ratio) is int + isinstance(device_width, int) + and isinstance(device_height, int) + and isinstance(device_pixel_ratio, int) ): uc_metrics["width"] = device_width uc_metrics["height"] = device_height @@ -847,9 +847,9 @@ def _set_chrome_options( emulator_settings = {} device_metrics = {} if ( - type(device_width) is int - and type(device_height) is int - and type(device_pixel_ratio) is int + isinstance(device_width, int) + and isinstance(device_height, int) + and isinstance(device_pixel_ratio, int) ): device_metrics["width"] = device_width device_metrics["height"] = device_height @@ -1389,7 +1389,7 @@ def get_driver( headless = True if ( binary_location - and type(binary_location) is str + and isinstance(binary_location, str) and ( browser_name == constants.Browser.GOOGLE_CHROME or browser_name == constants.Browser.EDGE @@ -2553,9 +2553,9 @@ def get_local_driver( emulator_settings = {} device_metrics = {} if ( - type(device_width) is int - and type(device_height) is int - and type(device_pixel_ratio) is int + isinstance(device_width, int) + and isinstance(device_height, int) + and isinstance(device_pixel_ratio, int) ): device_metrics["width"] = device_width device_metrics["height"] = device_height @@ -3703,9 +3703,9 @@ def get_local_driver( if mobile_emulator: uc_metrics = {} if ( - type(device_width) is int - and type(device_height) is int - and type(device_pixel_ratio) is int + isinstance(device_width, int) + and isinstance(device_height, int) + and isinstance(device_pixel_ratio, int) ): uc_metrics["width"] = device_width uc_metrics["height"] = device_height diff --git a/seleniumbase/core/jqc_helper.py b/seleniumbase/core/jqc_helper.py index 79fe5554c00..49bcf8cc139 100644 --- a/seleniumbase/core/jqc_helper.py +++ b/seleniumbase/core/jqc_helper.py @@ -118,9 +118,7 @@ def jquery_confirm_text_dialog(driver, message, button=None, options=None): if not message: message = "" if button: - if not type(button) is list and not type(button) is tuple: - raise Exception('"button" should be a (text, color) tuple!') - if len(button) != 2: + if not isinstance(button, (list, tuple)) or len(button) != 2: raise Exception('"button" should be a (text, color) tuple!') else: button = ("Submit", "blue") diff --git a/seleniumbase/extensions/recorder.zip b/seleniumbase/extensions/recorder.zip index eaaecaaa4f5..dbae7aedf1f 100644 Binary files a/seleniumbase/extensions/recorder.zip and b/seleniumbase/extensions/recorder.zip differ diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index 1b9d1ab5d3f..c9204a82ad8 100644 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -951,6 +951,10 @@ def update_text( self.wait_for_ready_state_complete() else: element.send_keys(text[:-1]) + if self.slow_mode or self.demo_mode: + self.__demo_mode_pause_if_active(tiny=True) + else: + time.sleep(0.0135) try: element.send_keys(Keys.RETURN) except WebDriverException as e: @@ -976,6 +980,10 @@ def update_text( element.send_keys(text) else: element.send_keys(text[:-1]) + if self.slow_mode or self.demo_mode: + self.__demo_mode_pause_if_active(tiny=True) + else: + time.sleep(0.0135) try: element.send_keys(Keys.RETURN) except WebDriverException as e: @@ -1054,6 +1062,10 @@ def add_text(self, selector, text, by="css selector", timeout=None): element.send_keys(text) else: element.send_keys(text[:-1]) + if self.slow_mode or self.demo_mode: + self.__demo_mode_pause_if_active(tiny=True) + else: + time.sleep(0.0135) element.send_keys(Keys.RETURN) if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: self.wait_for_ready_state_complete() @@ -1067,6 +1079,10 @@ def add_text(self, selector, text, by="css selector", timeout=None): element.send_keys(text) else: element.send_keys(text[:-1]) + if self.slow_mode or self.demo_mode: + self.__demo_mode_pause_if_active(tiny=True) + else: + time.sleep(0.0135) element.send_keys(Keys.RETURN) if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: self.wait_for_ready_state_complete() @@ -1143,6 +1159,10 @@ def press_keys(self, selector, text, by="css selector", timeout=None): for key in text: element.send_keys(key) if press_enter: + if self.slow_mode or self.demo_mode: + self.__demo_mode_pause_if_active(tiny=True) + else: + time.sleep(0.0135) element.send_keys(Keys.RETURN) if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: if not self.undetectable: @@ -1348,7 +1368,7 @@ def open_start_page(self): If the start_page is not set, then "about:blank" will be used.""" self.__check_scope() start_page = self.start_page - if type(start_page) is str: + if isinstance(start_page, str): start_page = start_page.strip() # Remove extra whitespace if start_page and len(start_page) >= 4: if page_utils.is_valid_url(start_page): @@ -3398,7 +3418,7 @@ def switch_to_frame(self, frame, timeout=None): time.sleep(0.05) if self.undetectable: time.sleep(0.05) - if type(frame) is str and self.is_element_visible(frame): + if isinstance(frame, str) and self.is_element_visible(frame): try: self.scroll_to(frame, timeout=1) if self.__needs_minimum_wait(): @@ -6511,7 +6531,7 @@ def assert_no_404_errors(self, multithreaded=True, timeout=None): ): links.append(link) if timeout: - if not type(timeout) is int and not type(timeout) is float: + if not isinstance(timeout, (int, float)): raise Exception('Expecting a numeric value for "timeout"!') if timeout < 0: raise Exception('The "timeout" cannot be a negative number!') @@ -6575,21 +6595,21 @@ def __get_type_checked_text(self, text): """Do type-checking on text. Then return it when valid. If the text is acceptable, return the text or str(text). If the text is not acceptable, raise a Python Exception.""" - if type(text) is str: + if isinstance(text, str): return text - elif type(text) is int or type(text) is float: + elif isinstance(text, (int, float)): return str(text) # Convert num to string - elif type(text) is bool: + elif isinstance(text, bool): raise Exception("text must be a string! Boolean found!") elif type(text).__name__ == "NoneType": raise Exception("text must be a string! NoneType found!") - elif type(text) is list: + elif isinstance(text, list): raise Exception("text must be a string! List found!") - elif type(text) is tuple: + elif isinstance(text, tuple): raise Exception("text must be a string! Tuple found!") - elif type(text) is set: + elif isinstance(text, set): raise Exception("text must be a string! Set found!") - elif type(text) is dict: + elif isinstance(text, dict): raise Exception("text must be a string! Dict found!") else: return str(text) @@ -6685,12 +6705,12 @@ def get_pdf_text( raise Exception("%s is not a valid URL or file path!" % pdf) file_path = os.path.abspath(pdf) page_search = None # (Pages are delimited by '\x0c') - if type(page) is list: + if isinstance(page, list): pages = page page_search = [] for page in pages: page_search.append(page - 1) - elif type(page) is int: + elif isinstance(page, int): page = page - 1 if page < 0: page = 0 @@ -6761,7 +6781,7 @@ def assert_pdf_text( override=override, caching=caching, ) - if type(page) is int: + if isinstance(page, int): if text not in pdf_text: self.fail( "PDF [%s] is missing expected text [%s] on " @@ -6835,7 +6855,7 @@ def choose_file( sele_file_path = [selector, file_path] action = ["chfil", sele_file_path, origin, time_stamp] self.__extra_actions.append(action) - if type(abs_path) is int or type(abs_path) is float: + if isinstance(abs_path, (int, float)): abs_path = str(abs_path) try: if self.browser == "safari": @@ -6892,7 +6912,7 @@ def save_element_as_image_file( with open(image_file_path, "wb") as file: file.write(element_png) # Add a text overlay if given - if type(overlay_text) is str and len(overlay_text) > 0: + if isinstance(overlay_text, str) and len(overlay_text) > 0: pip_find_lock = fasteners.InterProcessLock( constants.PipInstall.FINDLOCK ) @@ -7579,11 +7599,7 @@ def assert_no_js_errors(self, exclude=[]): self.assert_no_js_errors(exclude=["Uncaught SyntaxError"]) self.assert_no_js_errors(exclude=["TypeError", "SyntaxE"]) """ self.__check_scope() - if ( - exclude - and not type(exclude) is list - and not type(exclude) is tuple - ): + if exclude and not isinstance(exclude, (list, tuple)): exclude = str(exclude).replace(" ", "").split(",") time.sleep(0.1) # May take a moment for errors to appear after loads. try: @@ -8198,7 +8214,7 @@ def set_default_timeout(self, timeout): The maximum allowable default timeout is: 60.0 seconds. (Test methods can still override timeouts outside that range.)""" self.__check_scope() - if not type(timeout) is int and not type(timeout) is float: + if not isinstance(timeout, (int, float)): raise Exception('Expecting a numeric value for "timeout"!') if timeout < 0: raise Exception('The "timeout" cannot be a negative number!') @@ -8758,7 +8774,7 @@ def _print(self, msg): To print without the new-line character end, use: "sys.stdout.write()". """ if hasattr(sb_config, "_multithreaded") and sb_config._multithreaded: - if type(msg) is not str: + if not isinstance(msg, str): try: msg = str(msg) except Exception: @@ -8992,7 +9008,7 @@ def generate_referral_chain(self, pages): generation without increasing the bounce rate, you'll want to visit at least one additional page on that site with a button click.)""" self.__check_scope() - if not type(pages) is tuple and not type(pages) is list: + if not isinstance(pages, (list, tuple)): raise Exception( "Exception: Expecting a list of website pages for chaining!" ) @@ -9101,7 +9117,7 @@ def assert_element_present( timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) - if type(selector) is list: + if isinstance(selector, list): self.assert_elements_present(selector, by=by, timeout=timeout) return True if self.__is_shadow_selector(selector): @@ -9137,12 +9153,12 @@ def assert_elements_present(self, *args, **kwargs): by = kwargs["by"] elif kwarg == "selector": selector = kwargs["selector"] - if type(selector) is str: + if isinstance(selector, str): selectors.append(selector) - elif type(selector) is list: + elif isinstance(selector, list): selectors_list = selector for selector in selectors_list: - if type(selector) is str: + if isinstance(selector, str): selectors.append(selector) else: raise Exception('Unknown kwarg: "%s"!' % kwarg) @@ -9151,11 +9167,11 @@ def assert_elements_present(self, *args, **kwargs): if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) for arg in args: - if type(arg) is list: + if isinstance(arg, list): for selector in arg: - if type(selector) is str: + if isinstance(selector, str): selectors.append(selector) - elif type(arg) is str: + elif isinstance(arg, str): selectors.append(arg) for selector in selectors: if self.__is_shadow_selector(selector): @@ -9178,7 +9194,7 @@ def assert_element(self, selector, by="css selector", timeout=None): timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) - if type(selector) is list: + if isinstance(selector, list): self.assert_elements(selector, by=by, timeout=timeout) return True if self.__is_shadow_selector(selector): @@ -9240,12 +9256,12 @@ def assert_elements(self, *args, **kwargs): by = kwargs["by"] elif kwarg == "selector": selector = kwargs["selector"] - if type(selector) is str: + if isinstance(selector, str): selectors.append(selector) - elif type(selector) is list: + elif isinstance(selector, list): selectors_list = selector for selector in selectors_list: - if type(selector) is str: + if isinstance(selector, str): selectors.append(selector) else: raise Exception('Unknown kwarg: "%s"!' % kwarg) @@ -9254,11 +9270,11 @@ def assert_elements(self, *args, **kwargs): if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) for arg in args: - if type(arg) is list: + if isinstance(arg, list): for selector in arg: - if type(selector) is str: + if isinstance(selector, str): selectors.append(selector) - elif type(arg) is str: + elif isinstance(arg, str): selectors.append(arg) for selector in selectors: if self.__is_shadow_selector(selector): @@ -11099,7 +11115,7 @@ def save_presentation( interval = 0 if interval == 0 and self.interval: interval = float(self.interval) - if not type(interval) is int and not type(interval) is float: + if not isinstance(interval, (int, float)): raise Exception('Expecting a numeric value for "interval"!') if interval < 0: raise Exception('The "interval" cannot be a negative number!') @@ -11188,7 +11204,7 @@ def begin_presentation( interval = 0 if interval == 0 and self.interval: interval = float(self.interval) - if not type(interval) is int and not type(interval) is float: + if not isinstance(interval, (int, float)): raise Exception('Expecting a numeric value for "interval"!') if interval < 0: raise Exception('The "interval" cannot be a negative number!') @@ -11767,7 +11783,7 @@ def add_data_point(self, label, value, color=None, chart_name=None): self.create_pie_chart(chart_name=chart_name) if not value: value = 0 - if not type(value) is int and not type(value) is float: + if not isinstance(value, (int, float)): raise Exception('Expecting a numeric value for "value"!') if not color: color = "" @@ -11878,7 +11894,7 @@ def display_chart(self, chart_name=None, filename=None, interval=0): interval = 0 if interval == 0 and self.interval: interval = float(self.interval) - if not type(interval) is int and not type(interval) is float: + if not isinstance(interval, (int, float)): raise Exception('Expecting a numeric value for "interval"!') if interval < 0: raise Exception('The "interval" cannot be a negative number!') @@ -12644,7 +12660,7 @@ def set_jqc_theme(self, theme, color=None, width=None): ) constants.JqueryConfirm.DEFAULT_COLOR = color.lower() if width: - if type(width) is int or type(width) is float: + if isinstance(width, (int, float)): # Convert to a string if a number is given width = str(width) if width.isnumeric(): @@ -12699,30 +12715,24 @@ def get_jqc_button_input(self, message, buttons, options=None): Width can be set using percent or pixels. Eg: "36.0%", "450px".""" from seleniumbase.core import jqc_helper - if message and type(message) is not str: + if message and not isinstance(message, str): raise Exception('Expecting a string for arg: "message"!') - if not type(buttons) is list and not type(buttons) is tuple: + if not isinstance(buttons, (list, tuple)): raise Exception('Expecting a list or tuple for arg: "button"!') if len(buttons) < 1: raise Exception('List "buttons" requires at least one button!') new_buttons = [] for button in buttons: - if ( - (type(button) is list or type(button) is tuple) - and (len(button) == 1) - ): + if isinstance(button, (list, tuple)) and (len(button) == 1): new_buttons.append(button[0]) - elif ( - (type(button) is list or type(button) is tuple) - and (len(button) > 1) - ): + elif isinstance(button, (list, tuple)) and (len(button) > 1): new_buttons.append((button[0], str(button[1]).lower())) else: new_buttons.append((str(button), "")) buttons = new_buttons if options: for option in options: - if not type(option) is list and not type(option) is tuple: + if not isinstance(option, (list, tuple)): raise Exception('"options" should be a list of tuples!') if self.headless or self.headless2 or self.xvfb: return buttons[-1][0] @@ -12769,18 +12779,12 @@ def get_jqc_text_input(self, message, button=None, options=None): Width can be set using percent or pixels. Eg: "36.0%", "450px".""" from seleniumbase.core import jqc_helper - if message and type(message) is not str: + if message and not isinstance(message, str): raise Exception('Expecting a string for arg: "message"!') if button: - if ( - (type(button) is list or type(button) is tuple) - and (len(button) == 1) - ): + if isinstance(button, (list, tuple)) and (len(button) == 1): button = (str(button[0]), "") - elif ( - (type(button) is list or type(button) is tuple) - and (len(button) > 1) - ): + elif isinstance(button, (list, tuple)) and (len(button) > 1): valid_colors = [ "blue", "default", @@ -12803,7 +12807,7 @@ def get_jqc_text_input(self, message, button=None, options=None): button = ("Submit", "blue") if options: for option in options: - if not type(option) is list and not type(option) is tuple: + if not isinstance(option, (list, tuple)): raise Exception('"options" should be a list of tuples!') if self.headless or self.headless2 or self.xvfb: return "" @@ -12850,30 +12854,24 @@ def get_jqc_form_inputs(self, message, buttons, options=None): Width can be set using percent or pixels. Eg: "36.0%", "450px".""" from seleniumbase.core import jqc_helper - if message and type(message) is not str: + if message and not isinstance(message, str): raise Exception('Expecting a string for arg: "message"!') - if not type(buttons) is list and not type(buttons) is tuple: + if not isinstance(buttons, (list, tuple)): raise Exception('Expecting a list or tuple for arg: "button"!') if len(buttons) < 1: raise Exception('List "buttons" requires at least one button!') new_buttons = [] for button in buttons: - if ( - (type(button) is list or type(button) is tuple) - and (len(button) == 1) - ): + if isinstance(button, (list, tuple)) and (len(button) == 1): new_buttons.append(button[0]) - elif ( - (type(button) is list or type(button) is tuple) - and (len(button) > 1) - ): + elif isinstance(button, (list, tuple)) and (len(button) > 1): new_buttons.append((button[0], str(button[1]).lower())) else: new_buttons.append((str(button), "")) buttons = new_buttons if options: for option in options: - if not type(option) is list and not type(option) is tuple: + if not isinstance(option, (list, tuple)): raise Exception('"options" should be a list of tuples!') if self.headless or self.headless2 or self.xvfb: return ("", buttons[-1][0]) @@ -14224,7 +14222,7 @@ def setUp(self, masterqa_mode=False): self.var2 = sb_config.var2 self.var3 = sb_config.var3 variables = sb_config.variables - if variables and type(variables) is str and len(variables) > 0: + if variables and isinstance(variables, str) and len(variables) > 0: import ast bad_input = False @@ -14236,7 +14234,7 @@ def setUp(self, masterqa_mode=False): else: try: variables = ast.literal_eval(variables) - if not type(variables) is dict: + if not isinstance(variables, dict): bad_input = True except Exception: bad_input = True @@ -14245,7 +14243,7 @@ def setUp(self, masterqa_mode=False): '\nExpecting a Python dictionary for "variables"!' "\nEg. --variables=\"{'KEY1':'VALUE', 'KEY2':123}\"" ) - elif type(variables) is dict: + elif isinstance(variables, dict): pass # Already processed else: variables = {} diff --git a/seleniumbase/fixtures/constants.py b/seleniumbase/fixtures/constants.py index 42039222bd8..350ffb64ae0 100644 --- a/seleniumbase/fixtures/constants.py +++ b/seleniumbase/fixtures/constants.py @@ -409,6 +409,8 @@ class ValidBinaries: "Google Chrome", "Chromium", "Google Chrome for Testing", + "Google Chrome Beta", + "Google Chrome Dev", "Brave Browser", "Opera", ] diff --git a/seleniumbase/fixtures/page_actions.py b/seleniumbase/fixtures/page_actions.py index 56633e45e13..877874fae07 100644 --- a/seleniumbase/fixtures/page_actions.py +++ b/seleniumbase/fixtures/page_actions.py @@ -1412,7 +1412,7 @@ def switch_to_frame(driver, frame, timeout=settings.SMALL_TIMEOUT): driver.switch_to.frame(frame) return True except Exception: - if type(frame) is str: + if isinstance(frame, str): by = None if page_utils.is_xpath_selector(frame): by = "xpath" diff --git a/seleniumbase/fixtures/shared_utils.py b/seleniumbase/fixtures/shared_utils.py index 19ccfa198a4..888e88cbb29 100644 --- a/seleniumbase/fixtures/shared_utils.py +++ b/seleniumbase/fixtures/shared_utils.py @@ -120,7 +120,7 @@ def format_exc(exception, message): exc = exceptions.NoSuchOptionException elif exception == "NoSuchOptionException": exc = exceptions.NoSuchOptionException - elif type(exception) is str: + elif isinstance(exception, str): exc = Exception message = "%s: %s" % (exception, message) return exc, message diff --git a/seleniumbase/js_code/active_css_js.py b/seleniumbase/js_code/active_css_js.py index 04ef165e86a..3a61770cafa 100644 --- a/seleniumbase/js_code/active_css_js.py +++ b/seleniumbase/js_code/active_css_js.py @@ -179,9 +179,8 @@ if (!selector_by_attr[i].includes(' > ') && ((num_by_attr[i] == 1) || (el == all_by_attr[i][0]))) { - if (n_i_attr == 'aria-label' || n_i_attr == 'for') - if (hasDigit(selector_by_attr[i])) - continue; + if (n_i_attr.startsWith('aria') || n_i_attr == 'for') + if (hasDigit(selector_by_attr[i])) continue; return selector_by_attr[i]; } child_count_by_attr[i] = ssOccurrences(selector_by_attr[i], ' > '); diff --git a/seleniumbase/js_code/recorder_js.py b/seleniumbase/js_code/recorder_js.py index 66405a6228c..67fe5c7af48 100644 --- a/seleniumbase/js_code/recorder_js.py +++ b/seleniumbase/js_code/recorder_js.py @@ -179,9 +179,8 @@ if (!selector_by_attr[i].includes(' > ') && ((num_by_attr[i] == 1) || (el == all_by_attr[i][0]))) { - if (n_i_attr == 'aria-label' || n_i_attr == 'for') - if (hasDigit(selector_by_attr[i])) - continue; + if (n_i_attr.startsWith('aria') || n_i_attr == 'for') + if (hasDigit(selector_by_attr[i])) continue; return selector_by_attr[i]; } child_count_by_attr[i] = ssOccurrences(selector_by_attr[i], ' > '); diff --git a/seleniumbase/plugins/base_plugin.py b/seleniumbase/plugins/base_plugin.py index 4816d235d0c..61b6da01afe 100644 --- a/seleniumbase/plugins/base_plugin.py +++ b/seleniumbase/plugins/base_plugin.py @@ -219,14 +219,14 @@ def configure(self, options, conf): def beforeTest(self, test): sb_config._context_of_runner = False # Context Manager Compatibility variables = self.options.variables - if variables and type(variables) is str and len(variables) > 0: + if variables and isinstance(variables, str) and len(variables) > 0: bad_input = False if not variables.startswith("{") or not variables.endswith("}"): bad_input = True else: try: variables = ast.literal_eval(variables) - if not type(variables) is dict: + if not isinstance(variables, dict): bad_input = True except Exception: bad_input = True diff --git a/seleniumbase/plugins/sb_manager.py b/seleniumbase/plugins/sb_manager.py index 2fdcbd007b0..911de408dd8 100644 --- a/seleniumbase/plugins/sb_manager.py +++ b/seleniumbase/plugins/sb_manager.py @@ -409,7 +409,7 @@ def SB( sb_config.proxy_driver = False if "--proxy-driver" in sys_argv or "--proxy_driver" in sys_argv: sb_config.proxy_driver = True - if variables and type(variables) is str and len(variables) > 0: + if variables and isinstance(variables, str) and len(variables) > 0: import ast bad_input = False if ( @@ -420,7 +420,7 @@ def SB( else: try: variables = ast.literal_eval(variables) - if not type(variables) is dict: + if not isinstance(variables, dict): bad_input = True except Exception: bad_input = True diff --git a/seleniumbase/translate/translator.py b/seleniumbase/translate/translator.py index b52959af2c6..67e87167272 100644 --- a/seleniumbase/translate/translator.py +++ b/seleniumbase/translate/translator.py @@ -635,7 +635,7 @@ def main(): first_paren = line.find("(") line1 = line[:first_paren + 1] line2 = new_ws + line[first_paren + 1:] - if not ("):") in line2: + if ("):") not in line2: new_sb_lines.append(line1) if get_width(line2) + w > console_width: if line2.count('", "') == 1: @@ -773,7 +773,7 @@ def main(): new_ws = line[0:whitespace] + " " line1 = line.split('("')[0] + "(" line2 = new_ws + '"' + line.split('("')[1] - if not ("):") in line2: + if ("):") not in line2: new_sb_lines.append(line1) if get_width(line2) + w > console_width: if line2.count('" in self.') == 1: @@ -802,7 +802,7 @@ def main(): new_ws = line[0:whitespace] + " " line1 = line.split("('")[0] + "(" line2 = new_ws + "'" + line.split("('")[1] - if not ("):") in line2: + if ("):") not in line2: new_sb_lines.append(line1) if get_width(line2) + w > console_width: if line2.count("' in self.") == 1: @@ -864,7 +864,7 @@ def main(): continue new_sb_lines.append(line2) continue - if line.count("(self.") == 1 and not ("):") in line: + if line.count("(self.") == 1 and ("):") not in line: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split("(self.")[0] + "(" @@ -920,7 +920,7 @@ def main(): else: new_sb_lines.append(line2) continue - if line.count(" % ") == 1 and not ("):") in line: + if line.count(" % ") == 1 and ("):") not in line: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split(" % ")[0] + " \\" @@ -929,7 +929,7 @@ def main(): new_sb_lines.append(line1) new_sb_lines.append(line2) continue - if line.count(" = ") == 1 and not (" # ") in line: + if line.count(" = ") == 1 and (" # ") not in line: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split(" = ")[0] + " = (" diff --git a/setup.py b/setup.py index 6df3b8e2e88..47c81c58863 100755 --- a/setup.py +++ b/setup.py @@ -189,7 +189,7 @@ 'pytest==8.0.0;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.0;python_version>="3.8"', + 'pytest-metadata==3.1.1;python_version>="3.8"', "pytest-ordering==0.6", 'pytest-rerunfailures==13.0', 'pytest-xdist==3.5.0',