How OpenHuman's WDIO E2E Testing Framework Adapts to Linux vs. macOS

OpenHuman uses WebdriverIO (WDIO) with platform-specific automation drivers—tauri-driver on Linux and Appium Mac2 on macOS—to test its Tauri-based desktop application, requiring different interaction models for WebView DOM access versus native UI automation.

The OpenHuman E2E testing framework provides comprehensive coverage for a Tauri-powered desktop application using a single WDIO configuration that dynamically selects automation backends. According to the tinyhumansai/openhuman source code, the framework resides in app/test/ and transparently handles the architectural differences between Linux's direct WebView access and macOS's native UI layer bridging.

Platform-Specific Automation Architecture

The framework detects the host operating system at runtime and initializes the appropriate WebDriver implementation. This dual-backend approach accommodates fundamental differences in how each platform exposes the application's React-based WebView for automation.

Linux: Tauri-Driver and Direct WebView Access

On Linux, the framework leverages tauri-driver, a W3C WebDriver server included with the Tauri toolchain. In app/test/wdio.conf.ts, the configuration returns capabilities that point directly to the compiled binary:

if (process.platform === 'linux') {
  return [{ 'tauri:options': { application: getAppPath() } }];
}

The driver launches the executable located at src-tauri/target/debug/OpenHuman and exposes the WebView's DOM directly to WDIO commands. This enables standard Selenium-style interactions—click(), sendKeys(), and executeScript()—to function exactly as they would in a browser automation context.

macOS: Appium Mac2 and Native UI Automation

On macOS, the framework switches to Appium Mac2, which utilizes Apple's XCUITest backend to drive the native macOS UI. The wdio.conf.ts constructs Appium-specific capabilities for the .app bundle:

return [
  {
    platformName: 'mac',
    'appium:automationName': 'Mac2',
    'appium:app': getAppPath(),
    'appium:showServerLogs': true,
  },
];

This backend represents UI elements as XCUIElementType* nodes rather than DOM nodes. Because XCUITest does not forward native click events into the embedded WebView, the framework requires W3C pointer actions for interaction, and executeScript calls are unsupported.

Configuration and Capability Detection

The getPlatformCapabilities() function in app/test/wdio.conf.ts serves as the central decision point for driver selection. It inspects process.platform and returns the appropriate capability set, while also computing platform-specific port assignments:

const isLinuxDriver = process.platform === 'linux';
const driverPort = isLinuxDriver
  ? parseInt(process.env.TAURI_DRIVER_PORT || '4444', 10)
  : parseInt(process.env.APPIUM_PORT || '4723', 10);

Port 4444 serves the tauri-driver on Linux, while port 4723 connects to the Appium server on macOS.

Runtime Platform Abstraction

To prevent test code from scattering platform-specific conditionals, the framework provides runtime helpers in app/test/e2e/helpers/platform.ts. The isTauriDriver() function determines the active backend by inspecting browser capabilities:

export function isTauriDriver(): boolean {
  if (typeof browser === 'undefined') return process.platform === 'linux';
  const caps = browser.capabilities as Record<string, unknown>;
  const automation = String(
    caps['appium:automationName'] ?? caps['automationName'] ?? ''
  ).toLowerCase();
  if (automation === 'mac2' || automation.includes('mac2')) return false;
  const platform = String(caps.platformName ?? caps['appium:platformName'] ?? '').toLowerCase();
  if (platform === 'mac') return false;
  return true;
}

Additional helpers like isMac2() and supportsExecuteScript() allow test specifications to branch cleanly when encountering platform limitations.

Handling UI Interaction Differences

The app/test/e2e/helpers/element-helpers.ts file encapsulates the interaction model divergence. On Linux, element.click() propagates directly to the DOM. On macOS, the framework must translate clicks into W3C pointer actions to ensure the native UI forwards the event to the WebView:

export async function clickElement(selector: string) {
  const el = await $(selector);
  if (isTauriDriver()) {
    await el.click();               // DOM click works on Linux
  } else {
    // Appium Mac2 requires pointer actions
    await browser.performActions([
      {
        type: 'pointer',
        id: 'mouse',
        parameters: { pointerType: 'mouse' },
        actions: [
          { type: 'pointerMove', origin: el, x: 0, y: 0 }, 
          { type: 'pointerDown', button: 0 }, 
          { type: 'pointerUp', button: 0 }
        ],
      },
    ]);
  }
}

CI Execution and Stability Considerations

The WDIO configuration adjusts connection timeouts based on platform behavior. Because tauri-driver can exhibit slower startup times on CI runners, app/test/wdio.conf.ts specifies elevated retry thresholds for Linux:

  • Linux: connectionRetryTimeout: 240000 (240 seconds) with connectionRetryCount: 5
  • macOS: Standard Appium defaults for faster session establishment

This ensures reliable session creation across different CI environments without modifying test logic.

Debugging Cross-Platform Failures

When assertions fail, the afterTest hook invokes captureFailureArtifacts() from app/test/e2e/helpers/artifacts.ts to record screenshots and page source. This artifact collection is crucial for debugging divergent UI states between Linux's direct WebView rendering and macOS's native UI layer composition.

Execution remains consistent across platforms via the same command interface:


# Build and run on either Linux or macOS

pnpm test:e2e:build
bash app/scripts/e2e-run-spec.sh app/test/e2e/specs/smoke.spec.ts smoke

Summary

  • OpenHuman uses tauri-driver on Linux for direct WebView DOM access and Appium Mac2 on macOS for native UI automation.
  • The getPlatformCapabilities() function in app/test/wdio.conf.ts selects the driver based on process.platform.
  • Platform helpers in app/test/e2e/helpers/platform.ts abstract driver detection via isTauriDriver() and supportsExecuteScript().
  • Element interactions require W3C pointer actions on macOS (performActions) versus standard clicks on Linux.
  • Linux CI runs use extended timeouts (240s) and increased retry counts (5) to accommodate tauri-driver startup latency.
  • Screenshots and page sources are automatically captured via captureFailureArtifacts() to diagnose platform-specific failures.

Frequently Asked Questions

Why does OpenHuman use different automation drivers for Linux and macOS?

Tauri officially supports tauri-driver as a W3C WebDriver implementation for Linux, which provides direct access to the WebView's DOM. On macOS, Apple's security model and the structure of Tauri applications require native UI automation via XCUITest through Appium Mac2, as the WebView is not directly exposed to WebDriver commands in the same manner.

Can I use the same test specification files on both Linux and macOS?

Yes. The framework maintains a single test suite in app/test/e2e/specs/ that runs unchanged on both platforms. The platform.ts helpers automatically detect the active driver and execute the appropriate interaction method, allowing tests to remain agnostic of the underlying automation technology.

Why does browser.execute fail on macOS but work on Linux?

The tauri-driver on Linux implements the W3C WebDriver specification fully, including script execution within the WebView context. Appium Mac2 operates at the native macOS UI layer using XCUITest, which cannot inject JavaScript into the embedded WebView. Tests should check supportsExecuteScript() before attempting DOM-based script execution.

How do I configure custom ports for parallel test execution?

Set environment variables before launching WDIO. For Linux, export TAURI_DRIVER_PORT (defaults to 4444). For macOS, export APPIUM_PORT (defaults to 4723). The wdio.conf.ts reads these variables via process.env when calculating the driverPort value.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →