npm package: @interopio/wdio-iocd-service
Description: A [WebdriverIO](https://webdriver.io/) service for running E2E tests against **[io.Connect Desktop](https://interop.io/products/io-connect/)**.   This service launches your packaged `.exe` via ChromeDriver, so you can write standard WDIO tests for your d
Author: interop.io
Latest version: 0.1.1

# <img src="https://webdriver.io/img/webdriverio.png" alt="WebdriverIO" width="20"/> @interopio/wdio-iocd-service

A [WebdriverIO](https://webdriver.io/) service for running E2E tests against **[io.Connect Desktop](https://interop.io/products/io-connect/)**.  
This service launches your packaged `.exe` via ChromeDriver, so you can write standard WDIO tests for your desktop app.

---

## Installation

Install the service:

```bash
npm install --save-dev @interopio/wdio-iocd-service
```

Install WebdriverIO (v8) with basic test deps:

```bash
npm install --save-dev webdriverio@^8 @wdio/cli@^8 @wdio/local-runner@^8 @wdio/mocha-framework@^8 @wdio/spec-reporter@^8
```

For TypeScript configs/specs:

```bash
npm install --save-dev ts-node typescript @types/node
```

---

## Configuration

`Note:` [Webdriver Capabilities](https://webdriver.io/docs/options/#capabilities) – learn how capabilities define the environment and settings for your test sessions

Create a `wdio.config.ts` file in your project root:

```ts
import { IOCDBaseConfiguration, IOCDService } from '@interopio/wdio-iocd-service';

export const config: WebdriverIO.Config = {
    ...IOCDBaseConfiguration,

    // Services
    services: IOCDService({
        // Optional video recording for failing tests, in the output dir ONLY recordings of failed tests will appear
        videoRecording: {
            enabled: true,
            fps: 20,
            outputDir: './artifacts/videos',
        },
    }),

    // Capabilities
    capabilities: [
        {
            browserName: 'iocd',
            'wdio:specs': ['./tests/**/*.spec.ts'],
            'iocd:options': {
                binary:
                    '%LOCALAPPDATA%/interop.io/io.Connect Desktop/Desktop/io-connect-desktop.exe',
                // Optional extend of the default configuration
                // systemConfig: '%LOCALAPPDATA%/interop.io/io.Connect Desktop/Desktop/system.json',
                // configOverrides: ['./overrides/override1.json', './overrides/override2.json'],

                // inspect: 9229, // Optional inspect specs port
            },
        },
    ],

    // WDIO Options
    // Our integration is framework- and reporter-agnostic — you can use Mocha, Jasmine, Cucumber,
    // or any other supported WDIO framework and reporter combination.
    logLevel: 'warn',
    framework: 'mocha',
    reporters: ['spec'],

    // TypeScript Autocompile
    autoCompileOpts: {
        autoCompile: true,
        tsNodeOpts: {
            transpileOnly: true,
            project: './tsconfig.json',
        },
    },
};
```

---

## tsconfig.json

Create a minimal `tsconfig.json` in your project root:

```json
{
    "compilerOptions": {
        "target": "ES2020",
        "module": "CommonJS",
        "moduleResolution": "Node",
        "outDir": "dist",
        "esModuleInterop": true,
        "types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"]
    },
    "include": ["wdio.config.ts", "tests/**/*.ts"]
}
```

`Note:`
This package is in an early version and currently supports WebdriverIO v8, which relies on CommonJS modules.
The configuration above uses "module": "CommonJS" to ensure compatibility with WDIO’s v8 runtime.
Once the package migrates to WebdriverIO v9, this will be updated to use ES Modules ("module": "NodeNext") for native import/export syntax and improved type support.

---

## Example Test

`tests/sample.spec.ts`

```ts
describe('Example usage of @interopio/wdio-iocd-service', () => {
    it('should have access to browser.io()', async () => {
        // A fully working io.Connect Desktop client
        const io = await browser.io();

        expect(io).toHaveAttribute('appManager');
        expect(io).toHaveAttribute('contexts');
        expect(io).toHaveAttribute('channels');
    });

    // The code below illustrates how you can combine APIs from both
    // WebdriverIO and the @interopio/wdio-iocd-service package.
    // ⚠️ Note: This example will fail unless you include and import an actual "InMemoryApp".
    it.skip('should present a simple example of how you can write tests with wdio and wdio-iocd-service', async () => {
        const io = await browser.io();
        // Provided by the IOCD service:
        // Gives access to the running io.Connect Desktop platform instance.
        const instance = await io.appManager.application('InMemoryApp').start();
        const window = await instance.getWindow();

        // Provided by the IOCD service:
        // Switches WDIO context to the given IOCD window.
        await browser.switchById(window.id);

        // Learn more about how WDIO manages multiple windows (handles):
        // https://webdriver.io/docs/api/webdriver/#getwindowhandle
        // https://w3c.github.io/webdriver/#dfn-get-window-handle
        // In IOCD tests, each app window maps to a unique handle ID returned by
        // `instance.getWindow()`, which we pass to `browser.switchById(window.id)`
        // to focus WebdriverIO commands on that specific window context.

        // Provided by WebdriverIO:
        // Mock a network request and provide a fake response.
        const mockUser = await browser.mock('**/api/user', { method: 'GET' });
        mockUser.respond({ name: 'John Doe', id: 1 });

        // Provided by WebdriverIO:
        // Select elements and perform DOM interactions inside the active context.
        const input = await $("input[name='username']");
        await input.setValue('John Doe');

        const submit = await $("button[type='submit']");
        await submit.click();

        const message = await $('.welcome');
        await message.waitForDisplayed({ timeout: 5000 });

        // Provided by WebdriverIO:
        // Assertions and element property checks.
        expect(await message.getText()).toContain('Welcome, John Doe');
    });
});
```

---

## Custom Commands

This service extends WebdriverIO’s `browser` and `element` objects with IO.Connect Desktop helpers.

### Browser commands

| Method                                 | Description                                                                                     | Example                                       |
| -------------------------------------- | ----------------------------------------------------------------------------------------------- | --------------------------------------------- |
| `browser.io()`                         | Returns a connected **IO.Connect API instance** with `appManager`, `contexts`, `channels`, etc. | `const io = await browser.io();`              |
| `browser.createIOConnectInstance(cfg)` | Creates a new **isolated IO.Connect instance** for a given app.                                 | `await browser.createIOConnectInstance(cfg);` |
| `browser.switchById(windowId)`         | Switches to a **Application frame** by its `windowId`.                                          | `await browser.switchById("my-window-id");`   |
| `browser.switchToLaunchpad()`          | Switches to the **Launchpad** window.                                                           | `await browser.switchToLaunchpad();`          |
| `browser.switchToNotificationPanel()`  | Switches to the **Notifications Panel** window.                                                 | `await browser.switchToNotificationPanel();`  |
| `browser.travelTime(ms)`               | Advances **browser time** by the given offset in milliseconds.                                  | `await browser.travelTime(5000);`             |
| `browser.restoreTime()`                | Restores the **original browser time** after using `travelTime`.                                | `await browser.restoreTime();`                |

### Element commands

| Method                                                        | Description                                                                       | Example                                            |
| ------------------------------------------------------------- | --------------------------------------------------------------------------------- | -------------------------------------------------- |
| `element.waitAndClick({ timeout? })`                          | Waits until the element is clickable, then performs a click.                      | `await $("#btn").waitAndClick({ timeout: 2000 });` |
| `element.waitForChildrenCount(selector, count, { timeout? })` | Waits until the element has exactly `count` children matching the given selector. | `await $("#list").waitForChildrenCount("li", 5);`  |

➡️ We are actively extending these commands further to improve the testing experience.

---

## Example Project Setup

**package.json**

```json
{
    "scripts": {
        "test": "npx wdio run ./wdio.config.ts"
    }
}
```

Run tests:

```bash
npm test
```

---

## Notes

-   `browserName: "iocd"` is required for this service to activate.
-   `iocd:options.binary` must point to your installed io.Connect Desktop `.exe`.
-   `iocd:options.systemConfig` _(optional)_ can be used to provide a path to a `system.json` file needed for your runs.
-   `iocd:options.configOverrides` _(optional)_ allows you to specify one or more overrides for `system.json` (useful for different capabilities or environments).
-   `iocd:options.inspect` _(optional)_ You can specify a node inspect port for debugging your specs
-   You can define `wdio:specs` per capability, giving you fine-grained control over which specs run against which configuration.
-   You may also define **appStores** and **settings** in your `system.json` or overrides, to configure your testing environment more precisely.

### Debugging with the `iocd:options.inspect` Port

When you specify an inspect port (e.g. `inspect:9229`), you can attach a debugger to your running WDIO test process and step through your specs in real time.

#### **VS Code**

1. Open the **Run and Debug** panel.
2. Click **“Add Configuration…”** → choose **“Node.js: Attach”**.
3. Set `"port": 9229` (or your configured inspect port).
4. Press **F5** to start debugging.

Official guide: [Debugging Node.js Apps in Visual Studio Code](https://code.visualstudio.com/docs/nodejs/nodejs-debugging)

#### **Chrome DevTools**

1. Open [`chrome://inspect`](chrome://inspect/#devices) in Chrome.
2. Click **“Configure…”** → ensure `localhost:9229` is listed.
3. Under **Remote Target**, click **“Inspect”** next to your running process.

Official guide: [Node.js Debugging with Chrome DevTools](https://nodejs.org/en/learn/getting-started/debugging)

#### **Other IDEs**

Most modern IDEs (e.g. **JetBrains WebStorm**, **IntelliJ IDEA**, **Rider**, etc.) support Node.js remote debugging.  
Simply configure an **“Attach to Node.js/Chrome”** run configuration pointing to the same host and port.

See: [WebStorm – Debug Node.js Applications](https://www.jetbrains.com/help/webstorm/running-and-debugging-node-js.html)

#### **Inline Debugging with WebdriverIO**

You can also use [`browser.debug()`](https://webdriver.io/docs/api/browser/debug/) directly inside your specs to pause execution at any point.  
This opens an interactive REPL in your terminal, allowing you to inspect the current browser state, execute commands, and resume when ready.  
It’s especially useful for quick troubleshooting without attaching an external debugger.

---

## Requirements

-   Node.js >= 20
-   npm >= 9
-   io.Connect Desktop installed (or system.json available)

---

With this service, you can run **real WDIO tests** against **Interop.io’s io.Connect Desktop** just like you would for a web app — but fully automated on the desktop.

## Learn More

To dive deeper into writing and structuring tests with WebdriverIO, check out the official documentation and learning resources:

-   [WebdriverIO Docs](https://webdriver.io/docs/gettingstarted/) – official guides, API reference, and configuration examples
-   [WebdriverIO Guide: Testing Basics](https://webdriver.io/docs/api) – learn about commands, selectors, assertions, and best practices

These resources will help you get familiar with the testing concepts used alongside `@interopio/wdio-iocd-service`.

Keep in mind that testing **Electron-based desktop applications** may not directly correlate with testing traditional web apps — some WebdriverIO APIs can behave differently or may not apply at all.
This is an **early version** of our integration service, and certain features will continue to evolve as we refine the bridge between **io.Connect Desktop** and **WebdriverIO.**