Skip to content

Releases: MustCodeAl/seleniumbase-rs

v0.6.0

Choose a tag to compare

@MustCodeAl MustCodeAl released this 23 Oct 09:41

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 --locked

Breaking

  • Downloads go to ./downloaded_files, as in Python SeleniumBase. BaseCase::get_downloads_folder,
    assert_downloaded_file, download_file and the other download helpers used the OS Downloads
    folder; they now use downloaded_files in 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_download and download.directory_upgrade,
    and the Firefox browser.download.* ones; a preference you set yourself wins), and the pure CDP
    engine sends Browser.setDownloadBehavior at launch, so Page::assert_downloaded_file finds a
    file without a folder being chosen first. downloaded_files/ is in .gitignore.
  • A reused browser keeps its cookies unless --crumbs. With --reuse-session or --rcs the
    browser handed to the next test used to have every cookie deleted; now the cookies stay (Python's
    default) and SB_CRUMBS=1 or browser::reuse::set_crumbs(true) deletes them. The other windows
    are still closed, the page's storage cleared and the page set to about: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 on about:blank, which no one wants. Without a start page (--url,
    SB_START_PAGE or the settings file) they now stop with error: 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. cdp needs 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-storage and cache-clear changed a profile that a fresh browser throws away
    at the end; they now stop with error: 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-css and throttle3g (an unrecognised
    subcommand now). Several steps in one browser are run-scenario, deferred and the MCP
    server. export-recording stays: 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-text and record stop with error: NAME opens its own URL; leave out --start-page when the command line names a start page; one that comes
    from SB_START_PAGE or the file is only a default and is ignored for them.
  • devtools, choose-file and deferred read the global options. They no longer have
    an option of their own for --headless, --user-data-dir, --proxy or --url; the
    global ones of the same names do the same work, and give the page to open (choose-file
    and deferred act on the start page, a blank upload page when choose-file has 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::breakpoint and
    common::debug_prompt.
    sb.breakpoint("message").await? pauses a test with the browser
    open and reads commands from the terminal: c goes on, s saves a screenshot and src the
    page source to latest_logs/<test id>/, u shows the address and title, js <expr> shows
    a value, q stops the test with a TestLifecycle error and h lists 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), and SB_BREAKPOINTS=0 turns
    it off, as Python's PYTHONBREAKPOINT=0 does. DebugPrompt, PromptKind, PromptOutcome,
    PromptPage and LineInput are public, for the runner's --trace, --pdb and
    --final-debug (not wired to a flag yet). BaseCase::recorder_mode says whether a
    recording window is open.

  • A command line that does not parse prints the command's page, then the error. Like
    Python's sbase, sbrs mkfile, sbrs grid-hub, sbrs get-text and sbrs plugin info
    print the usage, arguments, flags and examples of the command the words reached, a blank
    line, and last the parser's error: line and tip:, 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, --version and shell completion
    are as before.

  • Every command has an example and a help heading. sbrs <command> --help ends 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 in sbrs --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 by parse_chromium_args), with_disable_features and with_host_resolver_rules
    replace composing extra_args by hand. --incognito and --guest are 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 -private and -devtools, and
    security.csp.enable for --disable-csp; Firefox and Safari are maximized once running.

  • LocaleCode reads a language code in Python's form (ar_eg) or Chrome's (ar-EG) and
    writes Chrome's.

  • The start page: --start-page, --url and --start_page, SB_START_PAGE, and the
    start_page key of the settings file.
    Every sbrs command 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 over SB_START_PAGE, which wins over the file;
    a bare host gets https:// and a local file becomes a file:// URL. sbrs test --url X
    hands it on to the tests as SB_START_PAGE. The library side is Settings::start_page
    (which to_browser_config copies into BrowserConfig::start_page),
    SettingsOverrides::start_page and START_PAGE_ENV.

  • --save-screenshot saves a screenshot to latest_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 sbrs itself is global. sbrs get-title --headless --url https://example.com works as well as sbrs --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 with quit, 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
    Fingerprint JSON, described by the new schemas/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 a CapturedFingerprint (the fingerprint, the readings no setting holds in
    unmapped, and a ProbeInfo with the page, the time and the browser version);
    compare_fingerprint checks 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,
    mode 0600), from_json, to_json_pretty, diff (SettingDiff), compare_to
    (FingerprintComparison) and claimed_settings are 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 FILE reports the errors and warnings of a file (status 2 when
    there is an error); diff A B lists the settings where B differs from what A expects
    (status 1 when any does); presets lists the presets.

  • The global --fingerprint FILE, SB_FINGERPRINT and 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...

Read more

seleniumbase-rs 0.5.1

Choose a tag to compare

@MustCodeAl MustCodeAl released this 17 Oct 16:27

A patch release of the command line: it refuses again what clap refused in 0.4.0,
and sbrs-mcp names itself.

Changed

  • sbrs-mcp names itself: sbrs-mcp --version prints sbrs-mcp 0.5.1, and its help
    and the usage line of an error say sbrs-mcp, where they said seleniumbase-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 accepted true and false
    there and ignored them.
  • sbrs help nosuch, and help with any path that names no command, is an error with
    status 2 and an unrecognized subcommand message again. 0.5.0 printed the help of the
    last command it found, with status 0.

seleniumbase-rs 0.5.0

Choose a tag to compare

@MustCodeAl MustCodeAl released this 17 Oct 15:17

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

  • sbrs and sbrs-mcp are built by the new workspace package
    seleniumbase-rs-cli (crates/cli), not by seleniumbase-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_complete or clap_complete_fig (15 crates
    fewer in a default build). The commands, flags, aliases, defaults and exit
    statuses of sbrs are the same (its parser changed, see Changed), with one
    difference: a plain cargo build, cargo install or cargo run now includes
    pdf, so sbrs pdf-text and sbrs assert-pdf-text work 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 pdf and mcp-server on by default (the release archives
    always had them), and plugins-builtin (the built-in browser plugins, see
    Added), and forwards tui, gui, turso, test-util,
    full-tracing and tokio-console to the library, so
    cargo install ... seleniumbase-rs-cli --features tui is the way to add the
    terminal UI. default-members names both packages, so cargo build,
    cargo test and cargo run --bin sbrs at the repository root work as before;
    use -p seleniumbase-rs for the library alone. The release archives keep their
    names (seleniumbase-rs-v<version>-<target>). The messages sbrs prints for a
    build without tui or pdf now name the package that builds it
    (cargo install --git https://github.com/MustCodeAl/seleniumbase-rs seleniumbase-rs-cli --features tui), instead of seleniumbase-rs, which no
    longer has a binary. The Homebrew formula has to build --path crates/cli from
    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_text and the module api::pdf need it; printing a page to PDF
    (print_to_pdf, save_as_pdf) does not. sbrs pdf-text and
    assert-pdf-text stay in --help and, 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_text and 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_visible and wait_for_exact_text_not_visible swapped
    their first two arguments (a silent change: both are &str).
    wait_for_text_visible, wait_for_text_not_visible and wait_for_exact_text
    gained the selector they lacked, so an old call stops compiling. Pass "body"
    to look at the whole page, as the old wait_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_text now 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_text and
    delayed_assert_text took the selector first. delayed_assert_text also
    passed its arguments on in the other order, so delayed_assert_text("Welcome", "h1") looked for an element called Welcome; all three now follow Python's
    (text, selector), like deferred_assert_text.

    // before
    sb.deferred_assert_exact_text("#status", "Ready").await?;
    // after
    sb.deferred_assert_exact_text("Ready", "#status").await?;
  • find_text, find_exact_text and find_non_empty_text follow Python.
    They took no selector, searched all of <body> for the deepest element that
    held the text, and returned Result<Option<WebElement>> (the Option was
    never None). They now take the selector and return Result<WebElement>,
    the element of that selector, as Python's find_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's post_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, and switch_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
    DriverId now names a browser for good, and switch_to_default_driver()
    always reaches the first one. Changes:

    • get_new_driver() takes no argument and get_new_driver_with(config) takes
      the configuration (no more None); 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) refuses DriverId::DEFAULT (that browser is
      closed by quit()); 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 no u64. It is
    sleep, as in Python, and takes impl Into<f64>: wait(2) and wait(0.5)
    compile, but a u64 variable (or a usize, an i64) 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 a u8, not an f64: check_window("home", 3)
    instead of check_window("home", 3.0); the same for deferred_check_window
    and delayed_check_window. A float literal stops compiling. As before, a
    level other than 0, 1, 2 or 3 is an InvalidConfig error.

    // 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) and load_cookies(path) took any file path, so a call
    written like Python's (save_cookies("login")) wrote a file called login
    in the working directory. They now take a name: the file is
    saved_cookies/<name>.txt in the working directory (only the last part of
    the name is used, `.tx...

Read more

seleniumbase-rs 0.4.0

Choose a tag to compare

@MustCodeAl MustCodeAl released this 17 Oct 00:54

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_plugins gives a PluginSet to
    BaseCase::new and BrowserSession::connect: the set is checked against the
    browser, before_launch changes 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
    get on_browser, then the packages and on_page_created for the first
    window, for each window switch_to_new_window or open_new_window opens, and
    for any other on apply_plugins_to_window; quit or a drop calls
    on_close, and reconnect runs the hooks again. On Chromium browsers
    plugins send protocol commands through chromedriver's DevTools channel; on
    Firefox and Safari send is refused and scripts for every document run at
    once and again after each open. A refusal or a failing hook stops the
    session with SeleniumBaseError::Plugin. Plugins with reuse_session are
    refused. See the WebDriver section of docs/tutorials/plugins.md.
  • sb_cdp::Page::add_script_tag and add_style_tag add a script or a
    stylesheet to the current document from a URL or inline (TagSource),
    waiting for a file to load; add_init_script runs a script in every
    document the tab loads from then on and returns an InitScriptId for
    remove_init_script. Playwright's addScriptTag, addStyleTag and
    addInitScript.
  • With the plugins feature, sb_cdp::Page implements 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 visual feature:
    visual::VisualBaseline saves 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 with AssertionFailed and leaving <name>.actual.png and
    a <name>.diff.png that paints the differences red. Screenshots of another
    size fail without a comparison. check_page takes a BaseCase or an
    sb_cdp::Page, and BaseCase::assert_visual_match(name) keeps its baselines
    in visual_baseline/<test>/, <test> being the test's whole path (test
    binary, module path and function, as cart.checkout.renders), and follows
    with_visual_baseline_reset.
    check_window is 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 a HarRecorder,
    and HarRecorder::finish() returns a Har (HTTP Archive 1.2 serde types)
    that Har::save(path) writes as JSON. Entries come from the Network
    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/receive timings; each
    top-level navigation is a page with onContentLoad and onLoad. 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 prompt prints 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 --ascii marks and a
    --format of {browser}, {major}, {driver}, {mark} and
    {driver_major}. It starts no browser and uses no network: versions come
    from files (a macOS app's Info.plist, the version folder beside
    chrome.exe), and one that only --version tells is cached for ten minutes
    in the user cache folder. It prints nothing and exits with 0 when no browser
    is found (--verbose says 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_running reads a program's major
    version from its files only, and version_flag_major runs --version with 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 for sbrs, 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.
  • sbrs exits 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

Choose a tag to compare

@MustCodeAl MustCodeAl released this 15 Oct 22:38

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 features scrape and impersonate, the modules seleniumbase_rs::wire,
    seleniumbase_rs::scrape and seleniumbase_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,
    EphemeralCa and the rest) are gone from this crate. They are now the crates
    seleniumbase-rs-wire (was the module wire), seleniumbase-rs-scrape (was
    the feature scrape) and seleniumbase-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 give seleniumbase-rs by 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-util and http-body-util; the
    examples capture_tls, capture_hints, capture_h2, cookie_jar,
    browser_scraper, custom_profile, http_session and stealth_session; the
    tests and fixtures of the layer; the just scrape-gate, imp-gate and
    measure recipes and their mise tasks; the scrape CI matrix entries and the
    impersonate CI job; and CMake, NASM and LLVM from the build requirements.
  • futures-util without its default macros (select!/join!, unused), so the
    futures-macro proc macro is no longer compiled.
  • scraper without its default features (getopts for its own CLI, unused
    parse-error text). flate2 asks only for the zlib-rs backend zip already
    builds (pdf-extract still turns on its default one).
  • The unused tracing-futures dependency (every build compiled it) and the
    unused tracing-actix one from the full-tracing feature.
  • JSON logs come from tracing-subscriber's own JSON format instead of the
    separate json-subscriber crate.
  • The playwright feature no longer lists rustwright-core itself; rustwright
    already brings it without Python.
  • HTTPS uses rustls with the ring crypto provider for every client, so the
    aws-lc-sys C library is no longer compiled. A program that builds its own
    reqwest client next to this crate calls seleniumbase_rs::install_tls_provider()
    first.
  • reqwest no longer builds its blocking client: ending reused sessions at
    exit uses the async client on a small runtime of its own.
  • thirtyfour is built without its component derive macros and its own
    driver manager (this crate downloads drivers itself), which also removes its
    older zip and dirs.
  • zip is 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 by tests/client_hints_chrome.rs, 154.0.8037.100 and 156.0.8078.35 from
    Google's version history) instead of major.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 frozen Linux; Android 10; K form, 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 from chromium_hints(major) instead of a fixed
    Not(A:Brand 24, the Client Hints platform name (Windows, not Win32), and
    a fullVersionList.

  • The sbrs banner reads SeleniumBaseRust (figlet small font, 68 columns), and
    sbrs --help and sbrs-mcp --help say SeleniumBaseRust.

  • The tests' leftover-process checks (Edge, geckodriver, the MCP stdio servers)
    list processes with sysinfo (a dev-dependency) instead of pgrep and
    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 by stealth::behavior) from rand_distr's StandardNormal instead of a
    hand-written Box-Muller transform. The distribution is the same, but a seeded
    Humanizer now plans different paths and timings than before for the same
    seed. humanize::Rng implements rand::TryRng (so rand::Rng), and
    rand_distr is a new dependency.

Added

  • sbrs devtools launches Chrome with its DevTools endpoint open (--port,
    default 9222), prints the endpoint and configs for Google's
    chrome-devtools-mcp and Microsoft's Playwright MCP (--json for one line of
    JSON), and runs until Ctrl-C, which closes the browser. The Pure CDP MCP server
    has get_devtools_endpoint, and Browser::devtools_endpoint() and
    LaunchOptionsBuilder::debugging_port do the same from Rust. See
    docs/tutorials/devtools_mcp.md, which also covers Firefox's and Safari's MCP
    servers.

  • .github/workflows/release.yml builds sbrs and sbrs-mcp for five targets
    on a version tag and attaches the archives, with completions and Warp
    workflows, to the release; [package.metadata.binstall] points cargo binstall at them. Not run yet (GitHub Actions is locked on the account).

  • Feature ocr (off by default): ocr::Ocr reads the text a page shows from
    its screenshot (read_page, for a BaseCase or an sb_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_MODELS names a
    folder of your own copies instead.

  • Browser plugins for the Pure CDP engine (feature plugins, off by default;
    ADR 004 phase 2). LaunchOptionsBuilder::plugins takes a PluginSet from the
    plugin API crate
    seleniumbase-rs-plugin,
    re-exported as seleniumbase_rs::plugin_api. Browser::launch checks the set,
    lets before_launch change the arguments, preferences, extensions, proxy and
    headless mode, and calls on_browser; every tab the library attaches to gets
    the plugins' packages and on_page_created; Browser::close (or the last
    drop) calls on_close. Browser::connect_with_plugins does the same for a
    running browser. A plugin that refuses or fails stops the launch or the tab
    with the new SeleniumBaseError::Plugin. Guide: docs/tutorials/plugins.md.

  • LaunchOptionsBuilder::extension loads an unpacked extension over the
    protocol (Extensions.loadUnpacked, which branded Chrome 137 and later need
    instead of --load-extension), and LaunchOptionsBuilder::preference merges
    a preference into the profile's Default/Preferences before the browser
    starts.

  • Browser::launch_mocked (feature test-util) runs the launch steps, plugins
    included, against a MockCtrl, which MockCtrl::new now creates for a test
    to script first.

  • Feature tokio-console (off by default): sbrs, sbrs-mcp and programs that
    use the crate's logging serve tokio-console
    for watching async tasks. Build with RUSTFLAGS="--cfg tokio_unstable".

seleniumbase-rs 0.2.0

Choose a tag to compare

@MustCodeAl MustCodeAl released this 13 Oct 02:42

Changed

  • The binaries are renamed so they no longer clash with Python
    SeleniumBase, which installs its own sbase: the CLI is now sbrs and the
    MCP server sbrs-mcp (was seleniumbase-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 runs sbrs as user sbrs. sbase_config.toml keeps its name.

Added

  • sbrs completions fig writes 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 usage prints a usage (KDL) spec for jdx/usage and mise,
    kept in completions/sbrs.usage.kdl and checked against the CLI by a test.
  • Warp Workflows for common sbrs commands 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

Choose a tag to compare

@MustCodeAl MustCodeAl released this 12 Oct 23:25

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 (or msedge.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
    \\?\ prefix canonicalize adds on Windows; a blob: download was cancelled.
  • The tests that list processes with pgrep skip 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

Choose a tag to compare

@MustCodeAl MustCodeAl released this 12 Oct 22:48

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. BaseCase on WebDriver (thirtyfour) with the SeleniumBase API, and sb_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 get download 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, and seleniumbase-mcp, the MCP server (cdp, driver and sb servers), 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) and sbase 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 scrape and impersonate): browser-identical TLS, HTTP/2 and headers, an MITM proxy, HttpSession and StealthSession, 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, caseplans and behave-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) and cargo deny clean.
  • 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.