TomLabs.UiAutomation 0.1.1

dotnet add package TomLabs.UiAutomation --version 0.1.1
                    
NuGet\Install-Package TomLabs.UiAutomation -Version 0.1.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TomLabs.UiAutomation" Version="0.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TomLabs.UiAutomation" Version="0.1.1" />
                    
Directory.Packages.props
<PackageReference Include="TomLabs.UiAutomation" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TomLabs.UiAutomation --version 0.1.1
                    
#r "nuget: TomLabs.UiAutomation, 0.1.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TomLabs.UiAutomation@0.1.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TomLabs.UiAutomation&version=0.1.1
                    
Install as a Cake Addin
#tool nuget:?package=TomLabs.UiAutomation&version=0.1.1
                    
Install as a Cake Tool

TomLabs.UiAutomation

Build NuGet TomLabs.UiAutomation NuGet tomlabs-ui Downloads License: MIT

Drive and screenshot an Avalonia desktop app from a script — or from an AI coding session — without touching the desktop. No SendKeys, no cursor moves, no screen capture: the app itself answers on a loopback HTTP port, renders its window through RenderTargetBitmap, dumps its visual tree and injects clicks, key gestures and text straight into the Avalonia window through the same raw-input path the platform backends use. With --headless the same app runs on Avalonia's headless platform with the Skia renderer, so nothing appears on screen at all and the screenshots are still pixel-identical to the real window.

Two packages:

  • TomLabs.UiAutomation — the in-app server and the --headless switch. Reference it from the app.
  • TomLabs.UiAutomation.Tool — the tomlabs-ui dotnet tool that builds, starts, calls and stops the app.

Wire it into the app

// Program.cs
public static AppBuilder BuildAvaloniaApp(string[] args) =>
    AppBuilder.Configure<App>()
        .UsePlatformDetectOrHeadless(args)   // UsePlatformDetect(), or the headless platform with --headless
        .LogToTrace();

// App.axaml.cs, OnFrameworkInitializationCompleted
var automation = UiAutomationServer.StartIfEnabled(desktop, "MyApp", AppInfo.Version, message => log.Information(message));
automation?.Actions["page"] = (window, arg) => { viewModel.Page = arg; return new { page = arg }; };   // optional shortcuts
desktop.Exit += (_, _) => automation?.Dispose();

The server starts only when the environment variable MYAPP_AUTOMATION=<port> (the app name upper-cased) is set, and listens on 127.0.0.1 only. Nothing changes for normal users.

Drive it

dotnet tool install -g TomLabs.UiAutomation.Tool

tomlabs-ui start                          # build Release into ./.ui-build, start MyApp --headless with
                                          # MYAPP_AUTOMATION=47831 and MYAPP_DATA_DIR = a fresh copy of tests/ui-fixture
tomlabs-ui call "click?text=Settings"     # any endpoint; the JSON answer is printed
tomlabs-ui call "key?gesture=Ctrl%2BK"
tomlabs-ui shot out/settings.png          # PNG of the main window (--window top for the newest dialog)
tomlabs-ui tree "depth=8"                 # visual tree: Type #Name .classes [x,y wxh] "text"
tomlabs-ui stop

start --visible opens a real window (real DPI, native popups) — input still goes straight into the Avalonia window, never through the desktop. --data-dir, --fixture, --env NAME=VALUE, --project, --port and --no-build cover the rest; tomlabs-ui --help lists everything. curl http://127.0.0.1:47831/<endpoint> works just as well once the app runs.

Convention the tool relies on: the app reads its data folder from <APPNAME>_DATA_DIR (so a fixture can replace the live data), and tests/ui-fixture/ in the repository holds a small synthetic data set. Add .ui-build/ to .gitignore.

Endpoints

GET or POST, query-string arguments; JSON answers except /tree (text) and /screenshot (PNG). Errors are {"error": "..."} with 400 (bad argument, nothing found) or 500.

Endpoint What it does
/ name, version, platform (Avalonia.Headless / Avalonia.Win32), theme, open windows
/screenshot?window=&scale=&path= PNG of the window (top = newest dialog, an index or a title); path also saves it (fully qualified)
/tree?window=&name=&depth=&all=1 visual tree; name narrows to a named control, all=1 includes hidden nodes
/find?text=&name=&type= visible controls matching text (contains, exact first), Name or type, with bounds
/click?text=\|name=\|x=&y=&button=right&count=2 pointer press + release at the control's centre (or at x,y)
/key?gesture=Ctrl+Shift+K key down + up on the focused element (%2B for + in a URL; spaces work too)
/type?text= text input into the focused element
/get?path=Selected.Name read a property path on the window's DataContext
/set?path=Search&value=x write a DataContext property (or name=<Control>&property=Text&value=)
/invoke?command=SaveCommand&parameter= execute an ICommand of the DataContext
/do/<action>?arg= app-specific action registered in Actions
/theme?variant=dark\|light\|default switch the theme variant
/resize?width=&height= resize the window
/wait?ms= let timers, animations and layout settle
/quit shut the application down

Input-like commands wait ~150 ms (SettleMilliseconds) and force a layout pass before answering, so a screenshot taken right after them is settled. Windows other than the main one are addressed with window=.

In unit tests

The server also runs inside Avalonia.Headless.XUnit tests: new UiAutomationServer(UiAutomationHost.ForWindow(window), port, "app", "1.0") — see tests/ for examples. It forces a headless render tick before hit testing, so clicks work without a running main loop.

Publishing

Tags v<version> publish both packages to nuget.org through Trusted Publishing: no API key is stored anywhere. One-time setup: on nuget.org → Trusted Publishing, add a policy for repository owner TomasBouda, repository TomLabs.UiAutomation, workflow file build.yml. Then bump VersionPrefix in Directory.Build.props, add the changelog section and push the tag.

Notes

  • The library is built with AvaloniaAccessUnstablePrivateApis: raw input is public in Avalonia's implementation assemblies but hidden in the reference assemblies. Consumers do not need the switch, but Avalonia requires such a package to pin the exact Avalonia version ([11.3.18]), so the app must reference the same one; a new Avalonia version means a new package release.
  • Popups that are separate top-levels (context menus, tooltips) are not part of the main window's screenshot.
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.1 42 9/18/2026
0.1.0 38 9/18/2026