moli-webdriver-smoke
This is a standalone WebDriver smoke project for moli. It is a peer of
moli-cdp-smoke/: the runner starts a local fixture server, starts
moli serve, and exercises the public WebDriver wire surface from outside
the Rust test harness.
The default suite runs every Moli group: raw WebDriver Classic HTTP, raw BiDi
WebSocket messages, Selenium Python's webdriver.Remote client, cross-engine
semantics and navigation contracts, Moli's URL policy, and renderer-IO script
interruption. This covers both protocol-level wire shape and real Selenium
client workflows without requiring a group allowlist.
The semantics group contains isolated cross-engine contracts. It can
run against Moli's native WebDriver endpoint or an external ChromeDriver;
Moli uses standard W3C capabilities without Chrome vendor options.
The url-policy group is a Moli hosted-product contract. It
uses isolated Classic and BiDi sessions to verify the exact local-file
navigation error envelopes, unchanged browsing contexts, and absence of BiDi
navigation lifecycle events. It is deliberately skipped for desktop Chromium,
whose browser-granted local-file capability is a different product policy.
The navigation-errors group is a cross-engine Chromium/WPT contract.
It runs the same 37-case public-wire matrix against Moli or ChromeDriver:
ten Classic malformed-body/URL cases, 22 BiDi invalid-argument cases, two
missing-context cases, and three real address failures. It also checks the W3C
HTTP and BiDi error envelopes, unchanged Classic URL, and post-failure session
liveness. The baseline was executed on 2026-08-09 with Debian Chromium
145.0.7632.116 and matching ChromeDriver 145.0.7632.117.
The script-timeout boundary has two deliberately separate groups:
script-timeout-chromiumis the ChromeDriver 147 oracle. A yielding async script times out near its deadline, while a bounded non-yielding loop first returns to the renderer and only then producesscript timeout.script-interruptis a default Moli group for the renderer-IO extension. It runs actual infinite loops through both Classic sync and async execution, requires timeout-drivenRuntime.terminateExecutionnear the configured deadline, and repeats recovery work in the same window after every termination.
The split is intentional: the Moli group proves preemption, while the ChromeDriver group records that ChromeDriver itself does not offer the same non-yielding WebDriver timeout behavior.
Coverage
- WebDriver Classic status/session/delete value envelopes.
- Classic navigation, current URL, title, page source, CSS/XPath element lookup and identity, element
text/tag/displayed/enabled/rect/attribute/property/computed label/computed
role, send keys, clear form controls with input/change events, file upload, click, execute script,
key actions whose handler performs a top-level Page replacement while the action request is completing,
explicit screenshot unsupported errors, cookies, alerts, unhandled prompt behavior, window-scoped prompt
switching, shadow roots, page-side SharedWorker probe without polluting window
handles,
document.open()replacement stale-element behavior, and headless window state surface. - WebDriver BiDi
session.status,session.new,browsingContext.create,session.subscribe,browsingContext.navigate, DOMContentLoaded lifecycle events,input.performActions,input.releaseActions, key actions that trigger a top-level Page replacement and remain successful through the replacement lifecycle, element-origin input action routing without asserting layout hit-test effects,input.setFiles,network.getData,browser.setDownloadBehavior,browsingContext.downloadWillBegin/downloadEnd,network.setCacheBehavior, SharedWorkerbrowsingContext.contextCreated, livescript.realmCreated(type = "shared-worker"), andscript.getRealms(type = "shared-worker"), user context creation/removal, user-context viewport inheritance, explicitbrowsingContext.captureScreenshotunsupported errors,emulation.setUserAgentOverride,emulation.setLocaleOverride,emulation.setTimezoneOverride,emulation.setNetworkConditions, storage isolation,storage.setCookie,storage.getCookies, andsession.end. - Selenium Python Remote WebDriver session creation, Selenium BiDi facade
webSocketUrlattachment withbrowsing_contextcreate/navigate/get_tree/ locate_nodes, browsing context fragment/history event handlers,scriptevaluate/console/javascript-error handlers, add/remove preload scripts with context filtering, channel arguments, sandbox realms, and userContext filtering,getRealms, root-owned script handles,callFunction,disown, special number/BigInt serialization, nested handle/local-value arguments,network.add_request_handler()request interception,network.add_auth_handler()Basic Auth continuation,browseruser context/client-window, andemulationuser-context UA/locale/timezone/viewport/network-conditions workflows, navigation, title/current URL, page source, element text/tag/attribute/property/css/displayed/enabled/ rect, link text locators, CSS/XPath user-facing locator workflows, accessible name, computed ARIA role, label-associated controls including link/img/select/textarea/input-button naming, send keys, clear, execute sync/async scripts with primitive/null/array/WebElement results, WebElement arguments, timeout/error recovery, and nested WebElement return values, Selenium client-side script pin/list/unpin workflows, Selenium text handling for child/block/inline content, whitespace, NBSP, hidden/script text exclusion, Selenium relative locators vialocate_with(), fetch/XHR through async scripts, localStorage, sessionStorage, IndexedDB, Selenium remote downloads via/se/files, frame switching by index/name/id/WebElement with nested parent/default-content recovery and relative lookup errors, deleted-frame recovery, SeleniumSelectsingle/multi-select option lookup, select/deselect, disabled/hidden option errors, form submit via SeleniumWebElement.submit()and submit-button click, checkbox/radio selection, text input/textarea typing and clear input/change events, all-printable key input, text-controlchangecommit on blur, WebDriver private-key caret navigation/PageUp/PageDown, arrow-key legacyKeyboardEvent.keyCode, selection/delete/backspace, numpad, and function-key send keys, direct stale element reference errors after navigation/DOM removal/document replacement, implicit waits for single and multiple element lookup, WebDriverWait expected conditions for presence/visibility/text/clickability/invisibility/staleness/ selection/value/attribute/frame/alert, W3C keyboard actions, and explicit unsupported-operation errors for pointer/wheel actions including hover/double-click/context-click/drag-drop, Selenium remote file upload via/se/filewith FileReader content reads, action key input with Shift modifiers, explicit screenshot/element screenshot and print_page unsupported errors, cookie management including SameSite, expired cookies, delete-all, and exact-name deletion, window handles/new/switch/close/rect/ maximize/minimize, anchor/script popup windows, named window target reuse,window.open(..., "_self"), SeleniumShadowRootlookup/scoped find/ execute_script equality/no-such-shadow-root errors, alert/confirm/prompt text, and alert accept/dismiss/send keys. - Cross-engine Selenium semantics for capability matching, same-document realm and WebElement identity, cross-document stale elements, top-level and popup storage ownership, nested frame recovery, shadow-frame Window named access, dialog return values, and standard W3C error classes. Each scenario uses an independent session and emits a structured contract record.
- Hosted local-file navigation rejection as Classic HTTP
500with W3Cunknown error, and as a BiDiunknown error; both retainabout:blank, and the BiDi path emits no immediate or delayed navigation lifecycle event. - Chromium/WPT navigation failure shapes: Classic HTTP
400invalid argumentenvelopes for null/missing/non-string/relative/malformed URLs; BiDiinvalid argumentfor context, URL, and wait validation;no such framefor missing context ids;unknown errorfor unsupported schemes, DNS failure, and unsafe ports; and a usable session after the complete failure matrix.
Known remaining worker-surface gap: the BiDi SharedWorker smoke now requires
target context creation, live realm creation, and script.getRealms; richer
lifecycle, error, and teardown coverage is still pending.
Usage
Build moli first when no binary exists:
cargo build -p moli
Then run the default smoke:
cd moli-webdriver-smoke
uv sync
uv run moli-webdriver-smoke
With no --group, both local and CI runs execute all seven Moli groups:
classic,bidi,selenium,semantics,url-policy,navigation-errors,script-interrupt
CI adds --continue-on-failure so one failing group does not hide later smoke
results. The ChromeDriver-only script-timeout-chromium oracle remains an
explicit external-target run.
List available groups:
uv run moli-webdriver-smoke --list-groups
Run focused groups:
uv run moli-webdriver-smoke --group classic
uv run moli-webdriver-smoke --group bidi
uv run moli-webdriver-smoke --group selenium
uv run moli-webdriver-smoke --group semantics --continue-on-failure
uv run moli-webdriver-smoke --group url-policy --continue-on-failure
uv run moli-webdriver-smoke --group navigation-errors --continue-on-failure
uv run moli-webdriver-smoke --group script-interrupt
MOLI_WEBDRIVER_SMOKE_GROUPS=classic,bidi,selenium uv run moli-webdriver-smoke
Collect all subgroup failures in one diagnostic run:
uv run moli-webdriver-smoke \
--continue-on-failure
Run the same semantics group against a matching local Chrome/ChromeDriver
build:
~/chromium/src/out/Default/chromedriver --port=9515
uv run moli-webdriver-smoke \
--endpoint http://127.0.0.1:9515 \
--browser-name chrome \
--browser-binary ~/chromium/src/out/Default/chrome \
--group semantics --continue-on-failure
Run the bounded ChromeDriver script-timeout oracle against the local Chromium 147 pair:
~/chromium/src/out/Default/chromedriver --port=9515
uv run moli-webdriver-smoke \
--endpoint http://127.0.0.1:9515 \
--browser-name chrome \
--browser-binary ~/chromium/src/out/Default/chrome \
--group script-timeout-chromium
Run Moli's non-yielding sync/async termination contract against moli serve:
uv run moli-webdriver-smoke --group script-interrupt
Run the Chromium/WPT navigation failure matrix against any matching local Chrome/ChromeDriver pair (the driver and browser major versions must agree):
/path/to/chromedriver-145.0.7632.117 --port=9516
uv run moli-webdriver-smoke \
--endpoint http://127.0.0.1:9516 \
--browser-name chrome \
--browser-binary /usr/bin/chromium \
--group navigation-errors
Useful environment variables:
MOLI_BIN: path to themolibinary under test.MOLI_WEBDRIVER_PORT: WebDriver server port. Defaults to a free local port.MOLI_WEBDRIVER_SMOKE_GROUPS: comma-separated smoke group list. CLI--grouptakes precedence.MOLI_WEBDRIVER_SMOKE_CONTINUE_ON_FAILURE=1: equivalent to--continue-on-failure.MOLI_WEBDRIVER_SMOKE_TRACE_BG=1: print backgroundmoli servelogs.
If both debug and release binaries exist, the runner uses the newest one by file
modification time. Set MOLI_BIN when you need an exact binary.