Getting started
SPART-Desk automates Windows desktop applications from TypeScript: lazy locators, strict mode, auto-waiting actions and retrying assertions, through the accessibility tree first and the screen where a locator asks for it. This page takes a new user from installation to a first script. The Windows backend is SPART-Desk's own helper executable, which ships inside the package, behind SPART-Desk's own abstractions (backend-windows.md).
1. Install
npm install @spartaficial/spart-desk
npx spart-desk init # spart-desk.config.ts + an example test (asks first; --yes takes the defaults)
npx spart-desk doctor # what this machine can do, capability by capability
init writes spart-desk.config.ts, the project's defaults (runtime,
timeouts, expect.timeout, where failure screenshots go) and an example
test for Vitest or node:test. Nothing has to be installed beside the
package: the Windows backend ships inside it, and reading text off the
screen and matching a picture are the engine's own work (see
resolution.md); doctor says whether the helper runs on
this machine. Requirements: Node.js 24+, Windows 10 1809 or later, Windows
11, or Windows Server 2025 (Desktop Experience) for running automation (the package installs and type-checks anywhere). Offline
machines: distribution.md.
2. First script
import { Desktop, expectDesk } from "@spartaficial/spart-desk";
await using desktop = await Desktop.create(); // reads spart-desk.config.ts; options passed here win over it
const app = await desktop.launch({ executable: "C:\\Windows\\System32\\charmap.exe" });
const main = app.window();
await main.getByLabel("Characters to copy").fill("Hello");
await main.getByRole("checkbox", { name: "Advanced view" }).click();
await expectDesk(main.getByLabel("Characters to copy")).toHaveValue("Hello");
const text = await expectDesk.poll(() => main.getByLabel("Characters to copy").textContent()).toContain("Hello"); // poll returns the value
console.log(desktop.actions.records.map((r) => `${r.action} via ${r.strategy}`));
// fill via uia.set-value, click via uia.invoke, expect.toHaveValue via observe
What happened:
Desktop.createreadspart-desk.config.tsfrom the working directory (without one, or withDesktop.create({ timeout: 10_000 }), the options apply directly) and chose the local runtime and the Windows backend (the helper).launchstarted the process and waited for its first window.- Every locator is lazy:
getByLabel(...)describes a control; onlyfill()resolved it — UI Automation first — and auto-waited until it was visible and enabled. - Actions prefer accessibility patterns (
uia.set-value,uia.invoke) and fall back to real mouse/keyboard input only when a control has no pattern. await usingclosed the application when the scope ended.
3. Run it
Node 24 runs TypeScript directly: node script.ts. Any test runner works;
expectDesk does not depend on one. For Vitest/Jest/Mocha, create the
desktop in a before hook and dispose it in after, as the example test
init wrote does (npx vitest run or node --test tests/example.test.ts).
4. Where to go next
| Topic | Page |
|---|---|
The configuration file and spart-desk init | configuration.md |
| Finding controls | locators.md |
| Acting on them, input strategies, the foreground rule | actions.md |
| Waiting, timeouts, cancellation | auto-waiting.md |
| Assertions | assertions.md |
| What went wrong: records, traces, screenshots | assertions-diagnostics.md |
| Windows Sandbox runtime (experimental) | runtime-windows-sandbox.md |
| Resolving what UIA cannot see | resolution.md |
| AI agents over MCP | agentic-mcp.md |
| Installation, offline, mirrors | distribution.md, ci.md |
| Running tests in parallel (workers, runner projects) | parallelism.md |
| Something does not work | troubleshooting.md |
| Complete reference | api.md |
5. Testing your own automation code without Windows
import { Desktop } from "@spartaficial/spart-desk";
import { FakeDriver, FakeRuntime } from "@spartaficial/spart-desk/testing";
const driver = new FakeDriver([{ role: "window", name: "Demo", children: [{ role: "button", name: "OK" }] }]);
const desktop = await Desktop.create({ runtime: new FakeRuntime(driver) });
const app = await desktop.launch({ executable: "demo.exe" });
const main = app.window();
await main.getByRole("button", { name: "OK" }).click();
console.log(driver.performed); // [{ action: { type: "click", ... }, controlId: "0.0" }]
The fake backend runs the same locator, waiting and recording code as the real one, so page objects and helpers can be unit-tested on any machine.