npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

appium-desktop-driver

v2.1.0

Published

Appium driver for Windows

Readme

Appium Desktop Driver

Appium Desktop Driver is a Windows UI automation driver for Appium 3. It automates UWP, WinForms, WPF, Win32, Java Swing, and Internet Explorer applications.

Key advantages over WinAppDriver:

  • Faster XPath evaluation against the live UIA tree
  • RawView element support (elements hidden from ContentView/ControlView)
  • Reliable text input independent of the active keyboard layout
  • Java Swing / AWT automation via injected JVM agent (no JAB required)
  • WebView2, Chrome, and Edge embedded content via CDP
  • Internet Explorer 11 automation via built-in IE DOM Bridge (no IEDriverServer required)
  • Built-in screen recording, clipboard API, and vision-based finding
  • MCP server for direct use with AI coding agents

Installation

appium driver install --source=npm appium-desktop-driver

Requires Appium 3 and Windows 10 or later.

Capabilities

All capabilities use the appium: prefix in W3C format (except platformName).

| Capability | Type | Description | | --- | --- | --- | | platformName | string | Required. Must be Windows | | appium:automationName | string | Required. Must be DesktopDriver | | appium:app | string | Exe path, UWP AUMID, or Root for the desktop | | appium:appTopLevelWindow | string | Hex/decimal handle to attach to existing window | | appium:appArguments | string | CLI arguments for the launched process | | appium:appWorkingDir | string | Working directory for the process | | appium:appEnvironment | object | Env vars injected for the session lifetime | | appium:ms:waitForAppLaunch | number | Seconds (<=120) or ms (>120) to wait for window | | appium:shouldCloseApp | boolean | Close the window at session end. Default: true | | appium:prerun | object | { script } or { command } — PowerShell before start | | appium:postrun | object | { script } or { command } — PowerShell after end | | appium:isolatedScriptExecution | boolean | Isolated PS for pre/postrun. Default: false | | appium:smoothPointerMove | string | CSS easing for mouse (e.g. ease-in) | | appium:delayBeforeClick | number | Milliseconds before each click | | appium:delayAfterClick | number | Milliseconds after each click | | appium:javaSwing | boolean | Enable JVM agent for Java Swing/AWT apps | | appium:jdkPath | string | Path to JDK root (e.g. C:\Program Files\Java\jdk1.8.0_xxx). Overrides JAVA_HOME for agent injection. Required only for Path B/C. | | appium:webviewEnabled | boolean | Enable WebView2 / Chrome / Edge CDP | | appium:webviewDevtoolsPort | number | CDP port (auto-selected when omitted) | | appium:chromedriverExecutablePath | string | Local Chromedriver binary | | appium:edgedriverExecutablePath | string | Local msedgedriver binary | | appium:chromedriverCdnUrl | string | Custom CDN for Chromedriver downloads | | appium:edgedriverCdnUrl | string | Custom CDN for EdgeDriver downloads | | appium:ffmpegExecutablePath | string | Local ffmpeg binary for screen recording | | appium:ms:forcequit | boolean | Force-kill process on session close | | appium:ms:experimental-webdriver | boolean | Experimental WebDriver features | | appium:logFile | string | Path to write session logs | | appium:ieDriverServerPath | string | Deprecated. No longer used; IE is automated via the built-in DOM Bridge. |

Examples

Java Swing via JVM agent

The driver automates Java Swing/AWT applications by injecting a lightweight JVM agent — no jabswitch, no JAB DLL required.

Three injection paths are available:

  • Path A — driver launches the JVM (appium:app + appium:javaSwing: true). No JAVA_HOME needed.
  • Path B — attach to an already-running JVM at session time (appium:appTopLevelWindow + appium:javaSwing: true). Requires JAVA_HOME or appium:jdkPath pointing to a JDK.
  • Path C — inject agent mid-session via windows: attachJavaSwing. Start any session, switch to the Java window, then call the command. Requires JAVA_HOME or appium:jdkPath (or pass jdkPath as a script argument).

See API.md — Java Swing Automation for full examples, JAVA_HOME setup, and supported XPath attributes.

Desktop root: click an icon and switch windows

import { remote } from 'webdriverio';

const driver = await remote({
  hostname: '127.0.0.1',
  port: 4723,
  path: '/',
  capabilities: {
    platformName: 'Windows',
    'appium:automationName': 'DesktopDriver',
    'appium:app': 'Root', // attach to the Windows desktop shell
  },
});

// Double-click a desktop shortcut by its accessible name
const icon = await driver.$('~Notepad');
await driver.executeScript('windows: click', [{
  elementId: icon.elementId,
  times: 2,
  interClickDelayMs: 100,
}]);

// Give the app time to open, then switch to the new window by handle...
await driver.pause(2000);
const allHandles = await driver.getWindowHandles();
await driver.switchToWindow(allHandles[allHandles.length - 1]);

// ...or switch by title (partial, case-insensitive)
await driver.executeScript('windows: switchToWindowByTitle', [{ title: 'Notepad' }]);

// To include untitled windows (getWindowHandles omits them), use getWindows instead.
// It returns { handle, title, className } for every visible top-level window.
const windows = await driver.executeScript('windows: getWindows', []);
const popup = windows.find(w => w.className === 'SunAwtDialog' && w.title === '');
if (popup) await driver.switchToWindow(popup.handle);

const titleBar = await driver.$('//TitleBar');
console.log(await titleBar.getText());

// Switch back to the desktop root at any time
await driver.switchToWindow('root');

await driver.deleteSession();

Windows Calculator: invoke pattern

windows: invoke fires the UIA InvokePattern. Prefer it over .click() for controls that do not respond reliably to pointer events.

import { remote } from 'webdriverio';

const driver = await remote({
  hostname: '127.0.0.1',
  port: 4723,
  path: '/',
  capabilities: {
    platformName: 'Windows',
    'appium:automationName': 'DesktopDriver',
    'appium:app': 'Microsoft.WindowsCalculator_8wekyb3d8bbwe!App',
  },
});

async function invoke(el) {
  await driver.executeScript('windows: invoke', [el]);
}

// 5 + 3 =
await invoke(await driver.$('~num5Button'));
await invoke(await driver.$('~plusButton'));
await invoke(await driver.$('~num3Button'));
await invoke(await driver.$('~equalButton'));

const display = await driver.$('~CalculatorResults');
console.log(await display.getText()); // "Display is 8"

await driver.deleteSession();

Ctrl+drag via W3C Actions

Actions are processed tick by tick. All actions at the same tick index across input sources run simultaneously. Ticks advance sequentially. Use pause entries to keep sources aligned when mixing keyboard and pointer inputs.

import { remote } from 'webdriverio';

const driver = await remote({
  hostname: '127.0.0.1',
  port: 4723,
  path: '/',
  capabilities: {
    platformName: 'Windows',
    'appium:automationName': 'DesktopDriver',
    'appium:app': 'C:\\MyApp\\app.exe',
  },
});

const source = await driver.$('~dragSource');
const target = await driver.$('~dropTarget');

await driver.performActions([
  {
    type: 'key',
    id: 'keyboard',
    actions: [
      { type: 'pause' },                    // tick 0 — wait for mouse move
      { type: 'keyDown', value: '\uE009' }, // tick 1 — Ctrl key (W3C key value)
      { type: 'pause' },                    // tick 2 — hold
      { type: 'pause' },                    // tick 3 — hold during drag
      { type: 'keyUp', value: '\uE009' },   // tick 4 — Ctrl up
    ],
  },
  {
    type: 'pointer',
    id: 'mouse',
    parameters: { pointerType: 'mouse' },
    actions: [
      { type: 'pointerMove', duration: 0, origin: source },   // tick 0
      { type: 'pointerDown', button: 0 },                      // tick 1
      { type: 'pause', duration: 300 },                        // tick 2
      { type: 'pointerMove', duration: 600, origin: target },  // tick 3
      { type: 'pointerUp', button: 0 },                        // tick 4
    ],
  },
]);

await driver.releaseActions();
await driver.deleteSession();

Internet Explorer (IE DOM Bridge)

The driver includes a built-in IE DOM Bridge — a 32-bit C# process (IEBridge.exe) that attaches to a running IE 11 window via the WM_HTML_GETOBJECT message, retrieves IHTMLDocument2 through COM, and routes all element commands over stdio JSON. No IEDriverServer and no WebDriver protocol proxy are involved.

IE mode activates automatically whenever a session window has the IEFrame window class. This happens when:

  • appium:app is set to the IE executable and the driver launches it, or
  • appium:appTopLevelWindow points to an existing IE window handle, or
  • windows: switchToWindow / windows: switchToWindowByTitle targets an IE window.

Prerequisites:

  • Internet Explorer 11 at C:\Program Files\Internet Explorer\iexplore.exe
  • Internet Options → Security: Protected Mode disabled on all four zones
  • Internet Options → Advanced: Enhanced Protected Mode disabled
  • View → Zoom: set to exactly 100%

Supported locator strategies: css selector, id, xpath

Supported XPath patterns: //tag, //tag[@attr="val"], //tag[contains(text(),"...")], //tag[contains(@attr,"...")], //tag[@attr]

Command support:

| Command | Status | Notes | |---|---|---| | findElement / findElements | ✅ | css selector, id, xpath | | getTitle | ✅ | | | getUrl | ✅ | | | getPageSource | ✅ | | | url() | ✅ | navigate to URL | | getWindowHandle | ✅ | returns HWND hex string | | getText(el) | ✅ | | | getAttribute(el, name) | ✅ | | | clear(el) | ✅ | input / textarea only | | setValue(el, value) | ✅ | input / textarea only | | click(el) | ✅ / ⚠️ | works for buttons, links; broken for checkboxes and radios | | isDisplayed(el) | ✅ | | | isEnabled(el) | ⚠️ | unreliable for elements without an id attribute | | isSelected(el) | ❌ | always returns false | | executeScript(script, args) | ✅ | runs JS in the IE tab | | switchToFrame(id) | ✅ | by index, element, or null for default content | | switchToParentFrame() | ✅ | returns to default content (single-level only) | | getElementRect(el) | ❌ | not implemented | | getElementScreenshot(el) | ❌ | not implemented | | windows: extension commands | ❌ | target the UIA tree, not the HTML DOM |

Launch IE and automate it

import { remote } from 'webdriverio';

const driver = await remote({
  hostname: '127.0.0.1',
  port: 4723,
  path: '/',
  capabilities: {
    platformName: 'Windows',
    'appium:automationName': 'DesktopDriver',
    'appium:app': 'C:\\Program Files\\Internet Explorer\\iexplore.exe',
    'appium:appArguments': 'https://example.com',
    'appium:shouldCloseApp': true,
  },
});

await driver.pause(3000); // wait for IE to load the page
await driver.setTimeout({ implicit: 5000 });

const title = await driver.getTitle();
console.log(title); // "Example Domain"

const h1 = await driver.$('h1');
console.log(await h1.getText());

await driver.deleteSession();

Attach to an already-open IE window

import { remote } from 'webdriverio';

// hwnd is the decimal or hex HWND string of the IEFrame window
const driver = await remote({
  hostname: '127.0.0.1',
  port: 4723,
  path: '/',
  capabilities: {
    platformName: 'Windows',
    'appium:automationName': 'DesktopDriver',
    'appium:appTopLevelWindow': '0x001A0B2C',
    'appium:shouldCloseApp': false,
  },
});

const url = await driver.getUrl();
console.log(url);

await driver.deleteSession();

Switch into an iframe

// By 0-based index
await driver.switchToFrame(0);

// By element
const frameEl = await driver.$('iframe#content');
await driver.switchToFrame(frameEl);

// Element finds are now scoped to the frame's document
const h1 = await driver.$('h1');
console.log(await h1.getText());

// Back to the top-level document
await driver.switchToFrame(null);

Only one level of nesting is tracked — switching into a frame while already inside one replaces the current frame context rather than stacking. switchToParentFrame() always returns to default content.

Switch between IE and UIA within the same session

Start a Root session and navigate between windows freely. The driver switches between IE Bridge mode and UIA mode automatically based on whether the target window is an IEFrame.

import { remote } from 'webdriverio';

const driver = await remote({
  hostname: '127.0.0.1',
  port: 4723,
  path: '/',
  capabilities: {
    platformName: 'Windows',
    'appium:automationName': 'DesktopDriver',
    'appium:app': 'Root',
  },
});

// Focus the IE window — IE Bridge activates automatically
await driver.executeScript('windows: switchToWindowByTitle', [{ title: 'Internet Explorer' }]);

const h1 = await driver.$('h1');
console.log(await h1.getText());

// Switch to a non-IE window — UIA resumes automatically
await driver.executeScript('windows: switchToWindowByTitle', [{ title: 'Notepad' }]);

await driver.deleteSession();

API Reference

Full reference for all locator strategies, windows: extension commands, WebView/CDP automation, screen recording, and vision-based finding: API.md.

Development

npm install
npm run lint
npm run build
npm run test

Uses Comment Tagged Templates for PowerShell/C syntax highlighting in VS Code.