Repository navigation
Releases: MustCodeAl/seleniumbase-rs
Release list
v0.6.0
Prebuilt binaries are for macOS on Apple Silicon only (GitHub Actions cannot run for this repository right now). Everywhere else:
brew install MustCodeAl/tap/sbrs
# or
cargo install --git https://github.com/MustCodeAl/seleniumbase-rs --tag v0.6.0 seleniumbase-rs-cli --lockedBreaking
- Downloads go to
./downloaded_files, as in Python SeleniumBase.BaseCase::get_downloads_folder,
assert_downloaded_file,download_fileand the other download helpers used the OS Downloads
folder; they now usedownloaded_filesin the working directory
(browser::downloads::downloads_dir), and a browser the library launches is told to save
there: WebDriver sessions ask for it in their capabilities (the Chrome and Edge preferences
download.default_directory,download.prompt_for_downloadanddownload.directory_upgrade,
and the Firefoxbrowser.download.*ones; a preference you set yourself wins), and the pure CDP
engine sendsBrowser.setDownloadBehaviorat launch, soPage::assert_downloaded_filefinds a
file without a folder being chosen first.downloaded_files/is in.gitignore. - A reused browser keeps its cookies unless
--crumbs. With--reuse-sessionor--rcsthe
browser handed to the next test used to have every cookie deleted; now the cookies stay (Python's
default) andSB_CRUMBS=1orbrowser::reuse::set_crumbs(true)deletes them. The other windows
are still closed, the page's storage cleared and the page set toabout:blank. - A page command needs a page.
get-text,get-title,get-attribute,screenshot,
assert-element,hover,send-keys, the alert commands and the other commands that act
on a page used to act onabout:blank, which no one wants. Without a start page (--url,
SB_START_PAGEor the settings file) they now stop witherror: NAME acts on a page, and every sbrs command starts a fresh browser; give the page: sbrs --url https://example.com NAME ...and status 2, before any browser starts.cdpneeds no page. - A command that changes the profile needs
--user-data-dir.clear-cookies,
load-cookies,set-local-storage-item,remove-local-storage-item,
clear-local-storageandcache-clearchanged a profile that a fresh browser throws away
at the end; they now stop witherror: NAME changes the profile, which a fresh browser throws away; add --user-data-dir DIR(status 2) unless the folder is named. - Fifteen commands are removed because they have no observable effect in a one-shot
browser:go-back,go-forward,refresh,switch-to-window,switch-to-frame,
switch-to-default-content,switch-to-parent-frame,switch-to-newest-window,
switch-to-default-window,open-new-window,open-new-tab,close-window,
set-window-position,get-active-element-cssandthrottle3g(an unrecognised
subcommand now). Several steps in one browser arerun-scenario,deferredand the MCP
server.export-recordingstays: it writes a test from an action file and starts no browser. - A command with a URL of its own refuses
--url.open,smoke,download-file,
print-pdf,pdf-text,assert-pdf-textandrecordstop witherror: NAME opens its own URL; leave out --start-pagewhen the command line names a start page; one that comes
fromSB_START_PAGEor the file is only a default and is ignored for them. devtools,choose-fileanddeferredread the global options. They no longer have
an option of their own for--headless,--user-data-dir,--proxyor--url; the
global ones of the same names do the same work, and give the page to open (choose-file
anddeferredact on the start page, a blank upload page whenchoose-filehas none).
The same global option on both sides of the command is a repeat and is refused.
Added
-
A debug prompt:
BaseCase::breakpoint,sb_cdp::Page::breakpointand
common::debug_prompt.sb.breakpoint("message").await?pauses a test with the browser
open and reads commands from the terminal:cgoes on,ssaves a screenshot andsrcthe
page source tolatest_logs/<test id>/,ushows the address and title,js <expr>shows
a value,qstops the test with aTestLifecycleerror andhlists the commands. The time
spent at the prompt is added to the time limit. The pause is skipped, with a warning in the
log, when standard input is not a terminal (CI,cargo nextest), andSB_BREAKPOINTS=0turns
it off, as Python'sPYTHONBREAKPOINT=0does.DebugPrompt,PromptKind,PromptOutcome,
PromptPageandLineInputare public, for the runner's--trace,--pdband
--final-debug(not wired to a flag yet).BaseCase::recorder_modesays whether a
recording window is open. -
A command line that does not parse prints the command's page, then the error. Like
Python'ssbase,sbrs mkfile,sbrs grid-hub,sbrs get-textandsbrs plugin info
print the usage, arguments, flags and examples of the command the words reached, a blank
line, and last the parser'serror:line andtip:, on standard error with status 2. A
mistake found after parsing (a page command with no page, a start page for a command with a
URL of its own) prints the same way. An unknown command, or none, prints the commands by
group, names only, instead of the 112-line help.--help,--versionand shell completion
are as before. -
Every command has an example and a help heading.
sbrs <command> --helpends with an
Examples:section (sbrs --url https://example.com get-text --css h1), the commands are
grouped as Page commands, Files and generators, Record and replay, Tests, Drivers, grid and
servers, Import and convert, Security, and Info and setup insbrs --help, and a test
proves that every example parses. The examples are in the usage spec and in
docs/help/cli_reference.md. -
The Chromium switches are typed.
BrowserConfig::with_dark_mode,with_guest_mode,
with_incognito,with_devtools,with_disable_ws/with_enable_ws,with_swiftshader,
with_enable_3d_apis,with_maximize,with_no_sandbox,with_disable_gpu,
with_headless1/with_headless2,with_xvfb,with_chromium_arg(Python's comma list,
read byparse_chromium_args),with_disable_featuresandwith_host_resolver_rules
replace composingextra_argsby hand.--incognitoand--guestare left out, with a
warning, when the session loads an extension. A headless session may keep its GPU
(with_enable_3d_apis,with_swiftshader). Firefox gets-privateand-devtools, and
security.csp.enablefor--disable-csp; Firefox and Safari are maximized once running. -
LocaleCodereads a language code in Python's form (ar_eg) or Chrome's (ar-EG) and
writes Chrome's. -
The start page:
--start-page,--urland--start_page,SB_START_PAGE, and the
start_pagekey of the settings file. Everysbrscommand starts a fresh browser, so
a command that acts on a page now acts on this one:sbrs --url https://example.com get-text --css h1. The command line wins overSB_START_PAGE, which wins over the file;
a bare host getshttps://and a local file becomes afile://URL.sbrs test --url X
hands it on to the tests asSB_START_PAGE. The library side isSettings::start_page
(whichto_browser_configcopies intoBrowserConfig::start_page),
SettingsOverrides::start_pageandSTART_PAGE_ENV. -
--save-screenshotsaves a screenshot tolatest_logs/just before a browser command
quits and prints its path (Saved screenshot: PATH). A command that changes the page
(click-partial-link-text,hover,human-click,add-text, ...) also prints where the
browser ended up,Now at: URL (TITLE), so a click that navigates shows where it went. -
Every option of
sbrsitself is global.sbrs get-title --headless --url https://example.comworks as well assbrs --headless --url https://example.com get-title; the same option on both sides of the command is refused as a repeat (status 2).
Every page command now ends withquit, where some left the browser to the drop-time
cleanup. -
Fingerprint capture, export and import. A browser's fingerprint can now be read from
a real browser and handed back to any launch path, as one file format everywhere: the v2
FingerprintJSON, described by the newschemas/fingerprint.schema.json(a test fails
when the schema is stale).Page::capture_fingerprint(pure CDP) and
BaseCase::capture_fingerprint(WebDriver) read what a page can see of the browser and
return aCapturedFingerprint(the fingerprint, the readings no setting holds in
unmapped, and aProbeInfowith the page, the time and the browser version);
compare_fingerprintchecks that a live page presents a fingerprint and lists what no page
can read as unverifiable instead of as a match.Fingerprint::load,save(atomic,
mode0600),from_json,to_json_pretty,diff(SettingDiff),compare_to
(FingerprintComparison) andclaimed_settingsare public. -
sbrs fingerprint capture|export|check|diff|presets.capture [URL] [-o FILE] [--readings FILE]prints or writes the fingerprint of the browser the global options
start;export (--preset NAME | --random [--seed N] [--os OS])writes a preset or a seeded
random identity;check FILEreports the errors and warnings of a file (status 2 when
there is an error);diff A Blists the settings whereBdiffers from whatAexpects
(status 1 when any does);presetslists the presets. -
The global
--fingerprint FILE,SB_FINGERPRINTand a[fingerprint]table (file = "...") in the settings file make every browser a command starts present a fingerprint
file. The command line wins over the variable, which wins over the file; a file that
cannot be read stops the c...
seleniumbase-rs 0.5.1
A patch release of the command line: it refuses again what clap refused in 0.4.0,
and sbrs-mcp names itself.
Changed
sbrs-mcpnames itself:sbrs-mcp --versionprintssbrs-mcp 0.5.1, and its help
and the usage line of an error saysbrs-mcp, where they saidseleniumbase-rs.
Fixed
- A flag given twice after a subcommand,
sbrs wait-for-text --css a --css b, is an
error with status 2 again (the argument '--css' cannot be used multiple times), as it
was with clap. 0.5.0 took the last value. A flag that is a list, such as
deferred --assert, still repeats. - A value written after a switch,
sbrs --headless=false open ...or--help=1, is an
error with status 2 again, as it was with clap. 0.5.0 acceptedtrueandfalse
there and ignored them. sbrs help nosuch, andhelpwith any path that names no command, is an error with
status 2 and anunrecognized subcommandmessage again. 0.5.0 printed the help of the
last command it found, with status 0.
seleniumbase-rs 0.5.0
A release of API clean-ups and new structure. The fingerprint API is one
Spoof per setting with a separate StealthPolicy, built once and never
mutated. BaseCase text assertions and waits take their arguments in Python's
order, and the run settings, runner, CDP page and behave APIs are cleaned up.
The command line moved to its own workspace crate, seleniumbase-rs-cli, and
parses with usage instead of clap. The built-in plugins can be chosen by name
from features, the settings file, SB_PLUGINS or sbrs --plugin. PDF text
extraction is the opt-in feature pdf. The BREAKING changes, those that break a
call, are listed first.
Breaking
-
sbrsandsbrs-mcpare built by the new workspace package
seleniumbase-rs-cli(crates/cli), not byseleniumbase-rs. The library
stays at the repository root with the same name and path, so
seleniumbase-rs = { git = ... }keeps working, but it no longer has a binary
and no longer compiles clap,clap_completeorclap_complete_fig(15 crates
fewer in a default build). The commands, flags, aliases, defaults and exit
statuses ofsbrsare the same (its parser changed, see Changed), with one
difference: a plaincargo build,cargo installorcargo runnow includes
pdf, sosbrs pdf-textandsbrs assert-pdf-textwork where they used to exit
with status 2.# before cargo install --path . --bin sbrs cargo install --path . --bin sbrs-mcp --features mcp-server cargo install --git https://github.com/MustCodeAl/seleniumbase-rs --features mcp-server # after cargo install --path crates/cli cargo install --git https://github.com/MustCodeAl/seleniumbase-rs seleniumbase-rs-cli cargo binstall --git https://github.com/MustCodeAl/seleniumbase-rs seleniumbase-rs-cli
The new package has
pdfandmcp-serveron by default (the release archives
always had them), andplugins-builtin(the built-in browser plugins, see
Added), and forwardstui,gui,turso,test-util,
full-tracingandtokio-consoleto the library, so
cargo install ... seleniumbase-rs-cli --features tuiis the way to add the
terminal UI.default-membersnames both packages, socargo build,
cargo testandcargo run --bin sbrsat the repository root work as before;
use-p seleniumbase-rsfor the library alone. The release archives keep their
names (seleniumbase-rs-v<version>-<target>). The messagessbrsprints for a
build withouttuiorpdfnow name the package that builds it
(cargo install --git https://github.com/MustCodeAl/seleniumbase-rs seleniumbase-rs-cli --features tui), instead ofseleniumbase-rs, which no
longer has a binary. The Homebrew formula has to build--path crates/clifrom
the first release that has this change. -
PDF text extraction is the opt-in feature
pdf.pdf-extract(lopdf, fonts,
ciphers) was 34 of the crates of a default build.BaseCase::get_pdf_text,
assert_pdf_textand the moduleapi::pdfneed it; printing a page to PDF
(print_to_pdf,save_as_pdf) does not.sbrs pdf-textand
assert-pdf-textstay in--helpand, in a build without the feature, exit
with status 2 and say how to reinstall it with the feature
(cargo install --git https://github.com/MustCodeAl/seleniumbase-rs seleniumbase-rs-cli --features pdf). -
Text assertions take the text first and the selector second, as Python's
assert_text(text, selector)does. A call with the old order still compiles
(both arguments are&str), so search for these names:assert_text,
assert_exact_text,assert_exact_text_not_visible,assert_shadow_text,
RecorderSession::assert_text,AssertionApi::assert_textand the macro
sb_assert_text!. A failing assertion whose arguments look swapped says so in
its message.// before sb.assert_text("h1", "Example Domain").await?; sb_assert_text!(sb, "#status", "Saved")?; // after sb.assert_text("Example Domain", "h1").await?; sb_assert_text!(sb, "Saved", "#status")?;
The generators follow: the recorder, the scenario runner,
sbrs mkdir,
sbrs mkfile, the MCP export tools and the Python importer now write the new
order.RecorderSession::assert_text(sb, text, css)and the
AssertionApi::assert_text(expected, css)trait method changed with it. If
you generated tests with an earlier version, run the same search over them. -
The text waits take
(text, css, timeout_secs), as Python's
wait_for_text(text, selector, timeout)does.wait_for_text,
wait_for_exact_text_visibleandwait_for_exact_text_not_visibleswapped
their first two arguments (a silent change: both are&str).
wait_for_text_visible,wait_for_text_not_visibleandwait_for_exact_text
gained the selector they lacked, so an old call stops compiling. Pass"body"
to look at the whole page, as the oldwait_for_text_visible(text, timeout)
did.// before sb.wait_for_text("#status", "Saved", 10).await?; sb.wait_for_text_visible("Saved", 10).await?; sb.wait_for_exact_text("Saved", 10).await?; // after sb.wait_for_text("Saved", "#status", 10).await?; sb.wait_for_text_visible("Saved", "body", 10).await?; sb.wait_for_exact_text("Saved", "#status", 10).await?;
wait_for_textnow waits for the text to be displayed, like
wait_for_text_visible(in Python the first is an alias of the second). -
The deferred and delayed text checks take
(text, css).
deferred_assert_exact_text,delayed_assert_exact_textand
delayed_assert_texttook the selector first.delayed_assert_textalso
passed its arguments on in the other order, sodelayed_assert_text("Welcome", "h1")looked for an element calledWelcome; all three now follow Python's
(text, selector), likedeferred_assert_text.// before sb.deferred_assert_exact_text("#status", "Ready").await?; // after sb.deferred_assert_exact_text("Ready", "#status").await?;
-
find_text,find_exact_textandfind_non_empty_textfollow Python.
They took no selector, searched all of<body>for the deepest element that
held the text, and returnedResult<Option<WebElement>>(theOptionwas
neverNone). They now take the selector and returnResult<WebElement>,
the element of that selector, as Python'sfind_text(text, selector)does.
Pass"body"to look at the page.// before if let Some(el) = sb.find_text("Welcome").await? { /* ... */ } // after let el = sb.find_text("Welcome", "h1").await?;
-
post_message_and_highlight(message, css)takes the element to highlight,
as Python'spost_message_and_highlight(message, selector)does. It used to
highlight the toast it had just shown. An error while highlighting is now
returned instead of ignored.// before sb.post_message_and_highlight("Saved").await?; // after sb.post_message_and_highlight("Saved", "#save-button").await?;
-
The extra browsers are named by a
DriverId, and switching to the default
one is repeatable.get_new_driver(Option<BrowserConfig>)returned the
index of the browser it had just parked, andswitch_to_driver(index)swapped
the active browser with the one in that slot, so the slot held whichever
browser was left last:switch_to_default_driver()(slot 0) reached the
first browser only once, and its second call went back to the other. A
DriverIdnow names a browser for good, andswitch_to_default_driver()
always reaches the first one. Changes:get_new_driver()takes no argument andget_new_driver_with(config)takes
the configuration (no moreNone); both return the new browser's
DriverId.switch_to_driver(DriverId)makes that browser active and can be repeated;
active_driver()says which one is.quit_extra_driver(DriverId)refusesDriverId::DEFAULT(that browser is
closed byquit()); quitting the active extra browser falls back to the
default one, as in Python. A browser that was quit is an error to switch
to, not a disconnected session that panics.quit()closes every browser the case opened, not only the active one.driver_count()counts the browsers that are open, the active one
included (it counted the parked ones, quit placeholders too).
// before let first = sb.get_new_driver(None).await?; // the index of the parked browser sb.switch_to_driver(first).await?; // swaps: the slot now holds the other // after let second = sb.get_new_driver().await?; // the new browser's id sb.switch_to_default_driver().await?; // always the first browser sb.switch_to_driver(second).await?;
-
wait(seconds)takes whole or fractional seconds, and nou64. It is
sleep, as in Python, and takesimpl Into<f64>:wait(2)andwait(0.5)
compile, but au64variable (or ausize, ani64) no longer does.// before let seconds: u64 = 3; sb.wait(seconds).await; // after sb.wait(seconds as f64).await; // or keep the variable a u32 or an f64
-
check_window's level is au8, not anf64:check_window("home", 3)
instead ofcheck_window("home", 3.0); the same fordeferred_check_window
anddelayed_check_window. A float literal stops compiling. As before, a
level other than 0, 1, 2 or 3 is anInvalidConfigerror.// before sb.check_window("home", 3.0).await?; // after sb.check_window("home", 3).await?;
-
The cookie helpers take a name and use
saved_cookies/, as Python's do.
save_cookies(path)andload_cookies(path)took any file path, so a call
written like Python's (save_cookies("login")) wrote a file calledlogin
in the working directory. They now take a name: the file is
saved_cookies/<name>.txtin the working directory (only the last part of
the name is used, `.tx...
seleniumbase-rs 0.4.0
A feature release: browser plugins now run in the WebDriver engine as well as
the Pure CDP one, the Pure CDP engine gains page script and style tags and HAR
recording, the opt-in visual feature compares screenshots by structural
similarity, sbrs prompt prints a browser and driver segment for shell prompts,
and the documentation gains a guide to testing pages with a CAPTCHA.
Added
- Browser plugins for the WebDriver engine (feature
plugins; ADR 004
phase 3).BrowserConfig::with_pluginsgives aPluginSetto
BaseCase::newandBrowserSession::connect: the set is checked against the
browser,before_launchchanges the start-up settings (Chromium arguments,
capability preferences, extension folders, proxy and headless; Firefox
arguments, preferences and headless), and once the session runs the plugins
geton_browser, then the packages andon_page_createdfor the first
window, for each windowswitch_to_new_windoworopen_new_windowopens, and
for any other onapply_plugins_to_window;quitor a drop calls
on_close, andreconnectruns the hooks again. On Chromium browsers
plugins send protocol commands through chromedriver's DevTools channel; on
Firefox and Safarisendis refused and scripts for every document run at
once and again after eachopen. A refusal or a failing hook stops the
session withSeleniumBaseError::Plugin. Plugins withreuse_sessionare
refused. See the WebDriver section ofdocs/tutorials/plugins.md. sb_cdp::Page::add_script_tagandadd_style_tagadd a script or a
stylesheet to the current document from a URL or inline (TagSource),
waiting for a file to load;add_init_scriptruns a script in every
document the tab loads from then on and returns anInitScriptIdfor
remove_init_script. Playwright'saddScriptTag,addStyleTagand
addInitScript.- With the
pluginsfeature,sb_cdp::Pageimplements the plugin API's
PageHandle, so a program can hand one of its tabs to plugin code. - Visual regression testing, behind the new opt-in
visualfeature:
visual::VisualBaselinesaves a screenshot as a baseline PNG on the first
run and compares later screenshots with it by structural similarity (MSSIM
over 8 × 8 windows on each colour channel,image-compare), failing below a
threshold of 0.999 withAssertionFailedand leaving<name>.actual.pngand
a<name>.diff.pngthat paints the differences red. Screenshots of another
size fail without a comparison.check_pagetakes aBaseCaseor an
sb_cdp::Page, andBaseCase::assert_visual_match(name)keeps its baselines
invisual_baseline/<test>/,<test>being the test's whole path (test
binary, module path and function, ascart.checkout.renders), and follows
with_visual_baseline_reset.
check_windowis unchanged: it compares the HTML layout, as in Python. - HAR recording for the pure CDP engine (no new dependency, no feature):
Page::start_har()/start_har_with(HarOptions)return aHarRecorder,
andHarRecorder::finish()returns aHar(HTTP Archive 1.2 serde types)
thatHar::save(path)writes as JSON. Entries come from theNetwork
events of the tab and of its cross-site iframes, which run in targets of
their own and are attached while recording, extra-info headers included, with cookies, query strings, POST
bodies, one entry per redirect hop, failures with Chrome's_error, and
blocked/dns/connect/ssl/send/wait/receivetimings; each
top-level navigation is a page withonContentLoadandonLoad. Response
bodies are off by default and capped at 1 MiB each when turned on; the cap
can be raised to 64 MiB (MAX_CONTENT_BYTES). sbrs promptprints one short line for a shell prompt: the installed
Chrome's major version and whether a matching chromedriver is at hand
(chrome 155 ✓driver,✗driver,?driver), with--asciimarks and a
--formatof{browser},{major},{driver},{mark}and
{driver_major}. It starts no browser and uses no network: versions come
from files (a macOS app'sInfo.plist, the version folder beside
chrome.exe), and one that only--versiontells is cached for ten minutes
in the user cache folder. It prints nothing and exits with 0 when no browser
is found (--verbosesays why). The CLI page has setups for Bash, Zsh,
Fish, PowerShell, Starship and Warp, and.warp/workflows/a Workflow for it.browser::driver_version::major_without_runningreads a program's major
version from its files only, andversion_flag_majorruns--versionwith a
time limit.- Tutorial Testing Pages with a CAPTCHA: vendor test keys, and tests that click the test widget and read the token.
docs/completions/: a Fig completion spec forsbrs, prepared for withfig/autocomplete (not submitted), with the steps.
Fixed
- The pure CDP engine takes DevTools messages up to 256 MiB, as Puppeteer
does. Chrome sends each answer in one WebSocket frame, and one over the
default 16 MiB, such as a large screenshot or response body, used to close
the connection for every tab. sbrsexits quietly with status 0 when the reader of its output goes away
(sbrs completions zsh | head), instead of printing "error: Broken pipe".
seleniumbase-rs 0.3.0
A breaking release: the experimental stealth scraping layer leaves this crate.
Removed
- BREAKING: the stealth scraping layer moved to its own repository,
seleniumbase-rs-stealth.
The featuresscrapeandimpersonate, the modulesseleniumbase_rs::wire,
seleniumbase_rs::scrapeandseleniumbase_rs::impersonate, and their
crate-root re-exports (BrowserScraper,CookieJar,StealthIdentity,
SessionPolicy,ScrapeError,BrowserKind,Emulation,TlsSpec,
H2Spec,HeaderSpec,ImpersonationProfile,CustomProfileBuilder,
impersonation_client,HttpSession,MitmProxy,StealthSession,
EphemeralCaand the rest) are gone from this crate. They are now the crates
seleniumbase-rs-wire(was the modulewire),seleniumbase-rs-scrape(was
the featurescrape) andseleniumbase-rs-impersonate(was the feature
impersonate), which build on the public API of this crate and stay
experimental. Depend on them by git, for example
seleniumbase-rs-scrape = { git = "https://github.com/MustCodeAl/seleniumbase-rs-stealth" },
and giveseleniumbase-rsby its plain git URL next to them so both resolve to
one commit (Stealth Scraping). - With the layer go the optional dependencies
psl,wreq,btls,
tokio-btls,brotli,hyper,hyper-utilandhttp-body-util; the
examplescapture_tls,capture_hints,capture_h2,cookie_jar,
browser_scraper,custom_profile,http_sessionandstealth_session; the
tests and fixtures of the layer; thejust scrape-gate,imp-gateand
measurerecipes and their mise tasks; thescrapeCI matrix entries and the
impersonateCI job; and CMake, NASM and LLVM from the build requirements. futures-utilwithout its default macros (select!/join!, unused), so the
futures-macroproc macro is no longer compiled.scraperwithout its default features (getoptsfor its own CLI, unused
parse-error text).flate2asks only for thezlib-rsbackendzipalready
builds (pdf-extractstill turns on its default one).- The unused
tracing-futuresdependency (every build compiled it) and the
unusedtracing-actixone from thefull-tracingfeature. - JSON logs come from
tracing-subscriber's own JSON format instead of the
separatejson-subscribercrate. - The
playwrightfeature no longer listsrustwright-coreitself;rustwright
already brings it without Python. - HTTPS uses rustls with the
ringcrypto provider for every client, so the
aws-lc-sysC library is no longer compiled. A program that builds its own
reqwest client next to this crate callsseleniumbase_rs::install_tls_provider()
first. - reqwest no longer builds its
blockingclient: ending reused sessions at
exit uses the async client on a small runtime of its own. thirtyfouris built without itscomponentderive macros and its own
drivermanager(this crate downloads drivers itself), which also removes its
olderzipanddirs.zipis built with deflate only (the format of driver downloads and the
bundled extensions) instead of every format; zstd (a C library), bzip2, lzma,
xz, ppmd and AES no longer compile.
Fixed
- A WebDriver URL with a path and no trailing slash (a Grid's
http://hub:4444/wd/hub, or tauri-plugin-sbrs-automation's
http://127.0.0.1:4445/<token>) lost its last path segment, because the
client joins command paths as relative URLs; the slash is added when
connecting.
Changed
-
The generated browser identity claims Chrome 154, 155 or 156 (was 153 to
155), and its high-entropy Client Hints name a real build per major
(stealth::randomize::KNOWN_FULL_VERSIONS: 155.0.8059.40 measured on macOS
27.0.1 bytests/client_hints_chrome.rs, 154.0.8037.100 and 156.0.8078.35 from
Google's version history) instead ofmajor.0.0.0. Macs claim macOS 15.8.1,
26.7.1, 27.0.0 or 27.0.1; iPhones may claim Safari 26.0 with the frozen
iPhone OS 18_6(Safari 17.7 is gone). The Chrome presets, the--mobile
user agent and the examples claim Chrome 156 (PRESET_CHROMIUM_MAJOR), the
Android ones in Chrome's frozenLinux; Android 10; Kform, and the iPhone
preset is Safari 26.0. Seeds map to other identities than before. -
A fingerprint without Client Hints (the presets) gets its brand list, decoy
brand and full versions fromchromium_hints(major)instead of a fixed
Not(A:Brand24, the Client Hints platform name (Windows, notWin32), and
afullVersionList. -
The
sbrsbanner reads SeleniumBaseRust (figlet small font, 68 columns), and
sbrs --helpandsbrs-mcp --helpsay SeleniumBaseRust. -
The tests' leftover-process checks (Edge, geckodriver, the MCP stdio servers)
list processes withsysinfo(a dev-dependency) instead ofpgrepand
kill -0, so they run on Windows too instead of being skipped there. -
Human-paced input draws its bell-curve values (
stealth::humanize::Rng::gaussian,
used bystealth::behavior) fromrand_distr'sStandardNormalinstead of a
hand-written Box-Muller transform. The distribution is the same, but a seeded
Humanizernow plans different paths and timings than before for the same
seed.humanize::Rngimplementsrand::TryRng(sorand::Rng), and
rand_distris a new dependency.
Added
-
sbrs devtoolslaunches Chrome with its DevTools endpoint open (--port,
default 9222), prints the endpoint and configs for Google's
chrome-devtools-mcpand Microsoft's Playwright MCP (--jsonfor one line of
JSON), and runs until Ctrl-C, which closes the browser. The Pure CDP MCP server
hasget_devtools_endpoint, andBrowser::devtools_endpoint()and
LaunchOptionsBuilder::debugging_portdo the same from Rust. See
docs/tutorials/devtools_mcp.md, which also covers Firefox's and Safari's MCP
servers. -
.github/workflows/release.ymlbuildssbrsandsbrs-mcpfor five targets
on a version tag and attaches the archives, with completions and Warp
workflows, to the release;[package.metadata.binstall]pointscargo binstallat them. Not run yet (GitHub Actions is locked on the account). -
Feature
ocr(off by default):ocr::Ocrreads the text a page shows from
its screenshot (read_page, for aBaseCaseor ansb_cdp::Page) or from PNG
bytes (text_in_png), with ocrs, pure
Rust. Its two models (about 12 MB) are downloaded into the cache folder on
first use and checked against pinned SHA-256 hashes;SB_OCR_MODELSnames a
folder of your own copies instead. -
Browser plugins for the Pure CDP engine (feature
plugins, off by default;
ADR 004 phase 2).LaunchOptionsBuilder::pluginstakes aPluginSetfrom the
plugin API crate
seleniumbase-rs-plugin,
re-exported asseleniumbase_rs::plugin_api.Browser::launchchecks the set,
letsbefore_launchchange the arguments, preferences, extensions, proxy and
headless mode, and callson_browser; every tab the library attaches to gets
the plugins' packages andon_page_created;Browser::close(or the last
drop) callson_close.Browser::connect_with_pluginsdoes the same for a
running browser. A plugin that refuses or fails stops the launch or the tab
with the newSeleniumBaseError::Plugin. Guide:docs/tutorials/plugins.md. -
LaunchOptionsBuilder::extensionloads an unpacked extension over the
protocol (Extensions.loadUnpacked, which branded Chrome 137 and later need
instead of--load-extension), andLaunchOptionsBuilder::preferencemerges
a preference into the profile'sDefault/Preferencesbefore the browser
starts. -
Browser::launch_mocked(featuretest-util) runs the launch steps, plugins
included, against aMockCtrl, whichMockCtrl::newnow creates for a test
to script first. -
Feature
tokio-console(off by default):sbrs,sbrs-mcpand programs that
use the crate's logging serve tokio-console
for watching async tasks. Build withRUSTFLAGS="--cfg tokio_unstable".
seleniumbase-rs 0.2.0
Changed
- The binaries are renamed so they no longer clash with Python
SeleniumBase, which installs its ownsbase: the CLI is nowsbrsand the
MCP serversbrs-mcp(wasseleniumbase-mcp). Commands and flags are
unchanged; replace the name in scripts and MCP client settings, and generate
the shell completions again (sbrs completions zsh > _sbrs). The Docker
image runssbrsas usersbrs.sbase_config.tomlkeeps its name.
Added
sbrs completions figwrites a Fig completion spec, the format of
withfig/autocomplete that Warp's built-in completions, inshellisense and Amazon
Q use. Warp also uses the shell's own completions by default.sbrs completions usageprints a usage (KDL) spec for jdx/usage and mise,
kept incompletions/sbrs.usage.kdland checked against the CLI by a test.- Warp Workflows for common
sbrscommands in.warp/workflows/. - Every docs page has a title in front matter for Docs7; the mdBook build strips it.
- ADR 004 proposes a plugin framework for community crates, in the style of
puppeteer-extra and Zorilla.
seleniumbase-rs 0.1.1
Windows fixes from a second round of runs on a Windows 11 PC (Chrome 154 and
155, Edge 155, Firefox 153). There, formatting, clippy, the whole test suite,
the real-Chrome tests, Edge, Firefox and the Tauri 3 installer build now pass.
Fixed
- The browser version on Windows comes from the
chrome.exe(ormsedge.exe)
that really runs, read from its file version: while an update waited for a
restart, the newest version folder named a Chrome that was not running yet and
the wrong chromedriver was fetched. The folder rule also skips a waiting update. - Download folders are handed to Chrome as plain
C:\...paths, not with the
\\?\prefixcanonicalizeadds on Windows; ablob:download was cancelled. - The tests that list processes with
pgrepskip those checks on Windows.
Added
rust-toolchain.toml, so rustup uses Rust 1.99 in the repository by itself.- The Windows status in
docs/platforms.md. Known gap: on Windows nothing ends a
browser whose program was killed (macOS and Linux have a process-group watcher).
seleniumbase-rs 0.1.0
The first release of seleniumbase-rs, a Rust port of Python SeleniumBase 4.55.2 and its MCP server.
[dependencies]
seleniumbase-rs = { git = "https://github.com/MustCodeAl/seleniumbase-rs", tag = "v0.1.0" }Not published to crates.io.
What is in it
- Two engines.
BaseCaseon WebDriver (thirtyfour) with the SeleniumBase API, andsb_cdp(Browser,Page,Locator), which drives Chrome over the DevTools Protocol with no driver at all. - Drivers started for you: chromedriver for Chrome and other Chromium browsers, geckodriver for Firefox, msedgedriver for Edge (matched to the installed version), operadriver and safaridriver. On macOS and Linux they and their browsers end with the program that started them, also when it is killed.
sbase install/sbase getdownload a driver by name and version. - Parity with Python SeleniumBase: every upstream API has a recorded status in
docs/parity.md: 888 implemented, 383 composed from existing calls, 94 not applicable (with the reason), none planned. sbase, the command-line tool, andseleniumbase-mcp, the MCP server (cdp,driverandsbservers), both tested end to end as the real binaries.- Importers:
sbase import-python(SeleniumBase and Selenium tests),sbase import-playwright(JavaScript, TypeScript, Python, codegen, pytest) andsbase import-puppeteer(scripts, jest-puppeteer, Mocha, Chrome Recorder JSON) write Rust tests; anything they cannot convert stays as a TODO with its line number. - Stealth layer (experimental, features
scrapeandimpersonate): browser-identical TLS, HTTP/2 and headers, an MITM proxy,HttpSessionandStealthSession, with custom JA3/JA4/Akamai inputs. Only Chrome 155 on macOS is measured; other browsers and systems are refused rather than guessed. - Tauri 3: the example desktop app (
examples/tauri-profile-manager) builds on Tauri 3.0.0-alpha.4, and the whole library, every feature on, builds and passes its tests in a Tauri 3 app. - Terminal apps (feature
tui):sbase commander,caseplansandbehave-gui. - Tooling:
mise.toml(toolchain and the gate as tasks),context7.json, a Dockerfile.
How it was checked
- About 3,000 automated tests on macOS (Apple Silicon), with formatting, clippy (
-D warnings) andcargo denyclean. - 144 tests in 28 files against a real Chrome 155, plus real Edge 155 on macOS and Firefox 153 in Docker.
- Linux (arm64, in Docker): the test suite and the real-Chromium tests passed on an earlier commit.
Known limits
- Windows has not been verified yet. The first run on a Windows PC found a startup crash of
sbase(fixed here) and two test problems (fixed here); a full run is in progress. - Stealth fingerprints are measured for Chrome 155 on macOS only.
- Firefox sessions get no proxy, extensions, mobile emulation or CDP/UC modes yet.
- Ad blocking does not cover frames from other sites.
- Most behaviour is checked against mocks; the real-browser tests cover the main paths.
The full list of changes is in CHANGELOG.md.