Skip to content

Latest commit

 

History

History
513 lines (386 loc) · 13 KB

File metadata and controls

513 lines (386 loc) · 13 KB

Tauri Plugin Setup

Overview

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

Two WDIO Plugins

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

When You Need Each

  • 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
  • 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_PORT on Windows/Linux

Why Is It Required?

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.

What Works Without the Plugin

Only basic WebDriver operations work without the plugin:

  • Element interactions (click, type, etc.)
  • Navigation and page access
  • Basic WebDriver commands

What Requires the Plugin

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

Installation Steps

Step 1: Add Cargo Dependency

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"

Step 2: Register Plugin in Rust

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.

Step 3: Add Tauri Permissions

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"
  ]
}

Step 4: Enable Global Tauri API

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"]
    }
  }
}

Step 5: Import Frontend Plugin

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.wdioTauri API
  • Console forwarding
  • Invoke interception for mocking
  • Event listeners for backend logs

Step 6: Build and Verify

Build your Tauri app:

cd src-tauri
cargo build --release

The build should complete successfully without errors about the plugin.

Verifying the Plugin Installation

Method 1: Check Browser Console

After starting your test session, the plugin will log initialization messages:

[WDIO Plugin] Initializing...
[WDIO Plugin] Successfully initialized

Method 2: Test Plugin Availability

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);
});

Method 3: Try Execute API

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();
});

Troubleshooting Plugin Issues

"Tauri plugin not available" Error

This means the plugin is not detected. Check:

  1. Plugin registered in Rust

    .plugin(tauri_plugin_wdio::init())
  2. Frontend import present

    import '@wdio/tauri-plugin';
  3. withGlobalTauri enabled

    {
      "app": {
        "withGlobalTauri": true
      }
    }
  4. Permissions configured Check that "wdio:default" is in your capabilities permissions.

"window.wdioTauri is undefined" Error

The frontend plugin didn't initialize. Make sure:

  1. The import statement is in your main entry point, before tests run
  2. You're not running in an iframe or separate context
  3. 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);
});

Mocking Doesn't Work

Make sure:

  1. Plugin is installed and available (see verification methods above)
  2. You're using browser.tauri.mock(), not trying to mock directly
  3. 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');

Plugin Compilation Errors

If you get Rust compilation errors:

  1. Ensure Rust toolchain is up to date

    rustup update
  2. Clear Cargo cache

    cd src-tauri
    cargo clean
    cargo build --release
  3. Check Tauri version compatibility

    • tauri-plugin-wdio requires Tauri v2.0+
    • If you're on Tauri v1, this plugin won't work
  4. 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", ... }

Plugin Architecture

How It Works

The plugin consists of two parts:

  1. Rust Backend (packages/tauri-plugin/src/)

    • Provides Tauri commands: execute, log_frontend, debug_plugin
    • Handles script execution via window.eval()
    • Manages result serialization
  2. Frontend JavaScript (packages/tauri-plugin/guest-js/)

    • Auto-initializes on import
    • Exposes window.wdioTauri API
    • Intercepts window.__TAURI__.core.invoke for mocking
    • Forwards console logs to Tauri logger

Plugin Lifecycle

When you import @wdio/tauri-plugin:

  1. Plugin code loads and detects Tauri APIs
  2. Sets up console forwarding
  3. Sets up invoke interception for mocking
  4. Registers event listeners for backend logs
  5. Exposes window.wdioTauri object
  6. On page unload, cleans up listeners and timers

Permissions Detail

The wdio:default permission includes:

  • wdio:allow-execute - Execute JavaScript in frontend context
  • wdio:allow-log-frontend - Forward frontend logs
  • wdio:allow-debug-plugin - Debug plugin state
  • wdio:allow-set-mock - Set mock configuration
  • wdio:allow-get-mock - Get mock configuration
  • wdio:allow-clear-mocks - Clear all mocks
  • wdio:allow-reset-mocks - Reset all mocks
  • wdio:allow-restore-mocks - Restore all mocks
  • wdio:allow-get-active-window-label - Get active window label
  • wdio:allow-get-window-states - Get window states
  • wdio:allow-list-windows - List windows
  • wdio: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.

Production Considerations

Should I Include the Plugin in Production?

No, the plugin is test-only. For production builds:

  1. 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");
    }
  2. 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");
    }
  3. Build release without the plugin

    cargo build --release

Plugin Security

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

Next Steps

Once the plugin is installed and verified:

  1. Read Quick Start for minimal test setup
  2. See API Reference for available functions
  3. Check Usage Examples for testing patterns
  4. View Configuration for service options

Plugin 2: tauri-plugin-wdio-webdriver (Optional - Embedded WebDriver)

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.

Requirements

  • Tauri v2.0+
  • Embedded provider selected (auto-detected on macOS, or set driverProvider: 'embedded' / TAURI_WEBDRIVER_PORT)

Installation

1. Add Cargo Dependency

[target.'cfg(debug_assertions)'.dependencies]
tauri-plugin-wdio-webdriver = "0.1"

2. Register Plugin in Rust

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");
}

3. Add Permissions

{
  "identifier": "default",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "core:window:default",
    "webdriver:default"
  ]
}

Configuration

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

How It Works

  1. The service spawns your Tauri app with TAURI_WEBDRIVER_PORT environment variable
  2. The app starts an HTTP WebDriver server on that port
  3. WebdriverIO connects directly to that port (no tauri-driver intermediary)
  4. On test completion, the service terminates the app

Differences from External Driver

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

See Also