The tauri-plugin-wdio is a required Tauri plugin that enables WebdriverIO testing of Tauri applications. It provides:
- Execute API - Run JavaScript code from tests with access to Tauri APIs
- Mocking Support - Mock Tauri backend commands for isolated testing
- Log Forwarding - Capture console logs from both frontend and backend
- Invoke Interception - Enable mocking without backend command modifications
There are two Tauri plugins for WebdriverIO testing:
| Plugin | Package | Purpose | Required |
|---|---|---|---|
tauri-plugin-wdio |
Rust + JS | Execute API, mocking, log forwarding | Yes |
tauri-plugin-wdio-webdriver |
Rust only | Embedded WebDriver server | Only for embedded provider |
-
tauri-plugin-wdio- Always required for:- Executing JavaScript in the frontend (
browser.tauri.execute()) - Mocking Tauri commands (
browser.tauri.mock()) - Capturing frontend and backend logs
- Executing JavaScript in the frontend (
-
tauri-plugin-wdio-webdriver- Required when using the embedded driver provider:- Provides built-in WebDriver HTTP server
- Eliminates need for external tauri-driver
- Enables native macOS testing without CrabNebula
- Auto-detected on macOS; signal via
TAURI_WEBDRIVER_PORTon Windows/Linux
The @wdio/tauri-service explicitly checks for the plugin and will fail if it's not available:
Error: Tauri plugin not available. Make sure @wdio/tauri-plugin is installed and registered in your Tauri app.
Only basic WebDriver operations work without the plugin:
- Element interactions (click, type, etc.)
- Navigation and page access
- Basic WebDriver commands
All advanced testing features require the plugin:
- ✅
browser.tauri.execute()- Execute JavaScript with Tauri APIs - ✅
browser.tauri.mock()- Mock backend commands - ✅ All mocking operations
- ✅ Console log capture
- ✅ Backend log forwarding
Add the plugin to your src-tauri/Cargo.toml:
[dependencies]
tauri = { version = "2.9", features = ["..your other features.."] }
tauri-plugin-wdio = { path = "../../packages/tauri-plugin" }
# Or when published to crates.io:
# tauri-plugin-wdio = "1.0"Add the plugin initialization to your src-tauri/src/main.rs:
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_wdio::init()) // Add this line
.run(tauri::generate_context!())
.expect("error while running tauri application");
}Place it anywhere in the plugin chain - typically after other plugins.
The plugin requires WDIO permissions in your capabilities. Edit src-tauri/capabilities/default.json:
{
"identifier": "default",
"windows": ["main"],
"permissions": [
"core:default",
"core:window:default",
"wdio:default"
]
}Or use individual permissions if you prefer fine-grained control:
{
"permissions": [
"core:default",
"core:window:default",
"wdio:allow-execute",
"wdio:allow-log-frontend",
"wdio:allow-set-mock",
"wdio:allow-get-mock",
"wdio:allow-clear-mocks",
"wdio:allow-reset-mocks",
"wdio:allow-restore-mocks"
]
}Make sure withGlobalTauri is enabled in src-tauri/tauri.conf.json:
{
"app": {
"withGlobalTauri": true,
"windows": [
{
"title": "My App",
"width": 800,
"height": 600
}
],
"security": {
"capabilities": ["default"]
}
}
}Import the frontend plugin in your main frontend entry file (usually index.html or main.ts):
In HTML:
<script type="module">
import '@wdio/tauri-plugin';
</script>In TypeScript/JavaScript:
import '@wdio/tauri-plugin';This import must happen before your tests run. The plugin auto-initializes on import and sets up:
window.wdioTauriAPI- Console forwarding
- Invoke interception for mocking
- Event listeners for backend logs
Build your Tauri app:
cd src-tauri
cargo build --releaseThe build should complete successfully without errors about the plugin.
After starting your test session, the plugin will log initialization messages:
[WDIO Plugin] Initializing...
[WDIO Plugin] Successfully initialized
In a test, check if the plugin is available:
it('should have plugin available', async () => {
const available = await browser.tauri.isTauriApiAvailable?.();
expect(available).toBe(true);
});The execute API will work if the plugin is properly installed:
it('should execute JavaScript', async () => {
const result = await browser.tauri.execute(() => {
return window.location.href;
});
expect(result).toBeDefined();
});This means the plugin is not detected. Check:
-
Plugin registered in Rust
.plugin(tauri_plugin_wdio::init())
-
Frontend import present
import '@wdio/tauri-plugin';
-
withGlobalTaurienabled{ "app": { "withGlobalTauri": true } } -
Permissions configured Check that
"wdio:default"is in your capabilities permissions.
The frontend plugin didn't initialize. Make sure:
- The import statement is in your main entry point, before tests run
- You're not running in an iframe or separate context
- The app has time to initialize (add a small delay if needed):
it('should have wdioTauri', async () => {
// Give plugin time to initialize
await browser.pause(500);
const hasPlugin = 'wdioTauri' in window;
expect(hasPlugin).toBe(true);
});Make sure:
- Plugin is installed and available (see verification methods above)
- You're using
browser.tauri.mock(), not trying to mock directly - The mock is set up before calling the command:
// ✅ Correct
const mock = await browser.tauri.mock('my_command');
await mock.mockReturnValue('test');
await browser.tauri.execute(({ core }) => core.invoke('my_command'));
// ❌ Wrong - mock set up after calling command
await browser.tauri.execute(({ core }) => core.invoke('my_command'));
const mock = await browser.tauri.mock('my_command');If you get Rust compilation errors:
-
Ensure Rust toolchain is up to date
rustup update
-
Clear Cargo cache
cd src-tauri cargo clean cargo build --release -
Check Tauri version compatibility
- tauri-plugin-wdio requires Tauri v2.0+
- If you're on Tauri v1, this plugin won't work
-
Check dependency versions All plugins and tauri should be the same version. Edit
Cargo.toml:tauri = { version = "2.9", ... } tauri-plugin-wdio = { version = "2.9", ... }
The plugin consists of two parts:
-
Rust Backend (
packages/tauri-plugin/src/)- Provides Tauri commands:
execute,log_frontend,debug_plugin - Handles script execution via
window.eval() - Manages result serialization
- Provides Tauri commands:
-
Frontend JavaScript (
packages/tauri-plugin/guest-js/)- Auto-initializes on import
- Exposes
window.wdioTauriAPI - Intercepts
window.__TAURI__.core.invokefor mocking - Forwards console logs to Tauri logger
When you import @wdio/tauri-plugin:
- Plugin code loads and detects Tauri APIs
- Sets up console forwarding
- Sets up invoke interception for mocking
- Registers event listeners for backend logs
- Exposes
window.wdioTauriobject - On page unload, cleans up listeners and timers
The wdio:default permission includes:
wdio:allow-execute- Execute JavaScript in frontend contextwdio:allow-log-frontend- Forward frontend logswdio:allow-debug-plugin- Debug plugin statewdio:allow-set-mock- Set mock configurationwdio:allow-get-mock- Get mock configurationwdio:allow-clear-mocks- Clear all mockswdio:allow-reset-mocks- Reset all mockswdio:allow-restore-mocks- Restore all mockswdio:allow-get-active-window-label- Get active window labelwdio:allow-get-window-states- Get window stateswdio:allow-list-windows- List windowswdio:allow-switch-to-main- Switch to main window
For production, you might want to use only specific permissions:
{
"permissions": [
"wdio:allow-execute",
"wdio:allow-set-mock",
"wdio:allow-get-mock"
]
}This would enable the execute and basic mock APIs but disable log forwarding and window management.
No, the plugin is test-only. For production builds:
-
Conditionally register the plugin
fn main() { let mut builder = tauri::Builder::default(); #[cfg(debug_assertions)] { builder = builder.plugin(tauri_plugin_wdio::init()); } builder .run(tauri::generate_context!()) .expect("error while running tauri application"); }
-
Or use feature flags
[features] wdio = ["dep:tauri-plugin-wdio"] [dependencies] tauri-plugin-wdio = { version = "1.0", optional = true }
fn main() { let mut builder = tauri::Builder::default(); #[cfg(feature = "wdio")] { builder = builder.plugin(tauri_plugin_wdio::init()); } builder .run(tauri::generate_context!()) .expect("error while running tauri application"); }
-
Build release without the plugin
cargo build --release
The plugin:
- Only accepts code from WebdriverIO service (not from page scripts)
- Cannot access browser or system permissions without being granted
- Is disabled in production builds
- Requires explicit
wdio:permissions in capabilities
Once the plugin is installed and verified:
- Read Quick Start for minimal test setup
- See API Reference for available functions
- Check Usage Examples for testing patterns
- View Configuration for service options
This plugin embeds a W3C WebDriver HTTP server directly in your Tauri application. Use it when you want to test on macOS without CrabNebula, or when you prefer not to install an external driver.
- Tauri v2.0+
- Embedded provider selected (auto-detected on macOS, or set
driverProvider: 'embedded'/TAURI_WEBDRIVER_PORT)
[target.'cfg(debug_assertions)'.dependencies]
tauri-plugin-wdio-webdriver = "0.1"fn main() {
let builder = tauri::Builder::default();
#[cfg(debug_assertions)]
let builder = builder.plugin(tauri_plugin_wdio_webdriver::init());
builder
.run(tauri::generate_context!())
.expect("error while running tauri application");
}{
"identifier": "default",
"windows": ["main"],
"permissions": [
"core:default",
"core:window:default",
"webdriver:default"
]
}The embedded WebDriver server runs on port 4445 by default. To customize:
// In wdio.conf.ts
services: [['@wdio/tauri-service', {
driverProvider: 'embedded',
embeddedPort: 4445, // Optional, defaults to 4445
}]]Or via environment variable (also triggers auto-detection on Windows/Linux):
TAURI_WEBDRIVER_PORT=4445 npx wdio run wdio.conf.ts- The service spawns your Tauri app with
TAURI_WEBDRIVER_PORTenvironment variable - The app starts an HTTP WebDriver server on that port
- WebdriverIO connects directly to that port (no tauri-driver intermediary)
- On test completion, the service terminates the app
| Aspect | External Driver (official/crabnebula) | Embedded Server |
|---|---|---|
| Architecture | Separate driver process | HTTP server in-app |
| macOS support | Requires CrabNebula | Native |
| Setup complexity | Higher (driver installation) | Lower (no external deps) |
| Port management | Service manages | Service + app coordinate |