Add special options for setting the binary location

This commit is contained in:
Michael Mintz 2025-01-24 17:41:58 -05:00
parent a44a1d5395
commit 5dc8143fb0
7 changed files with 250 additions and 3 deletions

View File

@ -449,6 +449,27 @@ Note that different options could lead to the same result. (Eg. If you have the
--------
<h3><img src="https://seleniumbase.github.io/img/green_logo.png" title="SeleniumBase" width="32" /> Setting the binary location:</h3>
🔵 By default, SeleniumBase uses the browser binary detected on the System PATH.
🎛️ To change this default behavior, you can use:
```bash
pytest --binary-location=PATH
```
The `PATH` in `--binary-location=PATH` / `--bl=PATH` can be:
* A relative or exact path to the browser binary.
* `"cft"` as a special option for `Chrome for Testing`.
* `"chs"` as a special option for `Chrome-Headless-Shell`.
Before using the `"cft"` / `"chs"` options, call `sbase get cft` / `sbase get chs` in order to download the specified binaries into the `seleniumbase/drivers` folder. The default version is the latest stable version on https://googlechromelabs.github.io/chrome-for-testing/. You can change that by specifying the arg as a parameter. (Eg. `sbase get cft 131`, `sbase get chs 132`, etc.)
With the `SB()` and `Driver()` formats, the binary location is set via the `binary_location` parameter.
--------
<h3><img src="https://seleniumbase.github.io/img/green_logo.png" title="SeleniumBase" width="32" /> Customizing default settings:</h3>
🎛️ An easy way to override [seleniumbase/config/settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py) is by using a custom settings file.

View File

@ -482,8 +482,8 @@ def get_configured_sb(context):
extension_dir = sb.extension_dir # revert to default
sb.extension_dir = extension_dir
continue
# Handle: -D binary-location=PATH / binary_location=PATH
if low_key in ["binary-location", "binary_location"]:
# Handle: -D binary-location=PATH / binary_location=PATH / bl=PATH
if low_key in ["binary-location", "binary_location", "bl"]:
binary_location = userdata[key]
if binary_location == "true":
binary_location = sb.binary_location # revert to default
@ -884,6 +884,14 @@ def get_configured_sb(context):
sb.headless = True # Firefox has regular headless
elif sb.browser not in ["chrome", "edge"]:
sb.headless2 = False # Only for Chromium browsers
if (
sb.binary_location
and sb.binary_location.lower() == "chs"
and sb.browser == "chrome"
):
sb.headless = True
sb.headless1 = False
sb.headless2 = False
# Recorder Mode only supports Chromium browsers.
if sb.recorder_ext and (sb.browser not in ["chrome", "edge"]):
raise Exception(

View File

@ -1,6 +1,7 @@
import fasteners
import logging
import os
import platform
import re
import shutil
import subprocess
@ -64,6 +65,7 @@ LOCAL_EDGEDRIVER = None
LOCAL_IEDRIVER = None
LOCAL_HEADLESS_IEDRIVER = None
LOCAL_UC_DRIVER = None
ARCH = platform.architecture()[0]
IS_ARM_MAC = shared_utils.is_arm_mac()
IS_MAC = shared_utils.is_mac()
IS_LINUX = shared_utils.is_linux()
@ -2657,6 +2659,118 @@ def get_driver(
or browser_name == constants.Browser.EDGE
)
):
if (
binary_location.lower() == "cft"
and browser_name == constants.Browser.GOOGLE_CHROME
):
binary_folder = None
if IS_MAC:
if IS_ARM_MAC:
binary_folder = "chrome-mac-arm64"
else:
binary_folder = "chrome-mac-x64"
elif IS_LINUX:
binary_folder = "chrome-linux64"
elif IS_WINDOWS:
if "64" in ARCH:
binary_folder = "chrome-win64"
else:
binary_folder = "chrome-win32"
if binary_folder:
binary_location = os.path.join(DRIVER_DIR, binary_folder)
if not os.path.exists(binary_location):
from seleniumbase.console_scripts import sb_install
args = " ".join(sys.argv)
if not (
"-n" in sys.argv or " -n=" in args or args == "-c"
):
# (Not multithreaded)
sys_args = sys.argv # Save a copy of current sys args
log_d(
"\nWarning: Chrome for Testing binary not found..."
)
try:
sb_install.main(override="cft")
except Exception as e:
log_d("\nWarning: Chrome download failed: %s" % e)
sys.argv = sys_args # Put back the original sys args
else:
chrome_fixing_lock = fasteners.InterProcessLock(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
with chrome_fixing_lock:
with suppress(Exception):
shared_utils.make_writable(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
if not os.path.exists(binary_location):
sys_args = sys.argv # Save a copy of sys args
log_d(
"\nWarning: "
"Chrome for Testing binary not found..."
)
sb_install.main(override="cft")
sys.argv = sys_args # Put back original args
else:
binary_location = None
if (
binary_location.lower() == "chs"
and browser_name == constants.Browser.GOOGLE_CHROME
):
binary_folder = None
if IS_MAC:
if IS_ARM_MAC:
binary_folder = "chrome-headless-shell-mac-arm64"
else:
binary_folder = "chrome-headless-shell-mac-x64"
elif IS_LINUX:
binary_folder = "chrome-headless-shell-linux64"
elif IS_WINDOWS:
if "64" in ARCH:
binary_folder = "chrome-headless-shell-win64"
else:
binary_folder = "chrome-headless-shell-win32"
if binary_folder:
binary_location = os.path.join(DRIVER_DIR, binary_folder)
if not os.path.exists(binary_location):
from seleniumbase.console_scripts import sb_install
args = " ".join(sys.argv)
if not (
"-n" in sys.argv or " -n=" in args or args == "-c"
):
# (Not multithreaded)
sys_args = sys.argv # Save a copy of current sys args
log_d(
"\nWarning: "
"Chrome-Headless-Shell binary not found..."
)
try:
sb_install.main(override="chs")
except Exception as e:
log_d(
"\nWarning: "
"Chrome-Headless-Shell download failed: %s" % e
)
sys.argv = sys_args # Put back the original sys args
else:
chrome_fixing_lock = fasteners.InterProcessLock(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
with chrome_fixing_lock:
with suppress(Exception):
shared_utils.make_writable(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
if not os.path.exists(binary_location):
sys_args = sys.argv # Save a copy of sys args
log_d(
"\nWarning: "
"Chrome-Headless-Shell binary not found..."
)
sb_install.main(override="chs")
sys.argv = sys_args # Put back original args
else:
binary_location = None
if not os.path.exists(binary_location):
log_d(
"\nWarning: The Chromium binary specified (%s) was NOT found!"
@ -2666,7 +2780,7 @@ def get_driver(
elif binary_location.endswith("/") or binary_location.endswith("\\"):
log_d(
"\nWarning: The Chromium binary path must be a full path "
"that includes the driver filename at the end of it!"
"that includes the browser filename at the end of it!"
"\n(Will use default settings...)\n" % binary_location
)
# Example of a valid binary location path - MacOS:
@ -2675,6 +2789,34 @@ def get_driver(
else:
binary_name = binary_location.split("/")[-1].split("\\")[-1]
valid_names = get_valid_binary_names_for_browser(browser_name)
if binary_name == "Google Chrome for Testing.app":
binary_name = "Google Chrome for Testing"
binary_location += "/Contents/MacOS/Google Chrome for Testing"
elif binary_name in ["chrome-mac-arm64", "chrome-mac-x64"]:
binary_name = "Google Chrome for Testing"
binary_location += "/Google Chrome for Testing.app"
binary_location += "/Contents/MacOS/Google Chrome for Testing"
elif binary_name == "chrome-linux64":
binary_name = "chrome"
binary_location += "/chrome"
elif binary_name in ["chrome-win32", "chrome-win64"]:
binary_name = "chrome.exe"
binary_location += "\\chrome.exe"
elif binary_name in [
"chrome-headless-shell-mac-arm64",
"chrome-headless-shell-mac-x64",
]:
binary_name = "chrome-headless-shell"
binary_location += "/chrome-headless-shell"
elif binary_name == "chrome-headless-shell-linux64":
binary_name = "chrome-headless-shell"
binary_location += "/chrome-headless-shell"
elif binary_name in [
"chrome-headless-shell-win32",
"chrome-headless-shell-win64",
]:
binary_name = "chrome-headless-shell.exe"
binary_location += "\\chrome-headless-shell.exe"
if binary_name not in valid_names:
log_d(
"\nWarning: The Chromium binary specified is NOT valid!"
@ -2683,6 +2825,8 @@ def get_driver(
"" % (binary_name, valid_names)
)
binary_location = None
elif binary_location.lower() == "chs":
headless = True
if (uc_cdp_events or uc_subprocess) and not undetectable:
undetectable = True
if mobile_emulator and not user_agent:

View File

@ -530,6 +530,34 @@ def Driver(
break
count += 1
user_agent = agent
found_bl = None
if binary_location is None and "--binary-location" in arg_join:
count = 0
for arg in sys_argv:
if arg.startswith("--binary-location="):
found_bl = arg.split("--binary-location=")[1]
break
elif arg == "--binary-location" and len(sys_argv) > count + 1:
found_bl = sys_argv[count + 1]
if found_bl.startswith("-"):
found_bl = None
break
count += 1
if found_bl:
binary_location = found_bl
if binary_location is None and "--bl=" in arg_join:
for arg in sys_argv:
if arg.startswith("--bl="):
binary_location = arg.split("--bl=")[1]
break
if (
binary_location
and binary_location.lower() == "chs"
and browser == "chrome"
):
headless = True
headless1 = False
headless2 = False
recorder_mode = False
if recorder_ext:
recorder_mode = True

View File

@ -656,6 +656,7 @@ def pytest_addoption(parser):
parser.addoption(
"--binary_location",
"--binary-location",
"--bl",
action="store",
dest="binary_location",
default=None,
@ -1574,6 +1575,14 @@ def pytest_configure(config):
sb_config.extension_dir = config.getoption("extension_dir")
sb_config.disable_features = config.getoption("disable_features")
sb_config.binary_location = config.getoption("binary_location")
if (
sb_config.binary_location
and sb_config.binary_location.lower() == "chs"
and sb_config.browser == "chrome"
):
sb_config.headless = True
sb_config.headless1 = False
sb_config.headless2 = False
sb_config.driver_version = config.getoption("driver_version")
sb_config.page_load_strategy = config.getoption("page_load_strategy")
sb_config.with_testing_base = config.getoption("with_testing_base")

View File

@ -568,6 +568,34 @@ def SB(
break
count += 1
user_agent = agent
found_bl = None
if binary_location is None and "--binary-location" in arg_join:
count = 0
for arg in sys_argv:
if arg.startswith("--binary-location="):
found_bl = arg.split("--binary-location=")[1]
break
elif arg == "--binary-location" and len(sys_argv) > count + 1:
found_bl = sys_argv[count + 1]
if found_bl.startswith("-"):
found_bl = None
break
count += 1
if found_bl:
binary_location = found_bl
if binary_location is None and "--bl=" in arg_join:
for arg in sys_argv:
if arg.startswith("--bl="):
binary_location = arg.split("--bl=")[1]
break
if (
binary_location
and binary_location.lower() == "chs"
and browser == "chrome"
):
headless = True
headless1 = False
headless2 = False
recorder_mode = False
if recorder_ext:
recorder_mode = True

View File

@ -397,6 +397,7 @@ class SeleniumBrowser(Plugin):
parser.addoption(
"--binary_location",
"--binary-location",
"--bl",
action="store",
dest="binary_location",
default=None,
@ -1202,6 +1203,14 @@ class SeleniumBrowser(Plugin):
test.test.extension_dir = self.options.extension_dir
test.test.disable_features = self.options.disable_features
test.test.binary_location = self.options.binary_location
if (
test.test.binary_location
and test.test.binary_location.lower() == "chs"
and test.test.browser == "chrome"
):
test.test.headless = True
test.test.headless1 = False
test.test.headless2 = False
test.test.driver_version = self.options.driver_version
test.test.page_load_strategy = self.options.page_load_strategy
test.test.chromium_arg = self.options.chromium_arg