Add a New App

Register your Application-Under-Test (AUT) in GlobalConfig, choose the right platform tool, and verify it resolves before you model a single Control.

← Back to overview · 🇩🇪 Deutsch · ← Concepts · Add Controls →


Decide which platform tool fits your app

Every App-Under-Test (AUT) is one entry in GlobalConfig.apps — a type + tool pair that tells the framework which runtime Strategy to resolve calls through. Pick the row that matches your app:

Your app is a … type tool Status
Web app (browser-rendered) 'Web' 'Playwright' Available now
Windows desktop app 'Desktop' 'Appium-Windows' Available now
macOS desktop app 'Desktop' 'Mac2' Type defined, not yet shipped
Android app 'Mobile' 'UiAutomator2' Type defined, not yet shipped
iOS app 'Mobile' 'XCUITest' Type defined, not yet shipped

💡 What “type defined, not yet shipped” means. The AppConfig union already has a shape for macOS/Android/iOS entries — TypeScript accepts one, and nothing stops you from writing it. But no runtime Strategy is registered for Mac2/UiAutomator2/XCUITest today: the first Action or Check against such an entry fails at runtime with StrategyRegistry: No factory registered for tool "...". See FAQ — Does the framework support mobile testing? for the current status. Web and Windows-Desktop are the two platforms with a working runtime behind them right now — the rest of this page covers both.


Register a Web app

Add an entry to 2_Apps/1_Global/GlobalConfig.ts — the minimal shape needs only type, tool, and baseUrl:

// 2_Apps/1_Global/GlobalConfig.ts
export const apps = {
    YourApp: {
        type: 'Web',
        tool: 'Playwright',
        baseUrl: 'https://<your-app-domain>',
    },
} as const;

A realistic entry usually separates the URL into its own constant, so switching between staging and production touches one line instead of the whole object:

// 2_Apps/1_Global/GlobalConfig.ts
export const URL_YOUR_APP = process.env.YOUR_APP_URL ?? 'https://staging.<your-app-domain>';

export const apps = {
    YourApp: {
        type: 'Web',
        tool: 'Playwright',
        baseUrl: URL_YOUR_APP,
        headless: true,
    },
} as const;

headless only matters when a Control or a standalone Inspector run self-bootstraps a browser without an injected Playwright page — a TestCase running through the normal Playwright test-runner fixture ignores it. Once the entry exists, Core.defineExecutionStep('YourApp', 'Start', (env) => ...) resolves env.page/env.url from it automatically — see Build TestSteps.

Scaffold a Web app automatically with the CLI

The steps above — copy _Skeleton, substitute its App-name placeholder, add the GlobalConfig.apps entry, wire the global barrel — are also available as one command:

npx cc-testframework create-web-app --name DemoWeb --url http://localhost:8080

This copies the shipped _Skeleton template (12 Controls, TS_Main/TS_Dialog/TS_Message/TS_Execution/TS_Custom plus their .i18n.json catalogs) into 2_Apps/<N>_DemoWeb/<N> is the next free numeric prefix, auto-detected from your existing 2_Apps/ folders — replaces every __APP_NAME__ placeholder in the copied files with DemoWeb, and adds the matching entry to 2_Apps/1_Global/GlobalConfig.ts:

// 2_Apps/1_Global/GlobalConfig.ts
export const apps = {
    DemoWeb: {
        type: 'Web',
        tool: 'Playwright',
        baseUrl: 'http://localhost:8080',
    },
} as const;

It then also wires the app into 2_Apps/1_Global/References.ts — the global barrel every TestCase imports through (import * as Project from '@GlobalRef') — so Project.DemoWeb.* is importable right away, with no manual edit needed:

// 2_Apps/1_Global/References.ts
export * as DemoWeb from '../2_DemoWeb/References';

The re-export line mirrors whichever import style your barrel already uses predominantly — a relative specifier as shown above, or @Apps/2_DemoWeb/References when your existing app lines already use that path-alias form.

Flag Required Description
--name <Name> Yes The App name — a valid identifier (letters, digits, underscore; can’t start with a digit). Used as both the 2_Apps/<N>_<Name>/ folder suffix and the apps/barrel export key.
--url <url> Yes Base URL of the app under test — http://, https://, or file://.
--project-dir <path> No Project root containing 2_Apps/ (auto-detected by default — the command walks upward from your current directory looking for 2_Apps/1_Global/GlobalConfig.ts under ., ./pm, and ./tests).
--dry-run No Print the plan — target folder, files that would be written, the GlobalConfig.ts entry, and the References.ts plan — without touching disk.
--json No Additionally emit a machine-parseable JSON summary (including a referencesUpdated field, see below) alongside the human-readable output, for scripting or CI.
--help No Show usage.

The command exits 0 on success and 2 on a user error (invalid --name/--url, no project root found, or the target already registered). It’s idempotent: it refuses to overwrite an existing 2_Apps/<N>_<Name>/ folder or a duplicate apps key, and it leaves References.ts untouched if a re-export for that name already exists — re-running it against an already-scaffolded App fails safely instead of clobbering anything.

The --json summary’s referencesUpdated field reports what happened to the barrel: true (the re-export line was written), false (nothing to write — the app already had one, or no barrel file exists at all), or "skipped-example" for the pre-onboarding case below.

💡 The one case that still needs a manual step. If your project’s global barrel is still the un-activated 2_Apps/1_Global/References.ts.example (never renamed/filled in since scaffolding), the command soft-skips the barrel wiring — it never mutates a .example file — and prints the exact re-export line to add once you’ve activated it. Rename References.ts.example to References.ts, fill in the required re-exports described inside it, then add the printed line yourself. This is the only scenario where Project.DemoWeb.* isn’t importable immediately after the command finishes.

Run it with --dry-run first to see exactly what would land on disk (and what the References.ts plan looks like) without writing anything:

npx cc-testframework create-web-app --name DemoWeb --url http://localhost:8080 --dry-run

💡 Building against a Windows Desktop AUT instead? See Scaffold a Desktop app automatically with the CLI below — the Appium-Windows sibling of this command, same workflow.

Register an Electron app

An Electron app — Calculator-style packaged desktop apps built on Electron, or any app you build on it yourself — is a Web app tested via Playwright, not a Desktop app tested via Appium. Its UI is a Chromium renderer, plain DOM, not a native UIA tree, so it registers with the same type/tool pair as a browser web app. The only thing that changes is how the app gets launched: point executablePath at the packaged app’s executable instead of giving baseUrl a URL to open.

💡 One-command shortcut. npx cc-testframework create-electron-app --name <YourApp> --executable-path <path> scaffolds and registers this in one step — see Scaffold a New App — Electron app.

// 2_Apps/1_Global/GlobalConfig.ts
export const apps = {
    MyElectronApp: {
        type: 'Web',
        tool: 'Playwright',
        executablePath: 'C:\\path\\to\\MyElectronApp.exe', // the packaged Electron app's .exe
        // args: ['--flag'], // optional launch args
    },
} as const;

type stays 'Web', tool stays 'Playwright' — it’s one config shape, not a separate type/tool combination. The presence of executablePath (instead of baseUrl) is what marks the entry as Electron.

An Electron app is scaffolded from its own dedicated _Skeleton_Electron root, not from the browser _Skeleton. Its lifecycle steps use the same plain names as a browser web app — TS_Execution_Start/_Close/_Restart — since a scaffolded app is always exactly one of the two (browser web or Electron), never both at once, so there’s no naming collision to avoid and no *Electron suffix. These steps call Core.I_ElectronHandler internally, which launches the process via Playwright’s _electron.launch() instead of chromium.launch(), then uses the app’s first window as the session page.

💡 After launch, it’s a normal web page. Once TS_Execution_Start has run, the Electron window is an ordinary Playwright page — a Chromium-rendered DOM. Every existing Web Control and every TS_Main/TS_Dialog/TS_Message step works against it unchanged; there is no separate Electron-flavored Control API to learn.

Prerequisite: executablePath must point at the packaged Electron app’s .exe (or platform-equivalent binary) on the machine running the test process — the same app you’d otherwise double-click to open it.


Register a Windows Desktop app

💡 Electron-based Windows app? Register it as a Web app instead — see Register an Electron app above. Its renderer is a Chromium DOM, not a native UIA tree, so it goes through Playwright, not Appium/WinAppDriver.

A Windows Desktop AUT needs an executable path instead of a baseUrl, plus an optional appiumUrl for the Appium/WinAppDriver server:

// 2_Apps/1_Global/GlobalConfig.ts
export const apps = {
    YourDesktopApp: {
        type: 'Desktop',
        tool: 'Appium-Windows',
        executable: 'C:\\Users\\Public\\<YourApp>\\<YourApp>.exe',
        appiumUrl: 'http://host.docker.internal:4723',
    },
} as const;
  • executable is the absolute path WinAppDriver passes as the appium:app capability — it must exist on the machine the Appium server itself runs on, not necessarily the one running the test process.
  • appiumUrl is optional. Omit it and the framework resolves a sensible default through a three-tier priority chain (explicit value → APPIUM_URL environment variable → platform default) — see API Reference — Appium URL resolution and FAQ for the full chain and troubleshooting.
  • capabilities accepts an optional Record<string, unknown> merged over the framework’s default WinAppDriver capabilities, for anything app-specific the two fields above don’t cover.

Scaffold a Desktop app automatically with the CLI

The Desktop sibling of create-web-app — same one-command workflow, but for an Appium-Windows Desktop AUT instead of a Playwright Web AUT:

npx cc-testframework create-desktop-app --name DemoDesktop --executable "C:\apps\Demo.exe"
  create-web-app create-desktop-app
Skeleton copied _Skeleton _Skeleton_Desktop
Target flag --url <url> --executable <path> (+ optional --appium-url <url>)
GlobalConfig.apps entry type: 'Web', tool: 'Playwright', baseUrl type: 'Desktop', tool: 'Appium-Windows', executable, appiumUrl
Skeleton contents 12 Controls, TS_Main/Dialog/Message/Execution/Custom 12 Controls, TS_Main/Dialog/Message/Execution (no TS_Custom)

This copies the shipped _Skeleton_Desktop template into 2_Apps/<N>_DemoDesktop/ — same numeric-prefix auto-detection and __APP_NAME__ substitution as create-web-app — and adds the matching entry to GlobalConfig.ts:

// 2_Apps/1_Global/GlobalConfig.ts
export const apps = {
    DemoDesktop: {
        type: 'Desktop',
        tool: 'Appium-Windows',
        executable: 'C:\\apps\\Demo.exe',
        appiumUrl: 'http://host.docker.internal:4723',
    },
} as const;

It wires 2_Apps/1_Global/References.ts exactly like create-web-app — same alias-style mirroring, same idempotency, same .example pre-onboarding soft-skip — see Scaffold a Web app automatically with the CLI above for the full explanation of that mechanism; it isn’t repeated here.

Flag Required Description
--name <Name> Yes Same rule as create-web-app — a valid identifier, used as the folder suffix and the apps/barrel export key.
--executable <path> Yes Path to the app’s .exe on the Appium/Windows host, not on the machine running this command — not checked against the local filesystem.
--appium-url <url> No Appium server URL. Default: http://host.docker.internal:4723 (the devcontainer→Windows-host default).
--project-dir <path> No Same as create-web-app.
--dry-run No Same as create-web-app — prints the plan (target folder, GlobalConfig.ts entry, References.ts plan) without touching disk.
--json No Same as create-web-app — the JSON summary adds executable/appiumUrl alongside referencesUpdated.
--help No Show usage.

Same exit codes (0/2) and the same idempotency guarantees as create-web-app.

💡 Scaffolding runs anywhere; running the tests needs the Appium host. The command never touches the path in --executable — it only writes a string into GlobalConfig.ts, so it works from any machine, including a devcontainer with no Windows host attached. Actually running the generated TestCases needs a reachable Appium/WinAppDriver server at --appium-url, with the .exe present on that host — see FAQ — How does the framework know where to find the Appium server for Desktop tests? and the appiumUrl explanation above.

Run it with --dry-run first, same as create-web-app:

npx cc-testframework create-desktop-app --name DemoDesktop --executable "C:\apps\Demo.exe" --dry-run

UWP / Windows-Store apps: executable also accepts an AUMID

Not every Windows app is a classic Win32 .exe. UWP apps — Calculator and most Microsoft Store apps — have no plain executable file on disk to point at. For these, executable takes an AppUserModelId (AUMID) instead of a file path. It’s the same field either way — WinAppDriver’s app capability launches a .exe path and an AUMID transparently, with no extra field and no extra step:

// 2_Apps/1_Global/GlobalConfig.ts
export const apps = {
    Calc: {
        type: 'Desktop',
        tool: 'Appium-Windows',
        executable: 'Microsoft.WindowsCalculator_8wekyb3d8bbwe!App', // AUMID
    },
} as const;

💡 Finding an app’s AUMID. Run Get-StartApps in a PowerShell session on the machine the Appium server runs on — it lists every installed Start-menu app together with its AUMID. Narrow it down for a specific app with Get-StartApps | Where-Object AppID -like "*Calculator*". If you scaffold the App entry through the framework’s setup tooling, its Store-app picker looks the AUMID up for you — the manual Get-StartApps route above is the one to fall back on, or to use directly.

Prerequisites: the Store app must be installed on the machine WinAppDriver runs on, and a reachable Appium/WinAppDriver session is required either way — see FAQ — How does the framework know where to find the Appium server for Desktop tests?.


Register a Mobile app

GlobalConfig.apps accepts a type: 'Mobile' entry today, shaped like this:

// 2_Apps/1_Global/GlobalConfig.ts
export const apps = {
    YourMobileApp: {
        type: 'Mobile',
        tool: 'UiAutomator2', // or 'XCUITest' for iOS
        appPath: '/path/to/YourApp.apk', // or a bundle id for an already-installed iOS app
    },
} as const;

TypeScript accepts this entry and GlobalConfig.apps type-checks — but as the table above notes, no runtime Strategy is registered for UiAutomator2/XCUITest yet, so a Control action against this entry throws at runtime rather than at compile time. Register it now if you want the shape ready for later; don’t rely on it resolving a real session today.


Register more than one App

GlobalConfig.apps is a single object — most projects grow more than one entry over time, mixing families freely:

// 2_Apps/1_Global/GlobalConfig.ts
export const apps = {
    YourWebApp: {
        type: 'Web',
        tool: 'Playwright',
        baseUrl: URL_YOUR_WEB_APP,
    },
    YourDesktopApp: {
        type: 'Desktop',
        tool: 'Appium-Windows',
        executable: 'C:\\Users\\Public\\<YourApp>\\<YourApp>.exe',
    },
} as const;

Each key is independent — a TestCase composing steps from YourWebApp and YourDesktopApp in the same file works exactly like composing steps from one App, since every defineTestStep/defineExecutionStep call already carries its own App key. Add a new key any time a new AUT joins your test suite; existing entries and the TestCases built on them are unaffected.


Verify your App is reachable

A one-line TS_Execution_Start/TS_Execution_Close pair — see Build TestSteps for the full file — confirms the entry resolves before you invest in Controls:

// 3_Cases/TC_Smoke.spec.ts
import * as Project from '@GlobalRef';

Project.Core.test('TC_Smoke', async () => {
    Project.Core.Step.setCurrentTestCaseName('TC_Smoke');
    await Project.YourApp.TS_Execution_Start();
    await Project.YourApp.TS_Execution_Close();
});

Run it with npx playwright test 3_Cases/TC_Smoke.spec.ts from inside pm/ — see Run and Debug for flags and options.

Common failures and their fix:

Error Cause Fix
unknown appName 'YourApp' Typo, or the entry is missing from GlobalConfig.apps Check the key spelling matches exactly, including case
Web app 'YourApp' has no 'baseUrl' type: 'Web' entry missing the baseUrl field Add baseUrl to the entry
Desktop app 'YourApp' has no 'executable' type: 'Desktop', tool: 'Appium-Windows' entry missing executable Add the absolute .exe path
StrategyRegistry: No factory registered for tool "..." Entry uses Mac2, UiAutomator2, or XCUITest Not runnable yet — see Decide which platform tool fits your app above
Playwright times out navigating to baseUrl App unreachable from the machine running the tests (VPN, wrong environment URL, container networking) Verify the URL opens in a plain browser from the same machine/container the tests run in

Next step: model your Controls

With your App registered and reachable, model the interactive elements on its screens as Controls.

Next: Add Controls — locators, actions, and where to place the Control file.


📧 Questions? Contact: jens.szelag@itsbusiness.ch

itsbusiness AG · Bern · Switzerland