Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ msedgedriver.exe
operadriver.exe
uc_driver.exe

# Chromium Zip Files
chrome-mac.zip
chrome-linux.zip
chrome-win.zip

# Chromium folders
chrome-mac
chrome-linux
chrome-win

# Chrome for Testing Zip Files
chrome-mac-arm64.zip
chrome-mac-x64.zip
Expand Down
16 changes: 16 additions & 0 deletions help_docs/customizing_test_runs.md
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,20 @@ With the `SB()` and `Driver()` formats, the binary location is set via the `bina

--------

🎛️ To use the special `Chromium` binary:

```zsh
sbase get chromium
```

Then, run scripts with `--use-chromium` / `use_chromium=True`:

```zsh
pytest --use-chromium -n8 --dashboard --html=report.html -v --rs --headless
```

--------

🎛️ To use the special `Chrome for Testing` binary:

```zsh
Expand Down Expand Up @@ -721,6 +735,7 @@ sjw=None # Shortcut / Duplicate of "skip_js_waits".
wfa=None # Shortcut / Duplicate of "wait_for_angularjs".
cft=None # Use "Chrome for Testing"
chs=None # Use "Chrome-Headless-Shell"
use_chromium=None # Use base "Chromium"
save_screenshot=None # Save a screenshot at the end of each test.
no_screenshot=None # No screenshots saved unless tests directly ask it.
page_load_strategy=None # Set Chrome PLS to "normal", "eager", or "none".
Expand Down Expand Up @@ -816,6 +831,7 @@ server=None # Shortcut / Duplicate of "servername".
guest=None # Shortcut / Duplicate of "guest_mode".
wire=None # Shortcut / Duplicate of "use_wire".
pls=None # Shortcut / Duplicate of "page_load_strategy".
use_chromium=None # Use base "Chromium"
cft=None # Use "Chrome for Testing"
chs=None # Use "Chrome-Headless-Shell"
```
Expand Down
2 changes: 1 addition & 1 deletion mkdocs_build/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Minimum Python version: 3.10 (for generating docs only)

regex>=2025.11.3
pymdown-extensions>=10.19
pymdown-extensions>=10.19.1
pipdeptree>=2.30.0
python-dateutil>=2.8.2
Markdown==3.10
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ packages = [
"seleniumbase.drivers.brave_drivers",
"seleniumbase.drivers.comet_drivers",
"seleniumbase.drivers.atlas_drivers",
"seleniumbase.drivers.chromium_drivers",
"seleniumbase.extensions",
"seleniumbase.fixtures",
"seleniumbase.js_code",
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ certifi>=2025.11.12
exceptiongroup>=1.3.1
websockets>=15.0.1
filelock~=3.19.1;python_version<"3.10"
filelock>=3.20.0;python_version>="3.10"
filelock>=3.20.1;python_version>="3.10"
fasteners>=0.20
mycdp>=1.3.2
pynose>=1.5.5
Expand Down
2 changes: 1 addition & 1 deletion seleniumbase/__version__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# seleniumbase package
__version__ = "4.45.2"
__version__ = "4.45.3"
6 changes: 6 additions & 0 deletions seleniumbase/behave/behave_sb.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,12 @@ def get_configured_sb(context):
sb.binary_location = binary_location
sb_config.binary_location = binary_location
continue
# Handle: -D use-chromium
if low_key in ["use-chromium"] and not sb_config.binary_location:
binary_location = "_chromium_"
sb.binary_location = binary_location
sb_config.binary_location = binary_location
continue
# Handle: -D cft
if low_key in ["cft"] and not sb_config.binary_location:
binary_location = "cft"
Expand Down
1 change: 1 addition & 0 deletions seleniumbase/console_scripts/ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ sbase get chromedriver 114.0.5735.90
sbase get chromedriver stable
sbase get chromedriver beta
sbase get chromedriver -p
sbase get chromium
sbase get cft 131
sbase get chs
```
Expand Down
137 changes: 136 additions & 1 deletion seleniumbase/console_scripts/sb_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
sbase get chromedriver stable
sbase get chromedriver beta
sbase get chromedriver -p
sbase get chromium
sbase get cft 131
sbase get chs
Output:
Expand Down Expand Up @@ -46,16 +47,21 @@
from seleniumbase import drivers # webdriver storage folder for SeleniumBase
from seleniumbase.drivers import cft_drivers # chrome-for-testing
from seleniumbase.drivers import chs_drivers # chrome-headless-shell
from seleniumbase.drivers import chromium_drivers # base chromium

urllib3.disable_warnings()
ARCH = platform.architecture()[0]
IS_ARM_MAC = shared_utils.is_arm_mac()
IS_MAC = shared_utils.is_mac()
IS_ARM_LINUX = shared_utils.is_arm_linux()
IS_LINUX = shared_utils.is_linux()
IS_WINDOWS = shared_utils.is_windows()
DRIVER_DIR = os.path.dirname(os.path.realpath(drivers.__file__))
DRIVER_DIR_CFT = os.path.dirname(os.path.realpath(cft_drivers.__file__))
DRIVER_DIR_CHS = os.path.dirname(os.path.realpath(chs_drivers.__file__))
DRIVER_DIR_CHROMIUM = os.path.dirname(
os.path.realpath(chromium_drivers.__file__)
)
LOCAL_PATH = "/usr/local/bin/" # On Mac and Linux systems
DEFAULT_CHROMEDRIVER_VERSION = "114.0.5735.90" # (If can't find LATEST_STABLE)
DEFAULT_GECKODRIVER_VERSION = "v0.36.0"
Expand Down Expand Up @@ -203,6 +209,59 @@ def get_cft_latest_version_from_milestone(milestone):
return url_request.json()["milestones"][milestone]["version"]


def get_chromium_channel_revision(platform_code, channel):
"""Snapshots only exist for revisions where a build occurred.
Therefore, not all found revisions will lead to snapshots."""
platform_key = None
if platform_code in ["Mac_Arm", "Mac"]:
platform_key = "Mac"
elif platform_code in ["Linux_x64"]:
platform_key = "Linux"
elif platform_code in ["Win_x64"]:
platform_key = "Windows"
elif platform_code in ["Win"]:
platform_key = "Win32"
channel_key = None
if channel.lower() == "stable":
channel_key = "Stable"
elif channel.lower() == "beta":
channel_key = "Beta"
elif channel.lower() == "dev":
channel_key = "Dev"
elif channel.lower() == "canary":
channel_key = "Canary"
base_url = "https://chromiumdash.appspot.com/fetch_releases"
url = f"{base_url}?channel={channel_key}&platform={platform_key}&num=1"
url_request = requests_get_with_retry(url)
data = None
if url_request.ok:
data = url_request.text
else:
raise Exception("Could not determine Chromium revision!")
if data:
try:
import ast

result = ast.literal_eval(data)
revision = result[0]["chromium_main_branch_position"]
return str(revision)
except Exception:
return get_latest_chromedriver_version(platform_code)
else:
return get_latest_chromedriver_version(platform_code)


def get_chromium_latest_revision(platform_code):
base_url = "https://storage.googleapis.com/chromium-browser-snapshots"
url = f"{base_url}/{platform_code}/LAST_CHANGE"
url_request = requests_get_with_retry(url)
if url_request.ok:
latest_revision = url_request.text
else:
raise Exception("Could not determine latest Chromium revision!")
return latest_revision


def get_latest_chromedriver_version(channel="Stable"):
try:
if getattr(sb_config, "cft_lkgv_json", None):
Expand Down Expand Up @@ -274,6 +333,11 @@ def main(override=None, intel_for_uc=None, force_uc=None):
elif override.startswith("iedriver "):
extra = override.split("iedriver ")[1]
sys.argv = ["seleniumbase", "get", "iedriver", extra]
elif override == "chromium":
sys.argv = ["seleniumbase", "get", "chromium"]
elif override.startswith("chromium "):
extra = override.split("chromium ")[1]
sys.argv = ["seleniumbase", "get", "chromium", extra]
elif override == "cft":
sys.argv = ["seleniumbase", "get", "cft"]
elif override.startswith("cft "):
Expand Down Expand Up @@ -316,6 +380,8 @@ def main(override=None, intel_for_uc=None, force_uc=None):
downloads_folder = DRIVER_DIR_CFT
elif override == "chs" or name == "chs":
downloads_folder = DRIVER_DIR_CHS
elif override == "chromium":
downloads_folder = DRIVER_DIR_CHROMIUM
expected_contents = None
platform_code = None
copy_to_path = False
Expand Down Expand Up @@ -643,6 +709,31 @@ def main(override=None, intel_for_uc=None, force_uc=None):
"https://storage.googleapis.com/chrome-for-testing-public/"
"%s/%s/%s" % (use_version, platform_code, file_name)
)
elif name == "chromium":
if IS_MAC:
if IS_ARM_MAC:
platform_code = "Mac_Arm"
else:
platform_code = "Mac"
file_name = "chrome-mac.zip"
elif IS_LINUX:
platform_code = "Linux_x64"
file_name = "chrome-linux.zip"
elif IS_WINDOWS:
if "64" in ARCH:
platform_code = "Win_x64"
else:
platform_code = "Win"
file_name = "chrome-win.zip"
revision = get_chromium_latest_revision(platform_code)
msg = c2 + "Chromium revision to download" + cr
p_version = c3 + revision + cr
log_d("\n*** %s = %s" % (msg, p_version))
download_url = (
"https://storage.googleapis.com/chromium-browser-snapshots/"
"%s/%s/%s" % (platform_code, revision, file_name)
)
downloads_folder = DRIVER_DIR_CHROMIUM
elif name == "chrome-headless-shell" or name == "chs":
set_version = None
found_version = None
Expand Down Expand Up @@ -1003,12 +1094,12 @@ def main(override=None, intel_for_uc=None, force_uc=None):
remote_file = requests_get_with_retry(download_url)
with open(file_path, "wb") as file:
file.write(remote_file.content)
log_d("%sDownload Complete!%s\n" % (c1, cr))

if file_name.endswith(".zip"):
zip_file_path = file_path
zip_ref = zipfile.ZipFile(zip_file_path, "r")
contents = zip_ref.namelist()
log_d("%sDownload Complete!%s\n" % (c1, cr))
if (
len(contents) >= 1
and name in ["chromedriver", "uc_driver", "geckodriver"]
Expand Down Expand Up @@ -1249,6 +1340,48 @@ def main(override=None, intel_for_uc=None, force_uc=None):
"Chrome for Testing was saved inside:\n%s%s\n%s\n"
% (pr_base_path, pr_sep, pr_folder_name)
)
elif name == "chromium":
# Zip file is valid. Proceed.
driver_path = None
driver_file = None
base_path = os.sep.join(zip_file_path.split(os.sep)[:-1])
folder_name = contents[0].split("/")[0]
folder_path = os.path.join(base_path, folder_name)
if IS_MAC or IS_LINUX:
if (
"chromium" in folder_path
and "drivers" in folder_path
and os.path.exists(folder_path)
):
shutil.rmtree(folder_path)
subprocess.run(
["unzip", zip_file_path, "-d", downloads_folder]
)
elif IS_WINDOWS:
subprocess.run(
[
"powershell",
"Expand-Archive",
"-Path",
zip_file_path,
"-DestinationPath",
downloads_folder,
"-Force",
]
)
else:
zip_ref.extractall(downloads_folder)
zip_ref.close()
with suppress(Exception):
os.remove(zip_file_path)
log_d("%sUnzip Complete!%s\n" % (c2, cr))
pr_base_path = c3 + base_path + cr
pr_sep = c3 + os.sep + cr
pr_folder_name = c3 + folder_name + cr
log_d(
"Chromium was saved inside:\n%s%s\n%s\n"
% (pr_base_path, pr_sep, pr_folder_name)
)
elif name == "chrome-headless-shell" or name == "chs":
# Zip file is valid. Proceed.
driver_path = None
Expand Down Expand Up @@ -1300,6 +1433,7 @@ def main(override=None, intel_for_uc=None, force_uc=None):
tar = tarfile.open(file_path)
contents = tar.getnames()
if len(contents) == 1:
log_d("%sDownload Complete!%s\n" % (c1, cr))
for f_name in contents:
# Remove existing version if exists
new_file = os.path.join(downloads_folder, str(f_name))
Expand Down Expand Up @@ -1337,6 +1471,7 @@ def main(override=None, intel_for_uc=None, force_uc=None):
else:
# Not a .zip file or a .tar.gz file. Just a direct download.
if "Driver" in file_name or "driver" in file_name:
log_d("%sDownload Complete!%s\n" % (c1, cr))
log_d("Making [%s] executable ..." % file_name)
make_executable(file_path)
log_d("%s[%s] is now ready for use!%s" % (c1, file_name, cr))
Expand Down
6 changes: 6 additions & 0 deletions seleniumbase/console_scripts/sb_mkdir.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,12 @@ def main():
data.append("msedgedriver.exe")
data.append("operadriver.exe")
data.append("uc_driver.exe")
data.append("chrome-mac.zip")
data.append("chrome-linux.zip")
data.append("chrome-win.zip")
data.append("chrome-mac")
data.append("chrome-linux")
data.append("chrome-win")
data.append("chrome-mac-arm64.zip")
data.append("chrome-mac-x64.zip")
data.append("chrome-linux64.zip")
Expand Down
5 changes: 5 additions & 0 deletions seleniumbase/console_scripts/sb_mkrec.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def main():
use_brave = False
use_comet = False
use_atlas = False
use_chromium = False
use_uc = False
esc_end = False
start_page = None
Expand Down Expand Up @@ -150,6 +151,8 @@ def main():
use_comet = True
elif option.lower() == "--atlas":
use_atlas = True
elif option.lower() == "--use-chromium":
use_chromium = True
elif option.lower() == "--ee":
esc_end = True
elif option.lower() in ("--gui", "--headed"):
Expand Down Expand Up @@ -295,6 +298,8 @@ def main():
run_cmd += " --comet"
elif use_atlas:
run_cmd += " --atlas"
elif use_chromium:
run_cmd += " --use-chromium"
if force_gui:
run_cmd += " --gui"
if use_uc:
Expand Down
2 changes: 2 additions & 0 deletions seleniumbase/console_scripts/sb_recorder.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ def do_recording(file_name, url, overwrite_enabled, use_chrome, window):
command += " --comet"
elif "--atlas" in command_args:
command += " --atlas"
elif "--use-chromium" in command_args:
command += " --use-chromium"
if (
"--uc" in command_args
or "--cdp" in command_args
Expand Down
Loading