@interopio/iocd-cli 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/changelog.md CHANGED
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.0.2](https://github.com/InteropIO/iocd-cli/compare/iocd-cli-v1.0.1...iocd-cli-v1.0.2) (2026-08-18)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **templates:** pin wdio service 1.0.0-next.3 and document sso startup login in the skill ([fa49dc1](https://github.com/InteropIO/iocd-cli/commit/fa49dc15f541cfa79437f78c1fd5647d6dfd4aea))
14
+
8
15
  ## [1.0.1](https://github.com/InteropIO/iocd-cli/compare/iocd-cli-v1.0.0...iocd-cli-v1.0.1) (2026-08-16)
9
16
 
10
17
 
@@ -57,6 +57,17 @@ cd tests && npm run typecheck # type-check without launching the
57
57
  window and switch by `workspaceWindow.getGdWindow().id` for its DOM. Refresh the Workspace
58
58
  reference after structural changes before reading its updated tree.
59
59
  - **Keep the seed's config**: the `platform-wdio` runner and `binary: process.env.BINARY_PATH`.
60
+ - **Keep the test config layer minimal.** `config/system.json` is merged over the config the seed
61
+ already ships. Use it for test plumbing (fixture app store, `folders.userData`, log locations),
62
+ not to change platform behavior — that belongs in the seed's `modifications/`.
63
+ - **A test that needs different platform settings needs its own launch.** Platform-wide settings
64
+ (Channel mode, group type, dialogs) can't change mid-process: add a delta
65
+ `config/system.<variant>.json` and a second capability in `wdio.config.ts` with its own
66
+ `configOverrides` + `wdio:specs`, and `wdio:exclude` those specs from the default capability so
67
+ they don't also run under it. See [configuration](./references/configuration.md).
68
+ - **A seed with an SSO login page needs the service's `login` option** — the platform blocks startup
69
+ on the login window, so without it `npm test` hangs until the startup timeout. Credentials come
70
+ from env vars, never from specs or config. See [configuration](./references/configuration.md).
60
71
  - **Prefer service models for built-in UI.** Check the installed `models/index.d.ts`; model exports
61
72
  vary by `@interopio/wdio-iocd-service` version. Use ordinary WebdriverIO selectors for app UI.
62
73
  - **Preserve user files.** Never put the license (or `user-data`) in specs, logs, or CI artifacts.
@@ -67,5 +78,5 @@ cd tests && npm run typecheck # type-check without launching the
67
78
  - [authoring](./references/authoring.md) — spec shape, fixtures, synchronization, isolation.
68
79
  - [commands](./references/commands.md) — the IOCD-specific WebdriverIO commands (for the rest, read the service `.d.ts`).
69
80
  - [models](./references/models.md) — built-in IOCD UI page objects, current exports, and version rules.
70
- - [configuration](./references/configuration.md) — env vars, overrides, optional libraries, runners, CI.
81
+ - [configuration](./references/configuration.md) — env vars, overrides, per-capability extra config for tests that need other platform settings, SSO startup login automation, optional libraries, runners, CI.
71
82
  - [troubleshooting](./references/troubleshooting.md) — executable, license, startup, IO API, dead window handles, flaky tests.
@@ -34,7 +34,9 @@ describe("behavior under test", () => {
34
34
 
35
35
  Arrange platform state with the io.Connect API (`io`); exercise and assert user-visible behavior with
36
36
  WebdriverIO (`$`, `browser`). For config-dependent suites, read `globalThis.ioCDPlatformConfig` at
37
- describe time (the API isn't available then).
37
+ describe time (the API isn't available then) — and when the behavior needs *different* platform
38
+ settings, give it its own capability and config override, see
39
+ [configuration](./configuration.md).
38
40
 
39
41
  ## The generated specs are your templates
40
42
 
@@ -51,7 +53,8 @@ Each demonstrates a pattern worth copying — read the one closest to your goal:
51
53
  second app received an Interop invocation.
52
54
  - `notifications.spec.ts` — raise a notification and assert it in the Notification Panel.
53
55
  - `workspace-add-app.spec.ts` — add an app through the Workspaces add-application popup (UI clicks).
54
- The popup flow assumes universal mode (`config/system.json` sets `"advancedMode": false`).
56
+ In universal mode the popup renders inside the frame window; check the mode the seed ships and
57
+ adapt the spec instead of forcing `advancedMode` in the test override.
55
58
 
56
59
  ## Fixtures
57
60
 
@@ -66,8 +69,8 @@ For a Channel-enabled fixture, initialize the client API with `IODesktop({ chann
66
69
  enable `details.channelSelector` in its app definition. The built-in selector belongs to the Web
67
70
  Group, not the client app DOM: use `Group` and its frame caption model from
68
71
  `@interopio/wdio-iocd-service/models`, then switch to the selector popup before querying its DOM.
69
- See [models](./models.md), `channel-selector.spec.ts`, and the deterministic Channel definitions in
70
- `config/system.json`.
72
+ See [models](./models.md) and `channel-selector.spec.ts`. Read the configured Channels at runtime
73
+ with `io.channels.all()` — don't hard-code names or redefine Channels in the test override.
71
74
  Register membership changes with `io.channels.onChannelsChanged()` — it isn't a window API. Expose
72
75
  an app readiness signal and wait for it before changing the Channel through the UI, so the test
73
76
  can't act before the app has initialized Channels and registered its listener.
@@ -10,8 +10,143 @@ Read `tests/wdio.config.ts` — it composes `IOCDBaseConfiguration` + `IOCDServi
10
10
  | `iocd:options.configOverrides` | Extra platform config (absolute paths) applied only to tests. |
11
11
  | `iocd:options.ioInitOptions` | APIs + optional `libraries` used to build the test API. |
12
12
 
13
- `config/system.json` registers `tests/tests/config/apps.json` as a path app store and isolates
14
- `folders.userData` to `tests/user-data` (disposable — never commit it or reuse its state).
13
+ ## Configuration layers
14
+
15
+ The config the platform runs with is three layers, each deep-merged over the previous:
16
+
17
+ | Layer | Owner | Where |
18
+ |-------|-------|-------|
19
+ | Default `system.json` | io.Connect Desktop | inside the installed `iocd` component |
20
+ | Seed modifications | the seed | `modifications/**/iocd/config/system.json.merge` — dev *and* packed build |
21
+ | Test overrides | the test package | `config/system.json`, merged on top for the test process only |
22
+
23
+ **Keep the test layer minimal.** Whatever the seed configures is already in effect during a test
24
+ run — don't restate it. Override only what makes the run isolated and reproducible: the fixture app
25
+ store, `folders.userData`, log/crash output locations, and similar plumbing. Changing platform
26
+ behavior here (window mode, Channels, gateway, auth, themes) means the tests no longer exercise the
27
+ platform the seed ships — you'd be testing something else. Behavior changes belong in
28
+ `modifications/`; another *supported* configuration belongs in its own capability (below).
29
+
30
+ The generated `config/system.json` registers `tests/tests/config/apps.json` as a path app store and
31
+ isolates `folders.userData` to `tests/user-data` (disposable — never commit it or reuse its state).
32
+
33
+ ## Tests that need extra config
34
+
35
+ Platform-wide settings (Channel mode, group type, default dialogs, gateway) are fixed for the life
36
+ of a process — a spec can't switch them at run time. Tests that need a configuration other than the
37
+ one the seed ships therefore need **their own platform launch**: one capability per configuration in
38
+ the single `tests/wdio.config.ts`.
39
+
40
+ 1. Add a **delta** file next to the base one — `tests/config/system.<variant>.json`, containing only
41
+ the keys that differ.
42
+ 2. Put the specs that need it in their own folder — `tests/tests/<variant>/`.
43
+ 3. Add a capability for it: same `binary`, `iocd:runner` and `ioInitOptions` as the default one,
44
+ different `configOverrides` and `wdio:specs`.
45
+
46
+ ```ts
47
+ // tests/wdio.config.ts
48
+ const ioInitOptions = { appManager: "full", layouts: "full", channels: true, libraries: ["@interopio/workspaces-api"] };
49
+
50
+ const capability = (specs: string[], extraConfig: string[] = [], exclude: string[] = []) => ({
51
+ browserName: "iocd",
52
+ "iocd:runner": "platform-wdio" as const,
53
+ "iocd:options": {
54
+ binary,
55
+ ioInitOptions,
56
+ // Merged in order over the platform + seed config; the last file wins.
57
+ configOverrides: [path.resolve(rootDir, "config/system.json"), ...extraConfig],
58
+ },
59
+ // Nested array = all matching specs in ONE worker, so the platform starts once per capability.
60
+ "wdio:specs": [specs],
61
+ "wdio:exclude": exclude,
62
+ });
63
+
64
+ capabilities: [
65
+ capability(["./tests/**/*.spec.ts"], [], ["./tests/multi-channel/**/*.spec.ts"]),
66
+ capability(
67
+ ["./tests/multi-channel/**/*.spec.ts"],
68
+ [path.resolve(rootDir, "config/system.multi-channel.json")]
69
+ ),
70
+ ],
71
+ ```
72
+
73
+ Rules:
74
+
75
+ - **Exclude the variant specs from the default capability** (`wdio:exclude`). Without it they run
76
+ twice — once under a configuration they weren't written for.
77
+ - **The default capability keeps the seed's shipped configuration.** A variant covers another
78
+ configuration your seed genuinely supports; it's not a way to make a failing test pass. Behavior
79
+ your users always get belongs in the seed's `modifications/`.
80
+ - **One launch per capability, run sequentially** (`IOCDBaseConfiguration` sets `maxInstances: 1`).
81
+ Budget a full platform startup + shutdown for each — group specs by configuration; never add a
82
+ capability per spec.
83
+ - **`npm test -- --spec <file>` overrides capability spec patterns**, so a single-spec run executes
84
+ that spec under *every* capability. Config-dependent specs must read the live config and skip when
85
+ it doesn't apply (below).
86
+
87
+ ### Read the live config in a spec
88
+
89
+ `globalThis.ioCDPlatformConfig` is the resolved system config, available **synchronously at describe
90
+ time** (the IO API is not) — so it can drive `describe.skip`/`this.skip()` or per-mode test
91
+ generation:
92
+
93
+ ```ts
94
+ type PlatformConfigView = { windows?: { channelSelector?: { type?: "single" | "multi" } } };
95
+ const selectorType = (globalThis as { ioCDPlatformConfig?: PlatformConfigView }).ioCDPlatformConfig
96
+ ?.windows?.channelSelector?.type;
97
+
98
+ describe("multi-Channel selection", function () {
99
+ before(function () {
100
+ if (selectorType !== "multi") {
101
+ this.skip();
102
+ }
103
+ });
104
+ });
105
+ ```
106
+
107
+ Never treat a missing value as `false`/absent: a spec asserting "nothing happens" would then pass
108
+ under every configuration — green for the wrong reason. Skip explicitly, or throw a message naming
109
+ the configuration that was actually found.
110
+
111
+ ## Platform with an SSO login at startup
112
+
113
+ A seed configured with an interactive login (`ssoAuth` in the system config) reports readiness only
114
+ **after** someone signs in — without automation, `npm test` sits on the login window until the
115
+ startup timeout and no spec runs. The service's `login` option drives that window while the
116
+ platform boots. Requires `@interopio/wdio-iocd-service` 1.0.0-next.3+ (the version currently
117
+ generated); older test packages must update `tests/package.json` first.
118
+
119
+ ```ts
120
+ // tests/wdio.config.ts
121
+ services: IOCDService({
122
+ login: {
123
+ window: { url: /login\.mycompany\.com/ }, // substring or RegExp; `title` also works
124
+ handler: async (browser) => {
125
+ await browser.$("#username").setValue(process.env.IOCD_TEST_USER!);
126
+ await browser.$("#password").setValue(process.env.IOCD_TEST_PASSWORD!);
127
+ await browser.$("button[type=submit]").click();
128
+ },
129
+ },
130
+ }),
131
+ ```
132
+
133
+ Rules:
134
+
135
+ - **The handler runs before the platform is ready.** No `browser.io()`, no models — plain
136
+ WebdriverIO against the login page only. The WDIO globals (`browser`, `$`, `$$`) are set while it
137
+ runs, so page objects work.
138
+ - **The signed-in session belongs to the platform process**, so the apps under test are
139
+ authenticated exactly as in production.
140
+ - **Credentials come from env vars / CI secrets** — never from specs or config files.
141
+ - **A still-valid session skips the phase** (no login window appears; `onNoLoginWindow` defaults to
142
+ `"skip"`, set `"fail"` to require one). Budgets: `timeoutMs` (find window + handler) and
143
+ `readyTimeoutMs` (readiness after the handler), both defaulting to 120000.
144
+ - **Any login failure stops the run** — no window, a handler error/timeout, or no readiness after
145
+ the handler. The platform is destroyed and the capability's specs are reported failed. The login
146
+ is never retried.
147
+ - **MFA prompts and native OS dialogs can't be driven by WebDriver** — use an exempt test account or
148
+ a non-interactive auth configuration for the test platform.
149
+ - `login` also accepts `(capability) => LoginOptions | undefined` for per-capability variants.
15
150
 
16
151
  ## Add an optional IO library
17
152
 
@@ -11,7 +11,7 @@ over copying internal IOCD selectors into a spec.
11
11
 
12
12
  ## Current Generated Version
13
13
 
14
- The generated test package currently pins `@interopio/wdio-iocd-service@1.0.0-next.2`. Its
14
+ The generated test package currently pins `@interopio/wdio-iocd-service@1.0.0-next.3`. Its
15
15
  `@interopio/wdio-iocd-service/models` subpath exports:
16
16
 
17
17
  | Area | Models and types |
@@ -18,9 +18,13 @@ the license via secrets. Never hard-code a license in a spec or fixture.
18
18
  ## Startup timeout
19
19
 
20
20
  Temporarily set `iocd:options.enableLogging: true` in `tests/wdio.config.ts` and read the platform
21
- output. Check for invalid config/app paths, license failures, gateway port conflicts (the generated
22
- `config/system.json` pins the test gateway to port 8385), and stale test user-data before raising
23
- `startupTimeoutMs`.
21
+ output. Check for invalid config/app paths, license failures, gateway port conflicts (the port comes
22
+ from the seed's own config — another running instance holds it), and stale test user-data before
23
+ raising `startupTimeoutMs`.
24
+
25
+ If a login window appears and stays open, the seed configures an interactive SSO login and startup
26
+ is blocked until someone signs in. Automate it with the service's `login` option — see
27
+ [configuration](./configuration.md).
24
28
 
25
29
  ## Missing IO API
26
30
 
@@ -29,12 +29,54 @@ npm run typecheck
29
29
  ## Layout
30
30
 
31
31
  - `wdio.config.ts` — WebdriverIO + `IOCDService` configuration (the `platform-wdio` runner).
32
- - `config/system.json` — platform overrides applied only to the test process.
32
+ - `config/system.json` — test-only overrides applied to the test process.
33
33
  - `tests/common/iocd.ts` — polling, event-waiting, and cleanup-registry helpers.
34
34
  - `tests/config/apps.json` — fixture app definitions.
35
35
  - `tests/assets/applications/` — static fixture app HTML.
36
36
  - `tests/*.spec.ts` — the specifications below.
37
37
 
38
+ `config/system.json` is merged over the platform configuration the seed already ships (the io.Connect
39
+ Desktop defaults plus the seed's `modifications/`). Keep it minimal — the fixture app store and the
40
+ isolated `folders.userData` — so the tests exercise the same platform behavior as the built app.
41
+ Change platform behavior in the seed's `modifications/` instead.
42
+
43
+ ## Tests that need extra configuration
44
+
45
+ Platform-wide settings (Channel mode, group type, default dialogs) can't change while the platform
46
+ runs, so specifications that need a different configuration need their own platform launch. Add one
47
+ capability per configuration in `wdio.config.ts`:
48
+
49
+ ```typescript
50
+ capabilities: [
51
+ // Default configuration: everything except the multi-Channel specifications.
52
+ {
53
+ ...baseCapability,
54
+ "iocd:options": { ...baseOptions, configOverrides: [path.resolve(rootDir, "config/system.json")] },
55
+ "wdio:specs": [["./tests/**/*.spec.ts"]],
56
+ "wdio:exclude": ["./tests/multi-channel/**/*.spec.ts"],
57
+ },
58
+ // Extra configuration: only the specifications written for it.
59
+ {
60
+ ...baseCapability,
61
+ "iocd:options": {
62
+ ...baseOptions,
63
+ configOverrides: [
64
+ path.resolve(rootDir, "config/system.json"),
65
+ path.resolve(rootDir, "config/system.multi-channel.json"),
66
+ ],
67
+ },
68
+ "wdio:specs": [["./tests/multi-channel/**/*.spec.ts"]],
69
+ },
70
+ ],
71
+ ```
72
+
73
+ Capabilities run one after another (`maxInstances` is `1`), each starting and stopping its own
74
+ platform, so group specifications by configuration rather than adding a capability per file. Exclude
75
+ the variant specifications from the default capability so they don't run twice, keep the default
76
+ capability on the configuration the seed ships, and let a configuration-dependent specification read
77
+ `globalThis.ioCDPlatformConfig` at `describe` time to skip when it isn't running under its own
78
+ capability (`npm test -- --spec` overrides the capability spec patterns).
79
+
38
80
  ## Sample specifications
39
81
 
40
82
  | Specification | What it demonstrates |
@@ -1,12 +1,4 @@
1
1
  {
2
- "advancedMode": false,
3
- "channels": {
4
- "enabled": true,
5
- "definitions": [
6
- { "name": "Red", "meta": { "color": "red" } },
7
- { "name": "Green", "meta": { "color": "green" } }
8
- ]
9
- },
10
2
  "appStores": [
11
3
  {
12
4
  "type": "path",
@@ -15,21 +7,6 @@
15
7
  }
16
8
  }
17
9
  ],
18
- "gw": {
19
- "configuration": {
20
- "port": 8385,
21
- "ip": "127.0.0.1",
22
- "authentication": {
23
- "available": ["basic"],
24
- "default": "basic"
25
- }
26
- }
27
- },
28
- "windowManagement": {
29
- "group": {
30
- "type": "Web"
31
- }
32
- },
33
10
  "folders": {
34
11
  "userData": "%WDIO_ROOT_DIR%/user-data"
35
12
  }
@@ -10,7 +10,7 @@
10
10
  "license": "MIT",
11
11
  "devDependencies": {
12
12
  "@interopio/desktop": "^6.21.0",
13
- "@interopio/wdio-iocd-service": "1.0.0-next.2",
13
+ "@interopio/wdio-iocd-service": "1.0.0-next.3",
14
14
  "@interopio/workspaces-api": "^4.5.0",
15
15
  "@types/chai": "^5.2.3",
16
16
  "@types/mocha": "^10.0.10",
@@ -1141,9 +1141,9 @@
1141
1141
  }
1142
1142
  },
1143
1143
  "node_modules/@interopio/wdio-iocd-service": {
1144
- "version": "1.0.0-next.2",
1145
- "resolved": "https://registry.npmjs.org/@interopio/wdio-iocd-service/-/wdio-iocd-service-1.0.0-next.2.tgz",
1146
- "integrity": "sha512-ZjWJ4CQSQIcLLmlPhXT0BSKp5Gn+4NAMp4ybHGmghW3v3h8Wi0ip0VNQvmrqpTj5/gily9USmRXByJu2Ls2FnQ==",
1144
+ "version": "1.0.0-next.3",
1145
+ "resolved": "https://registry.npmjs.org/@interopio/wdio-iocd-service/-/wdio-iocd-service-1.0.0-next.3.tgz",
1146
+ "integrity": "sha512-BR4yqnf888cp9IRiSusCXPPn5v1eJhdTQPSrv6CIth1NBVx9XH08UexopNcgLl8dqh6NCeEWbXoAivuTFFQ4bg==",
1147
1147
  "dev": true,
1148
1148
  "license": "MIT",
1149
1149
  "dependencies": {
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "devDependencies": {
12
12
  "@interopio/desktop": "^6.21.0",
13
- "@interopio/wdio-iocd-service": "1.0.0-next.2",
13
+ "@interopio/wdio-iocd-service": "1.0.0-next.3",
14
14
  "@interopio/workspaces-api": "^4.5.0",
15
15
  "@types/chai": "^5.2.3",
16
16
  "@types/mocha": "^10.0.10",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interopio/iocd-cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "CLI tool for setting up, building and packaging io.Connect Desktop platforms",
5
5
  "engines": {
6
6
  "node": ">=22.0.0",