Coded UI automation, without Coded UI Test

Microsoft deprecated Coded UI Test in Visual Studio 2019 and never shipped a first-party replacement. Your old UIMap.uitest files still run, sort of, and the Microsoft.VisualStudio.TestTools.UITesting namespace is on life support. This page is how to write coded UI automation today, against the same Windows AutomationId your old project used, in ten lines.

M
Matthew Diakonov
8 min read
4.9from developers shipping desktop automation
Playwright-style >> chain for Windows UIA and macOS AX
Reads the same AutomationId CUIT already tagged in your app
Ten-line Calculator example in examples/win_calculator.py
Full prefix table in docs/SELECTORS_CHEATSHEET.md
MCP server for Claude Code, Cursor, VS Code, Windsurf

What actually happened to Coded UI Test

Coded UI Test shipped with Visual Studio 2010 as Microsoft's answer to writing automated UI tests in C# against WPF, WinForms, Win32, and MFC applications. It was built on top of Microsoft UI Automation (UIA), which is the accessibility API Windows still ships today, and it exposed that API through the Microsoft.VisualStudio.TestTools.UITesting namespace. You would drop a UIMap.uitest file into your test project, open the Coded UI Test Builder, point-and-click at controls, and Visual Studio would generate a UIMap.Designer.cs file with a typed class per control.

Microsoft officially deprecated the framework in Visual Studio 2019 and did not bring it forward as a new project template in Visual Studio 2022. The runtime still loads for legacy projects, but the Test Tools installer workload no longer offers Coded UI as a first choice. The Coded UI Test Builder has not received a meaningful update in years. Meanwhile, the underlying accessibility API, UIA, is not going anywhere. The layer Microsoft abandoned is the high-level coded UI automation layer, not the plumbing underneath.

The rest of this guide is what to do about it. The short version is that you keep every AutomationId you ever tagged, you throw away the UIMap files, you replace the runner with something that parses selectors as an expression, and you gain cross-platform support, Playwright-shaped chains, and an MCP server for your coding assistant in the bargain.

Side by side: the Calculator test, then and now

Both of these add 1 + 2 in the Windows Calculator, locate the result display by its AutomationId (which is CalculatorResults, and has been for years), and assert the answer. The left side is the smallest coherent slice of a real Coded UI Test project, generated UIMap class included. The right side is the full contents of examples/win_calculator.py condensed to the same happy path.

1 + 2 = 3 in Calculator

// CUIT: UIMap.Designer.cs (auto-generated)
public class CalculatorResultsControl : WinText
{
    public CalculatorResultsControl(UITestControl searchLimitContainer)
        : base(searchLimitContainer)
    {
        this.SearchProperties[UITestControl.PropertyNames
            .AutomationId] = "CalculatorResults";
        this.WindowTitles.Add("Calculator");
    }
}

// CUIT: CodedUITest1.cs
[CodedUITest]
public class CalculatorTest
{
    [TestMethod]
    public void AddOnePlusTwo()
    {
        var ui = new UIMap();
        Mouse.Click(ui.OneButton);
        Mouse.Click(ui.PlusButton);
        Mouse.Click(ui.TwoButton);
        Mouse.Click(ui.EqualsButton);
        Assert.AreEqual("Display is 3",
            ui.CalculatorResultsControl.DisplayText);
    }
}
36% fewer lines

The AutomationId is the same string in both snippets. That is the whole point. The work you did years ago to make your WPF or WinForms app accessible is the work a modern runner uses to drive it. You are changing the test harness, not the application.

The anchor fact: one file maps every selector to Playwright

The entire selector grammar is documented in one place: docs/SELECTORS_CHEATSHEET.md. It lists fourteen atomic prefixes (role:, name:, id:, nativeid:, classname:, text:, pos:x,y, visible:, rightof:, leftof:, above:, below:, near:, nth:) plus the >> chain combinator, the && compound operator, and the .. parent navigator. The table in that file has an explicit "Rough Playwright equivalent" column for every one of them. A Playwright user reading down that table learns the desktop grammar in about two minutes.

FeatureCoded UI TestTerminator
Match by AutomationIdSearchProperties[UITestControl.PropertyNames.AutomationId] = "CalculatorResults"nativeid:CalculatorResults
Match by role and label togetherWinButton with SearchProperties for ControlType plus Name, declared in UIMap.uitestrole:Button && name:Save
Descendant traversalNested UITestControl containers with manual SearchLimitContainer wiringwindow:Calculator >> role:Button >> name:Seven
Spatial proximityNot supported; hand-rolled coordinate math or recorder quirks onlyrightof:name:Username, below:name:OK, near:text:Cancel
Localized label fallbacksMultiple UIMap entries, one per locale, each regenerated by handrole:Button && (name:Confirm || name:Bestaetigen)
Invisible-element filteringPost-hoc WaitForControlCondition with a custom predicaterole:Button && !visible:false
Parent navigationWalk via Container until you reach the right ancestor..
Editor source of truthUIMap.uitest XML plus UIMap.Designer.cs, regenerated by a toolPlain string in your test file, diffable in Git

Source: docs/SELECTORS_CHEATSHEET.md in the Terminator repository, the SearchProperties and UIMap patterns in the archived Microsoft.VisualStudio.TestTools.UITesting docs.

How a selector becomes an action

The flow is the same whether you are reading an AutomationId from a Win32 dialog, a WPF button, or a native macOS control. A selector string is parsed into a predicate, the predicate walks the OS accessibility tree, the match resolves to an element handle, the handle invokes an action. No screenshot matching on the hot path. No flakey pixel diff.

Selector to action, without pixels

nativeid:CalculatorResults
role:Button && name:Save
window:Calc >> role:Button >> name:Seven
Terminator selector engine
element.click()
element.type_text(...)
element.name()

The full working example

This is the condensed version of examples/win_calculator.py. Open Calculator, locate five controls by their accessible names, click them in order, read the result display through its AutomationId, assert. It is ten lines of real work and it is the same thing a Coded UI Test project does with a UIMap file, a designer class, and an MSTest attribute.

examples/win_calculator.py

Install and wire it into your agent

Pick the language your existing tests already live in. The Rust core is the same under all three SDKs. The last command registers the MCP server so an AI coding assistant can drive the same selectors at the tool-call level.

install terminator

What the deprecation actually cost you, and what you get back

A concrete inventory of what leaves the building when you move off Coded UI Test, and what arrives in its place. Nothing here is aspirational; every row corresponds to code in the Terminator repository.

Visual Studio 2022 support

Coded UI Test was deprecated in Visual Studio 2019 and removed from the Test Tools workload. New projects cannot target it.

UIMap.uitest file

The XML map that every CUIT project generated is gone. With Terminator the selector is the locator string itself.

Microsoft.VisualStudio.TestTools.UITesting

The namespace CUIT projects import is unsupported. Runtime binaries still ship for legacy projects, but no one is fixing the bugs.

Windows-only lock-in

CUIT ran on Windows through MSAA and UIA. Terminator's core is Rust, talks Windows UIA and macOS AX, and the selector grammar is identical on both.

Hand-written test bodies

Terminator ships an MCP server. Any coding assistant with MCP (Claude Code, Cursor, Windsurf, VS Code) can open an app, write selectors, and assert output without you typing the test.

Record-only workflows

The workflow recorder captures mouse, keyboard, and UI element metadata together, then replays as a deterministic script you can edit. No more CUIT Recorder crashes.

What it looks like in numbers

0
atomic selector prefixes
all documented in one cheat sheet
0
lines to drive Calculator
examples/win_calculator.py
0+
desktop platforms supported
Windows UIA and macOS AX
0
MCP clients out of the box
Claude Code, Cursor, VS Code, Windsurf

Migrating off Coded UI Test? Let us read your UIMap.

Thirty minutes with the Terminator team: we open your old UIMap.uitest, point each SearchProperties entry at its Terminator selector, and hand you back a working script.

Frequently asked questions

Frequently asked questions

What is coded UI automation and is Microsoft's Coded UI Test still supported?

Coded UI automation means writing test code that drives a desktop application through its accessibility tree, instead of by pixel coordinates. In Microsoft land, the canonical framework was Coded UI Test, exposed through the Microsoft.VisualStudio.TestTools.UITesting namespace. Microsoft deprecated Coded UI Test with the release of Visual Studio 2019 and did not bring it forward as a first-class offering in Visual Studio 2022. Existing legacy projects still run, but the product is in maintenance only, and the Test Tools installer workload no longer offers Coded UI as a new project template. The accessibility APIs Coded UI Test used, specifically Windows UI Automation (UIA), are alive and well; it is the CUIT layer on top of them that was abandoned.

Can Terminator use the same AutomationId I already tagged in WPF or WinForms for my old Coded UI Test project?

Yes. If your WPF or WinForms controls set AutomationProperties.AutomationId, Terminator finds them with the nativeid prefix. examples/win_calculator.py in the Terminator repo uses calculator_window.locator("nativeid:CalculatorResults").first() to grab the same CalculatorResults element that a CUIT-era UIMap.Designer.cs class would have reached through SearchProperties[UITestControl.PropertyNames.AutomationId] = "CalculatorResults". No code change on the application side. The work you already did to make your app accessible carries forward.

How does Terminator's selector syntax map to Playwright?

Every prefix in docs/SELECTORS_CHEATSHEET.md has an explicit Playwright equivalent column in the cheat sheet. role:Button maps to role=button, name:Save maps to text=Save or aria/Save, id:submit maps to css=#submit, classname:Edit maps to css=.Edit, visible:true maps to the :visible pseudo-class, and the chain combinator >> is the same >> that Playwright uses for descendant selectors. The one difference is that the spatial family (rightof, leftof, above, below, near) are first-class prefixes, not second-class helpers, and they compose with boolean operators like any other predicate.

How do I actually drive Calculator from code without Visual Studio?

Install terminator-py (pip install terminator-py) or @mediar-ai/terminator (npm i @mediar-ai/terminator) and run ten lines. The full script is in examples/win_calculator.py in the repo: create a Desktop, open calc.exe with desktop.open_application, grab the CalculatorResults display through nativeid, click Name:One, Name:Plus, Name:Two, Name:Equals, then read the display element's accessible name and assert it contains 3. It finishes in under two seconds on a cold Calculator launch. No MSTest attribute, no generated XML, no Visual Studio project.

Is Terminator just Coded UI Test rebranded, or is the underlying approach different?

Different in three ways. First, selectors are parsed as a boolean expression (AND, OR, NOT, parentheses) instead of being a flat SearchProperties dictionary, so one locator can match a localized or conditionally visible control. Second, the runtime is Rust, and the Node.js and Python SDKs are native NAPI-RS and PyO3 bindings, so the hot path is accessibility calls in compiled code, not interpreter overhead. Third, Terminator ships an MCP server (terminator-mcp-agent on npm) so an AI coding assistant can drive desktop apps directly; Coded UI Test predates LLM agents and has no story for them.

Does Terminator work on macOS, or is this Windows-only like CUIT was?

The core Rust crate at crates/terminator targets Windows UI Automation and macOS Accessibility simultaneously, and the selector grammar is the same on both platforms. The same window:Calculator >> role:Button >> name:Seven chain parses and evaluates whether the accessibility backend is UIA or AX. Linux support lives in the open issues. This is the single biggest architectural difference from Coded UI Test, which only ever ran on Windows.

What about the old CUIT Recorder? Is there an equivalent?

crates/terminator-workflow-recorder records mouse, keyboard, clipboard, hotkey, and UI focus events together with the UI element metadata (role, name, bounds) at the moment of each event, then emits a timestamped JSON workflow you can edit, commit, and replay deterministically. The README shows the event envelope, including ui_element fields with role and name. The recorder also filters system noise like clock updates so the output is a readable diff, not a dump.

terminatorDesktop automation SDK
© 2026 terminator. All rights reserved.