01
Native, not simulated
Drive the same window, DOM, dialogs, frames, shadow roots, and input surfaces your users interact with.
Native Tauri automation
Run complex end-to-end tests against the real WKWebView, WebView2, and WebKitGTK surfaces in your desktop app—with standard W3C clients and deterministic app lifecycle management.
$ cargo add tauri-wd \
--optional
added tauri-wd to dependencies
Built for real applications
Keep your test code portable while the embedded engine handles each operating system’s native webview API.
01
Drive the same window, DOM, dialogs, frames, shadow roots, and input surfaces your users interact with.
02
Use WebdriverIO, Selenium, Fantoccini, or any current client that speaks the W3C WebDriver protocol.
03
Each session launches one app process, isolates its test directory, forwards logs, and cleans up the full process tree.
Command coverage
Architecture
The public driver owns sessions and process safety. A test-only plugin translates W3C commands inside the app.
Sends ordinary W3C commands to a stable loopback endpoint.
Merges capabilities, launches the binary, queues commands, bounds data, and reaps failures.
Executes commands through WKWebView, WebView2, or WebKitGTK.
Setup
Gate the plugin behind a Cargo feature, build the test binary, then point your preferred client at the driver.
Use a dedicated feature so release binaries never contain an automation server.
[features]
e2e = ["dep:tauri-wd"]
[dependencies]
tauri-wd = {
version = "0.1", optional = true
}
The plugin remains inert unless tauri-wd launches
the process with a private automation token.
let builder = tauri::Builder::default();
#[cfg(feature = "e2e")]
let builder = builder.plugin(
tauri_wd::init(),
);
Pass the current test binary through the namespaced
tauri:options capability.
export const config = {
hostname: "127.0.0.1",
port: 4444,
capabilities: [{
"tauri:options": {
application: "./target/debug/my-app",
},
}],
};
Current protocol only
The driver merges alwaysMatch with one compatible
firstMatch candidate.
desiredCapabilities and launcher aliases are rejected.
| Field | Type | Purpose |
|---|---|---|
application |
string · required | Path to the current test-enabled executable. |
args |
string[] | Arguments passed directly to the app process. |
env |
object | String environment values scoped to this session. |
cwd |
string | Working directory for the app process. |
startupTimeout |
integer · ms | Maximum time to bind the plugin and verify JavaScript responsiveness. |
headless |
boolean | Run without a visible window. On macOS the window stays on screen but transparent, click-through and never focused, so the webview keeps rendering; elsewhere it is hidden. |
Native JavaScript evaluation, snapshots, PDF output, dialogs, and window control.
Serialized script execution, native message callbacks, actions, and process-tree cleanup.
WebKit automation under a real display or Xvfb, with native screenshots and PDF.
Secure by construction
The driver can launch an executable and supply environment variables, so the network boundary and build boundary are intentionally narrow.
Reliability model
The plugin owns the listener before it announces readiness.
Concurrent client calls cannot corrupt native script state.
Dead apps release session capacity without waiting for another command.
Questions
Yes. The public endpoint uses the current W3C protocol.
WebdriverIO, Selenium, and Fantoccini can all create sessions with
tauri:options.
It should not. Keep the plugin optional and enable it only in a dedicated test build. It also remains inert unless the driver supplies the automation environment and token.
Yes. Each session has its own app process, private plugin
endpoint, token, command queue, and isolated automation directory.
Set --max-sessions according to runner capacity.
WebDriver controls the webview, not OS-owned surfaces such as a
native file picker. In an e2e build, use
automation_enabled() to expose a deterministic
test-only command or fixture path for that boundary.
The driver notices the process exit in the background, removes the session, releases capacity, and reports an invalid or failed session instead of poisoning future tests.
Ready to test the whole app?