packages/cua-driver/test-harness/README.md
Deterministic host apps that present a fixed scenario set for cua-driver
to drive. One layout, three buckets: cross-OS reusable assets in
shared/, OS-specific (or cross-platform) target apps in apps/,
host-OS build scripts in build/, host-OS CLI smoke runners in
smoke/.
test-harness/
├── shared/ # cross-OS reusable
│ ├── scenarios.json # single SoT for AutomationIds / window titles
│ └── web/index.html # WebView2 + Electron load this same file
├── apps/ # the test-target host apps
│ ├── cross-platform/
│ │ └── electron/ # CuaTestHarness.Electron (CDP host, runs on any OS)
│ ├── macos/
│ │ ├── appkit/ # CuaTestHarness.AppKit (single-file Swift)
│ │ └── swiftui/ # CuaTestHarness.SwiftUI (single-file Swift)
│ └── windows/
│ ├── wpf/ # CuaTestHarness.Wpf (.NET 8 WPF)
│ ├── winui3/ # CuaTestHarness.WinUI3 (.NET 8 WinUI3 unpackaged)
│ └── webview2/ # CuaTestHarness.WebView (WPF + Microsoft.Edge.WebView2)
├── build/ # host-OS build scripts (publish into ../rust/test-apps/)
│ ├── macos.sh # builds appkit + swiftui
│ └── windows.ps1 # builds wpf + winui3 + webview2 + electron
└── smoke/ # host-OS per-tool CLI smoke runners
├── macos.sh # spawns appkit harness, iterates every tool
└── results/macos.txt # checked-in baseline for diff-based regression detection
shared/scenarios.json is the single source of truth — both the host
apps and the Rust integration tests read it so AutomationIds, AX
identifiers, and window titles never drift between the two halves.
Published build outputs land in ../rust/test-apps/harness-{wpf,winui3,webview,electron,appkit,swiftui}/
so the existing sandbox runner and Rust integration tests pick them up
without further wiring.
WPF (apps/windows/wpf):
counter, text_body)text_input)click_target)scroll_target)MessageBox (message_box)bottom_strip)HwndHost (child_hwnd)Window (owned_popup)layered_popup)accelerator)WinUI3 (apps/windows/winui3):
counter / text_body / exit (parity with WPF)text_input — UIA ValuePattern)CommandBarFlyout — popup in same HWND, DComp-renderedPopup — primitive, not a separate HWNDWebView2 (apps/windows/webview2):
shared/web/index.html — same page as the Electron harness.default_cdp_port=9222 so cua-driver's page tool can drive it.Electron (apps/cross-platform/electron):
shared/web/index.html. CDP on default_cdp_port=9223.AppKit (apps/macos/appkit):
counter)text_body)text_input)click_target)scroll_target)ns_menubar)SwiftUI (apps/macos/swiftui):
.popover() — Mac analogue of WinUI3 CommandBarFlyout / XAML PopupWindows:
| Capability | WPF | WinUI3 |
|---|---|---|
| Smoke (UIA tree + AutomationIds) | yes | yes |
| UIA Invoke click | yes | (implicit) |
right_click | yes | — |
double_click | yes | — |
type_text (PostMessage / UIA) | yes | yes |
set_value (UIA ValuePattern) | yes | (covered) |
scroll (WM_VSCROLL via hook) | yes | — |
press_key accelerator (F5) | yes | — |
Modal MessageBox open + dismiss | yes | — |
| Owned popup open + parse | yes | — |
| Layered popup capture | yes | — |
| XAML Popup open + parse | — | yes |
macOS:
| Capability | AppKit | SwiftUI |
|---|---|---|
| Smoke (AX tree + identifiers) | yes | yes |
| Counter click via element_index | yes | (implicit) |
set_value (AXSetAttribute) | yes | (covered) |
| Popover open + body assert | — | yes |
| Per-tool CLI smoke | smoke/macos.sh | — |
macOS (requires Xcode CLT for xcrun swiftc):
libs/cua-driver/test-harness/build/macos.sh # both apps
libs/cua-driver/test-harness/build/macos.sh --skip swiftui # AppKit only
libs/cua-driver/test-harness/build/macos.sh --clean # nuke stage dirs first
Windows (requires .NET 8 SDK on PATH; Node.js + npm for the Electron app):
cd libs\cua-driver\test-harness
.\build\windows.ps1 # all four apps
.\build\windows.ps1 -Skip winui3 # skip a specific one
# macOS
cd libs/cua-driver/rust
cargo test --release --test harness_appkit_test -- --ignored --nocapture
cargo test --release --test harness_swiftui_test -- --ignored --nocapture
# Windows (locally)
cd libs\cua-driver\rust
cargo test --test harness_wpf_test -- --ignored --nocapture
cargo test --test harness_winui3_test -- --ignored --nocapture
# Windows Sandbox (matches CI)
.\rust\sandbox\run-tests-in-sandbox.ps1 harness_wpf
.\rust\sandbox\run-tests-in-sandbox.ps1 harness_winui3
Tests are #[ignore] so they don't run in plain cargo test (they
require the harness apps to be built and, on macOS, TCC Accessibility
granted to the test runner).
Companion to the MCP integration tests. Spawns the AppKit harness as a victim, then iterates every cua-driver tool with sensible JSON args and reports PASS / FAIL / SKIP in a sorted table. Runs in ~25 seconds.
libs/cua-driver/test-harness/smoke/macos.sh
A baseline result file (smoke/results/macos.txt) is checked in for the
current driver version — diff against it to catch regressions in tool
coverage.
accessibilityIdentifier on AXStaticText doesn't propagate.
NSTextField label-mode + SwiftUI Text views render as AXStaticText
leaves and the OS drops the identifier slot. Tests assert AX-id only
on actionable controls and assert on text-content for labels.list_windows for any new window owned by the harness pid if
the trigger window doesn't contain the marker.