Status#
Accepted
Context#
The port needs to support both WebDriver (thirtyfour) and a Playwright-compatible backend (the rustwright crate), plus multiple simultaneous browser sessions.
Decision#
BaseCaseowns one activeBrowserSession(a WebDriver handle plus an optional CDP client) and a table of the other browsers it opened, each named by aDriverId.BaseCase::get_new_driver()(orget_new_driver_with(config)) connects a new session, makes it the active one and returns itsDriverId. The browser the case started with isDriverId::DEFAULT; the others are numbered in the order they were opened, and an id is never given to another browser. The session that was active is parked in the table.switch_to_driver(id)makes that browser the active one, so element and assertion helpers act on it, and parks the one you left; it does nothing whenidis the active browser, so it can be repeated. An id the case never gave out, or one whose browser was quit, returnsSeleniumBaseError::InvalidConfigand changes nothing.switch_to_default_driver()isswitch_to_driver(DriverId::DEFAULT): it always reaches the browser the case started with, as Python’sswitch_to_default_driverdoes with its fixed default driver.quit_extra_driver(id)quits a browser other than the default one. When it is the active browser, the case falls back to the default one, as in Python.driver_count()is the number of browsers still open, the active one included.BaseCase::quit()quits every browser the case opened, the parked ones too.- Playwright support is gated behind the
playwrightfeature flag, which pulls inrustwrightandrustwright-core. When enabled, a separatePlaywrightSession(src/browser/playwright.rs, with thin helpers insrc/api/playwright.rs) drives a Chromium launched byrustwright.BaseCaseholds an optional one, started withBaseCase::activate_playwright_mode()and read back withBaseCase::playwright_session(); both exist only with the feature, and none of the WebDriver helpers act on it.activate_playwright_modestarts the second Chromium as the case’sBrowserConfigsays (without its profile folder, which the WebDriver browser holds), andBaseCase::quitcloses it.
Consequences#
- Positive: Feature flags keep the default dependency graph small. No feature is on by default.
- Positive: Driver switching is explicit and test scripts can reason about which session is active.
- Positive: A session is never lost by switching.
DriverTable::switch(insrc/api/base_case_impls/base_case_impl_drivers.rs) parks the session you leave and brings the other in, so you can always return to it by its id. This matters because dropping aBrowserSessioncloses its browser in the background. - Positive: A
DriverIdnames one browser for as long as the case lives. It does not move when you switch, and a quit browser leaves a marker in the table, so switching to it is an error rather than a panic on a disconnected session. - Negative: The ids are numbers in the order the browsers were opened, not names you choose; keep the id
get_new_driverreturns. - Negative: Helpers that depend on Playwright-specific behavior live on
PlaywrightSession, requiring users to learn two APIs.
Later addition: the pure CDP engine#
src/sb_cdp is a third, independent backend: it launches Chrome and speaks the DevTools Protocol directly (Browser, Page, Locator, with BrowserContext, BrowserPool and SessionStore on top). It has no chromedriver and no thirtyfour dependency, is always compiled, and is not reached through BaseCase. See the Pure CDP guide.
Verified against commit 8e9180f (multi-driver behaviour: src/api/base_case_impls/base_case_impl_drivers.rs; Playwright gating: Cargo.toml, src/api/base_case.rs).