OS App

Two framework-shipped Apps for file, shell, system-info, and Windows-Registry interaction — the utility work almost every E2E suite needs, without writing your own Controls for it.

← Back to overview · 🇩🇪 Deutsch · ← Credential Management · FTP App →


What the OS App provides

Two ready-to-use Apps ship alongside _Skeleton and _ExampleWebApp — pick them in the scaffold wizard, or add their re-export by hand to an existing project:

App Platform Controls Purpose
_OS_Common Web, Desktop, Mobile — every OS FileEditor, SystemInfo, Shell Files, system time/version/hostname, arbitrary shell commands
_OS_Windows Windows only Registry, SystemInfoWindows Windows-Registry reads/writes, Windows Update status, installed-programs list, system time

Both are ordinary Apps in the three-layer sense described in Concepts — Controls, TestSteps, and a References.ts barrel — the only difference from an App you’d write yourself is that the framework ships them pre-instantiated. 13 TestSteps come from _OS_Common, 9 from _OS_Windows, all named TS_Main_<Control>_<Action> per the usual naming convention.


Import the OS Controls into your test

Your project’s 2_Apps/1_Global/References.ts re-exports each App’s barrel, exactly like any other scaffolded App:

// 2_Apps/1_Global/References.ts
export * as OSCommon from '../_OS_Common/References';
export * as OSWindows from '../_OS_Windows/References';

With that in place, every step is callable from a TestCase through Project:

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

await Project.OSCommon.TS_Main_FileEditor_CheckFileExists('./test-data/seed.json');

Unlike a TS_Main_* step written against your own App (see Build TestSteps), these steps take only their own business parameters — no leading refId, and no pageLogName/sectionName. None of the OS Steps address a screen element, so there is no reference screenshot to bind and no page context to log — the call shape is simply TS_Main_<Control>_<Action>(...businessArgs).

OSWindows imports without error on Linux and macOS too — only calling one of its methods on a non-Windows machine throws. See Windows-only Controls below.


Work with files (FileEditor)

Step What it does
TS_Main_FileEditor_CheckFileExists Returns whether a file exists at the given path
TS_Main_FileEditor_CreateTextfile Writes a text file, creating any missing parent folders
TS_Main_FileEditor_ReadTextfile Reads a text file’s full content back as a string
TS_Main_FileEditor_AppendTextfile Appends to an existing text file without overwriting it
TS_Main_FileEditor_RenameFile Renames or moves a file
TS_Main_FileEditor_GetFileModifyTime Returns a file’s last-modified timestamp
TS_Main_FileEditor_DeleteFile Deletes a file

A typical daily-grind flow — seed test data, let the app under test write an output file, assert on it, then clean up:

// Seed a fixture file before the flow starts
await Project.OSCommon.TS_Main_FileEditor_CreateTextfile('./test-data/import.csv', 'id,name\n1,Acme Corp');

// The app under test picks this up and later writes an export file — confirm it exists
await Project.OSCommon.TS_Main_FileEditor_CheckFileExists('./output/export.csv');

// Read it back and assert on the content
const csv = await Project.OSCommon.TS_Main_FileEditor_ReadTextfile('./output/export.csv');
await Core.Check.textOrValueIsSet(csv, true, 'Acme Corp');

// Append a run marker to a shared log instead of overwriting it
await Project.OSCommon.TS_Main_FileEditor_AppendTextfile('./output/run.log', `\n[${new Date().toISOString()}] flow finished`);

// Archive this run's export before the next run starts
await Project.OSCommon.TS_Main_FileEditor_RenameFile('./output/export.csv', './output/archive/export-previous.csv');

// Clean up in an afterEach/teardown block
await Project.OSCommon.TS_Main_FileEditor_DeleteFile('./test-data/import.csv');

TS_Main_FileEditor_GetFileModifyTime returns a file’s last-modified time as a Date — useful for a freshness assertion:

// Capture a timestamp before the app under test writes its export
const before = new Date();
await Project.OSCommon.TS_Main_FileEditor_CheckFileExists('./output/export.csv');

// Prove the file was actually rewritten this run, not left over from a previous one
const modifiedAt = await Project.OSCommon.TS_Main_FileEditor_GetFileModifyTime('./output/export.csv');
await Core.Check.exists(modifiedAt > before, true);

Query system info (SystemInfo)

Step What it does
TS_Main_SystemInfo_GetSystemTime Returns the current local time as a Date
TS_Main_SystemInfo_CheckSystemTime Compares local time against an external time-provider URL, within an allowed drift
TS_Main_SystemInfo_GetOSVersion Returns platform, release, and version of the OS running the test
TS_Main_SystemInfo_GetHostname Returns the machine’s hostname

TS_Main_SystemInfo_CheckSystemTime is the one built for a specific problem: a test-runner’s clock drifting out of sync silently breaks anything that relies on timestamps (token expiry, scheduled jobs, “created within the last minute” assertions) without an obvious error pointing at the clock itself.

// Fail fast if this machine's clock has drifted more than 5 seconds from a trusted external source
const drift = await Project.OSCommon.TS_Main_SystemInfo_CheckSystemTime('https://timeapi.io/api/Time/current/zone?timeZone=UTC', 5);
await Core.Check.exists(drift.withinTolerance, true);

// GetSystemTime returns a plain Date you can capture and reuse in your own comparisons
const now = await Project.OSCommon.TS_Main_SystemInfo_GetSystemTime();

// OS version and hostname come back as structured values, ready for your own diagnostics
const osVersion = await Project.OSCommon.TS_Main_SystemInfo_GetOSVersion();
const hostname = await Project.OSCommon.TS_Main_SystemInfo_GetHostname();
Core.Logger.info(`${now.toISOString()} — running on ${osVersion.platform} ${osVersion.release} (${hostname})`);

Execute shell commands (Shell)

Step What it does
TS_Main_Shell_ExecuteCommand Runs a single shell command, returning stdout/stderr/exit-code without throwing on a non-zero exit
TS_Main_Shell_RunScript Runs a script file, picking the interpreter from its extension (.sh, .ps1, .bat/.cmd)
// Prepare a database fixture before the suite runs
const result = await Project.OSCommon.TS_Main_Shell_ExecuteCommand('npm run seed:test-db');
await Core.Check.exists(result.exitCode === 0, true);

// Run a maintained setup script instead of duplicating its logic inline
await Project.OSCommon.TS_Main_Shell_RunScript('./scripts/prepare-fixtures.sh', ['--env', 'test']);

Treat any command built from user- or test-data-supplied input as untrusted. TS_Main_Shell_ExecuteCommand/TS_Main_Shell_RunScript run through the OS shell exactly as given — string-concatenating an unsanitized value into the command (e.g. a value read from a fixture file or an external API response) opens the same injection risk as building a SQL query from unsanitized input. Keep the command string itself hard-coded or sourced from your own trusted scripts, and pass variable data as separate arguments rather than splicing it into the command text.


Windows-only Controls

Requires the Windows platform. Registry and SystemInfoWindows only work when the test runner itself is on Windows — not when the App under test happens to be a Windows desktop app tested from a different host. Importing Project.OSWindows succeeds on Linux and macOS; calling any method below on a non-Windows machine throws This Control requires the Windows platform, so a suite that conditionally uses these steps should guard the call with a platform check first.

Read and write the Windows Registry

Step What it does
TS_Main_Registry_CheckKeyExists Returns whether a Registry key exists
TS_Main_Registry_CheckValueContains Returns whether a value contains an expected substring
TS_Main_Registry_ReadValue Reads a Registry value
TS_Main_Registry_AddKey Creates a Registry key (a no-op if it already exists)
TS_Main_Registry_DeleteKey Deletes a Registry key and all its subkeys

Every step accepts one of the 5 standard hives — HKLM, HKCU, HKCR, HKU, HKCC — as its first argument, then the key path:

// Verify the app under test persisted its "remember me" setting
const remembersLogin = await Project.OSWindows.TS_Main_Registry_CheckValueContains(
    'HKCU', 'Software\\YourApp\\Settings', 'RememberLogin', 'true',
);
await Core.Check.exists(remembersLogin, true);

// Read a value back directly, to assert on it or pass it into a later step
const theme = await Project.OSWindows.TS_Main_Registry_ReadValue('HKCU', 'Software\\YourApp\\Settings', 'Theme');
await Core.Check.textOrValueIsSet(theme, true, 'Dark');

// Set up a config-persistence test fixture, then clean it up afterwards
await Project.OSWindows.TS_Main_Registry_AddKey('HKCU', 'Software\\YourApp\\TestFixture');
await Project.OSWindows.TS_Main_Registry_DeleteKey('HKCU', 'Software\\YourApp\\TestFixture');

Query Windows-specific system info

Step What it does
TS_Main_SystemInfoWindows_GetWindowsUpdateInformation Returns last check/install times and whether a reboot is pending
TS_Main_SystemInfoWindows_CheckAppNotExists Returns whether a program is absent from the installed-programs list
TS_Main_SystemInfoWindows_ListInstalledPrograms Returns the full installed-programs list (name, version, publisher)
// Confirm your installer's uninstall path actually removed the program
const stillInstalled = await Project.OSWindows.TS_Main_SystemInfoWindows_CheckAppNotExists('Your App Name');
await Core.Check.exists(stillInstalled, true);

// GetWindowsUpdateInformation returns a structured object — assert no reboot is pending before a clean-update test
const updateInfo = await Project.OSWindows.TS_Main_SystemInfoWindows_GetWindowsUpdateInformation();
await Core.Check.exists(updateInfo.pendingRebootRequired, false);

// ListInstalledPrograms returns the full list — confirm your app made it onto the machine
const programs = await Project.OSWindows.TS_Main_SystemInfoWindows_ListInstalledPrograms();
await Core.Check.exists(programs.some((p) => p.displayName === 'Your App Name'), true);

Change the system time (destructive — requires admin)

TS_Main_SystemInfoWindows_SetSystemTime changes the machine’s actual system clock — there is no dry-run or simulation mode. This requires administrator privileges on the machine running the test, and most CI runners deliberately withhold that permission. Reserve it for a dedicated, disposable Windows VM used specifically for clock-dependent test scenarios (expiry logic, scheduled tasks, daylight-saving transitions) — never point it at a shared developer machine or a CI runner other tests depend on.

// Only ever run this against a disposable, dedicated test VM
await Project.OSWindows.TS_Main_SystemInfoWindows_SetSystemTime(new Date('2026-12-31T23:59:00Z'));

Combine OS steps with your app tests

The OS App is most useful combined with a TestStep against your own App in the same flow — for example, verifying that your Windows desktop application actually reads a setting from the Registry on startup:

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

Project.Core.test('TC_ConfigPersistence', async () => {
    Project.Core.Step.setCurrentTestCaseName('TC_ConfigPersistence');

    await Project.Core.Step.numberedStepBlock('Prepare a known config value', async () => {
        await Project.OSWindows.TS_Main_Registry_AddKey('HKCU', 'Software\\YourApp\\Settings');
        await Project.OSWindows.TS_Main_Registry_ReadValue('HKCU', 'Software\\YourApp\\Settings', 'Theme');
    });

    await Project.Core.Step.numberedStepBlock('Start the app and verify it applied the setting', async () => {
        await Project.YourApp.TS_Execution_Start();
        await Project.YourApp.TS_Main_CheckActiveTheme('', '', '', 'Dark');
        await Project.YourApp.TS_Execution_Close();
    });

    await Project.Core.Step.numberedStepBlock('Clean up the Registry fixture', async () => {
        await Project.OSWindows.TS_Main_Registry_DeleteKey('HKCU', 'Software\\YourApp\\Settings');
    });
});

Migrating to v0.22.0

Breaking change. Before v0.22.0, every _OS_Common/_OS_Windows step took the same leading refId, pageLogName, sectionName triple as a TS_Main_* step written against your own App (see Build TestSteps). As of v0.22.0, all TS_Main_FileEditor_*, TS_Main_SystemInfo_*, TS_Main_Shell_*, TS_Main_Registry_*, and TS_Main_SystemInfoWindows_* steps take only their own business parameters.

// before (≤ 0.21.0)
await Project.OSCommon.TS_Main_FileEditor_ReadTextfile(refId, pageLogName, sectionName, filePath);

// after (0.22.0)
await Project.OSCommon.TS_Main_FileEditor_ReadTextfile(filePath);

Why: none of these steps address a screen element — there is no reference screenshot to bind for Self-Healing, and no page to name in the report. The leading three arguments existed only because every step used to share one factory with UI-facing steps; the OS steps now use a dedicated, context-free step shape instead, and the ceremony that only made sense for UI steps drops away.

Update every OS-App call site in your project by removing its first three arguments — nothing else about the step (its name, its remaining arguments, or its return value) changes.


Common errors and how to fix them

Error Cause Fix
ENOENT FileEditor was pointed at a path whose parent folder doesn’t exist (for readTextfile/deleteFile/renameFile) or the file itself doesn’t exist yet createTextfile creates missing parent folders automatically; for the other steps, check the path first with TS_Main_FileEditor_CheckFileExists
EACCES The OS user running the tests doesn’t have permission to read/write/delete the target path Point at a path your test user owns (e.g. a project-local test-data/ folder) rather than a system or another user’s folder
This Control requires the Windows platform A Registry/SystemInfoWindows method was called on Linux or macOS Guard the call with a platform check, or only include this TestStep in a Windows-only test project/CI job
winreg not installed Registry is used on Windows, but npm install ran on a non-Windows machine first (winreg is an optionalDependencies entry) Run npm install again from a Windows machine, or in a Windows CI job, so the optional native dependency is actually installed
PowerShell not available SystemInfoWindows runs on a stripped-down Windows environment without powershell.exe on PATH Ensure PowerShell is installed and on PATH — standard on any full Windows Desktop/Server install, but sometimes missing from minimal container images

Where to go next

  • Build TestSteps — the general TS_Main_* call shape and naming convention these steps follow
  • Writing Your First TestCase — composing steps from multiple Apps into one TestCase
  • Skeleton Conventions — the naming/signature rules the shipped _OS_Common/_OS_Windows files conform to
  • Credential Management — the other framework-shipped storage/secret utilities
  • FTP App — the other framework-shipped utility App, for SFTP and FTP/FTPS file transfer
  • FAQ — troubleshooting setup issues

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

itsbusiness AG · Bern · Switzerland