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
```
-
+
> ``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
```
-
+
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 @@
-
+
## [
](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 @@
+
-
+
-
+
-
+
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 @@
-
+
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',