Add MSTest.Windows.UIAutomation for desktop UI tests - #10862
Add MSTest.Windows.UIAutomation for desktop UI tests#10862Amaury Levé (Evangelink) wants to merge 23 commits into
Conversation
Revive the MSTest.Windows.AppTesting work from PR #7810 on current main, including SDK integration, package metadata, a deterministic sample, and MTP/VSTest acceptance coverage. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 006cf5a8-6525-4ecb-829f-e83a99c4de05
🧪 Expert test review — PR #10862
Note: Mutation analysis reported N/A for all four tests — the acceptance tests spawn out-of-process test hosts against a Windows-only physical UI (Character Map), and the sample tests are documentation demos, so the production entry points (native app automation) could not be resolved/exercised for pseudo-mutation in this review. All four tests are gated correctly ( This advisory comment was generated automatically. Grades are heuristic and informational — they do not block merging. Suggestions on the Files changed tab can be applied with one click. Re-run with
|
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Note
🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.
Review Summary — MSTest.Windows.AppTesting
Clean PR that adds a new MSTest.Windows.AppTesting package with application lifecycle and Windows UI Automation base classes, wired through MSTest.Sdk. Two inline comments posted; neither is blocking.
| # | Dimension | Verdict |
|---|---|---|
| 1 | Algorithmic Correctness | ✅ OK — startup loop handles edge cases (process exit, timeout) correctly |
| 2 | Threading & Concurrency | AutomationTest uses [TestClass] (MTA) while subclasses use [STATestClass]; see inline comment |
| 3 | Security & IPC | ✅ N/A |
| 4 | Public API & Binary Compat | ✅ OK — all public API in PublicAPI.Unshipped.txt, no init accessors, types unsealed by design (base-class hierarchy), XML docs present |
| 5 | Performance & Allocations | ✅ OK — not a hot path |
| 6 | Cross-TFM Compatibility | ✅ OK — targets net8.0-windows / net9.0-windows only, validated in .targets |
| 7 | Resource & IDisposable | ✅ OK — Process disposed in finally; CloseMainWindow → Kill cascade is correct |
| 8 | Defensive Coding | ✅ OK — boundary checks for process exit, null handle, and timeout present |
| 9 | Naming & Code Style | ✅ OK |
| 10 | Logging & Observability | ✅ N/A |
| 11 | Error Messages & UX | ✅ OK — clear exception messages with variable names |
| 12 | Tests & Test Quality | ✅ OK — acceptance tests cover MTP and VSTest, OS-gated |
| 13 | Documentation | ✅ OK — PACKAGE.md present, XML docs thorough |
| 14 | Localization | ✅ N/A — no user-facing resx strings |
| 15 | Backward Compatibility | ✅ N/A — new package |
| 16 | Build & Pack Integration | ✅ OK — verify-nupkgs.ps1 updated, MSBuild integration mirrors Aspire/Playwright patterns |
| 17 | Serialization | ✅ N/A |
| 18 | Tracing & Telemetry | ✅ N/A |
| 19 | Nullability | ✅ OK — null! used for lifecycle-initialized properties, consistent with rest of codebase |
| 20 | TODOs & Leftovers | ✅ None found |
| 21 | Scope Discipline | ✅ Single concern |
| 22 | Script & Tooling Hygiene | ✅ OK — PowerShell hash table update is correct |
Overall: Looks good. The two inline comments are non-blocking suggestions for future hardening.
There was a problem hiding this comment.
Copilot review overview
Review tier: Balanced
Findings: 2
New issues introduced by this change (4)
| Severity | Finding |
|---|---|
samples/public/DemoMSTestSdk/ProjectUsingWindowsAppTesting/ProjectUsingWindowsAppTesting.csproj — This project is not listed in samples/public/DemoMSTestSdk/DemoMSTestSdk.sln. Because… |
|
samples/public/DemoMSTestSdk/ProjectUsingWindowsAppTesting/ProjectUsingWindowsAppTesting.csproj — The non-SDK example cannot restore as written: MSTestWindowsAppTestingVersion is only defined by… |
|
src/Package/MSTest.Sdk/Sdk/Features/WindowsAppTesting.targets — No test exercises this validation path: every generated acceptance TFM appends -windows. Add a… |
|
src/TestFramework/TestFramework.Windows.AppTesting/ApplicationTest.cs — The acceptance tests exercise only a responsive Character Map process. Add coverage for an app that… |
What changed in this PR
Adds Windows desktop UI automation support to MSTest through a new package, SDK integration, sample, and acceptance coverage.
Changes:
- Adds application lifecycle and UI Automation base classes.
- Integrates the package into MSTest.Sdk for MTP and VSTest.
- Adds Character Map samples, acceptance tests, and package verification.
| File | Description |
|---|---|
TestFx.slnx |
Includes the new package project. |
WindowsAppTestingSdkTests.cs |
Tests MTP and VSTest integration. |
WindowTest.cs |
Exposes the main UI Automation window. |
TestFramework.Windows.AppTesting.csproj |
Defines the Windows package. |
PublicAPI.Unshipped.txt |
Tracks new public APIs. |
PublicAPI.Shipped.txt |
Initializes the shipped baseline. |
PACKAGE.md |
Documents package usage. |
GlobalUsings.cs |
Imports MSTest APIs globally. |
AutomationTest.cs |
Adds the hierarchy root. |
ApplicationTest.cs |
Manages application lifecycle. |
VSTest.targets |
Enables the feature under VSTest. |
Sdk.props.template |
Defines feature and version properties. |
NativeAOT.targets |
Rejects unsupported NativeAOT usage. |
ClassicEngine.targets |
Enables the feature under MTP. |
WindowsAppTesting.targets |
Adds validation, package reference, and using. |
MSTest.Sdk.csproj |
Packs and versions the feature target. |
ProjectUsingWindowsAppTesting.csproj |
Adds the SDK sample configuration. |
CharacterMapTests.cs |
Demonstrates desktop UI tests. |
verify-nupkgs.ps1 |
Verifies package contents. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Run the base hierarchy on STA threads, honor cooperative cancellation during app startup, include the package in MSTest.slnf, and make package verification detect missing outputs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 006cf5a8-6525-4ecb-829f-e83a99c4de05
This comment has been minimized.
This comment has been minimized.
🧪 Expert test review — PR #10862Reviewed 4 new test methods introduced by this PR across the new
This advisory comment was generated automatically. Grades are heuristic
|
There was a problem hiding this comment.
Copilot review overview
Review tier: Balanced
Findings: 3
New issues introduced by this change (2)
| Severity | Finding |
|---|---|
test/IntegrationTests/MSTest.Acceptance.IntegrationTests/WindowsAppTestingSdkTests.cs — This VSTest leg omits the roll-forward setting used by the MTP leg above. The repo-local toolchain… |
|
src/Package/MSTest.Sdk/Sdk/Sdk.props.template — These new user-facing SDK properties are missing from the SDK documentation tables.… |
Pre-existing issues (4)
| Severity | Finding |
|---|---|
samples/public/DemoMSTestSdk/ProjectUsingWindowsAppTesting/ProjectUsingWindowsAppTesting.csproj — The non-SDK example cannot restore as written: MSTestWindowsAppTestingVersion is only defined by… View comment |
|
samples/public/DemoMSTestSdk/ProjectUsingWindowsAppTesting/ProjectUsingWindowsAppTesting.csproj — This project is not listed in samples/public/DemoMSTestSdk/DemoMSTestSdk.sln. Because… View comment |
|
src/TestFramework/TestFramework.Windows.AppTesting/ApplicationTest.cs — The acceptance tests exercise only a responsive Character Map process. Add coverage for an app that… View comment |
|
src/Package/MSTest.Sdk/Sdk/Features/WindowsAppTesting.targets — No test exercises this validation path: every generated acceptance TFM appends -windows. Add a… View comment |
Suppressed comments (3)
samples/public/DemoMSTestSdk/ProjectUsingWindowsAppTesting/ProjectUsingWindowsAppTesting.csproj:1
- This public sample is not included in
samples/public/DemoMSTestSdk/DemoMSTestSdk.sln.eng/build-samples.ps1:60-64builds discovered solutions rather than standalone projects, so the new sample is currently omitted from sample CI and can silently break. Add the project and its Debug/Release configurations to that solution.
<Project Sdk="MSTest.Sdk">
src/Package/MSTest.Sdk/Sdk/Features/WindowsAppTesting.targets:11
- The added acceptance asset always targets
*-windows, and no other test exercises this validation message, so removing or breaking this target would leave all current tests green. Add a non-Windows-target build case that enables the feature and asserts this diagnostic.
<Target Name="_ValidateWindowsAppTestingPlatform" BeforeTargets="Restore;Build"
Condition=" '$(TargetFramework)' != '' and '$(_IsWindowsTfm)' != 'true' ">
<Error Text="MSTest.Windows.AppTesting requires a Windows target framework (e.g. net8.0-windows). Current TargetFramework: '$(TargetFramework)'." />
src/TestFramework/TestFramework.Windows.AppTesting/ApplicationTest.cs:124
- The Character Map acceptance path closes normally and does not assert that cleanup terminated the process, so this forced-kill branch is untested; a regression here can leave desktop processes running after the suite. Add a test application that ignores
CloseMainWindowand verify that cleanup terminates its process tree.
_ = appProcess.CloseMainWindow();
if (!appProcess.WaitForExit(5000))
{
appProcess.Kill(entireProcessTree: true);
_ = appProcess.WaitForExit(5000);
Cover invalid target frameworks, early process exit, startup timeout, and forced cleanup while correcting the standalone sample package version. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 006cf5a8-6525-4ecb-829f-e83a99c4de05
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Copilot review overview
Review tier: Balanced
Findings: 1
New issues introduced by this change (1)
| Severity | Finding |
|---|---|
src/Package/MSTest.Sdk/Sdk/Features/WindowsAppTesting.targets — This check is case-sensitive, although the SDK accepts target framework monikers… |
Pre-existing issues (2)
| Severity | Finding |
|---|---|
test/IntegrationTests/MSTest.Acceptance.IntegrationTests/WindowsAppTestingSdkTests.cs — This VSTest leg omits the roll-forward setting used by the MTP leg above. The repo-local toolchain… View comment |
|
src/Package/MSTest.Sdk/Sdk/Sdk.props.template — These new user-facing SDK properties are missing from the SDK documentation tables.… View comment |
Issues resolved since last review (4)
| Severity | Finding |
|---|---|
src/TestFramework/TestFramework.Windows.AppTesting/ApplicationTest.cs — The acceptance tests exercise only a responsive Character Map process. Add coverage for an app that… View resolved comment |
|
src/Package/MSTest.Sdk/Sdk/Features/WindowsAppTesting.targets — No test exercises this validation path: every generated acceptance TFM appends -windows. Add a… View resolved comment |
|
samples/public/DemoMSTestSdk/ProjectUsingWindowsAppTesting/ProjectUsingWindowsAppTesting.csproj — The non-SDK example cannot restore as written: MSTestWindowsAppTestingVersion is only defined by… View resolved comment |
|
samples/public/DemoMSTestSdk/ProjectUsingWindowsAppTesting/ProjectUsingWindowsAppTesting.csproj — This project is not listed in samples/public/DemoMSTestSdk/DemoMSTestSdk.sln. Because… View resolved comment |
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
src/TestFramework/TestFramework.Windows.AppTesting/PACKAGE.md:12
- This standalone getting-started snippet does not pin
MSTest.Sdk, and the README does not provide aglobal.jsonpin, so users copying it from the NuGet package page cannot resolve the project SDK. Include an SDK version here, consistent withsrc/Package/MSTest.Sdk/PACKAGE.md:9-16.
<Project Sdk="MSTest.Sdk">
src/Package/MSTest.Sdk/Sdk/Sdk.props.template:45
- The repository's complete MSTest.Sdk property reference in
docs/glossary.md:312-343still lists only Aspire and Playwright and omits both new controls. Add anEnableWindowsAppTestingcompatibility row (including its NativeAOT error and VSTest support) and documentMSTestWindowsAppTestingVersion, otherwise the advertised complete reference is stale as soon as this feature ships.
<EnableWindowsAppTesting Condition=" '$(EnableWindowsAppTesting)' == '' ">false</EnableWindowsAppTesting>
<EnablePlaywright Condition=" '$(EnablePlaywright)' == '' ">false</EnablePlaywright>
<AspireHostingTestingVersion Condition=" '$(AspireHostingTestingVersion)' == '' ">${AspireHostingTestingVersion}</AspireHostingTestingVersion>
<MicrosoftNETTestSdkVersion Condition=" '$(MicrosoftNETTestSdkVersion)' == '' ">${MicrosoftNETTestSdkVersion}</MicrosoftNETTestSdkVersion>
<MSTestWindowsAppTestingVersion Condition=" '$(MSTestWindowsAppTestingVersion)' == '' ">${MSTestWindowsAppTestingVersion}</MSTestWindowsAppTestingVersion>
Apply desktop runtime roll-forward consistently to the VSTest acceptance leg and document the feature and version override properties. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 006cf5a8-6525-4ecb-829f-e83a99c4de05
🧪 Expert test review — PR #10862The pre-step's extract data (test-file/line-range TSV) was unavailable in this run, so this review was derived directly from the PR's file diffs. Two new test files were added: a sample demo (
Note on mutation column: production code for the new No inline suggestions were posted — findings above are minor reliability/coverage notes rather than concrete, safely-anchorable mechanical fixes. This advisory comment was generated automatically. Grades are heuristic and informational — they do not block merging. Re-run with
|
There was a problem hiding this comment.
Copilot review overview
Review tier: Balanced
Findings: 1
Pre-existing issues (1)
| Severity | Finding |
|---|---|
src/Package/MSTest.Sdk/Sdk/Features/WindowsAppTesting.targets — This check is case-sensitive, although the SDK accepts target framework monikers… View comment |
Issues resolved since last review (2)
| Severity | Finding |
|---|---|
src/Package/MSTest.Sdk/Sdk/Sdk.props.template — These new user-facing SDK properties are missing from the SDK documentation tables.… View resolved comment |
|
test/IntegrationTests/MSTest.Acceptance.IntegrationTests/WindowsAppTestingSdkTests.cs — This VSTest leg omits the roll-forward setting used by the MTP leg above. The repo-local toolchain… View resolved comment |
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
test/IntegrationTests/MSTest.Acceptance.IntegrationTests/WindowsAppTestingSdkTests.cs:150
- The generated asset imports
Microsoft.MSTest.Windows.AppTestingexplicitly, so these acceptance tests do not exercise the implicitUsingadded byWindowsAppTesting.targets. Since the public sample relies on that implicit import and is intentionally excluded from sample CI, removing this using is needed to make a regression in the SDK feature fail automated coverage.
using Microsoft.MSTest.Windows.AppTesting;
This comment has been minimized.
This comment has been minimized.
Use MSBuild's target-platform parser, cover uppercase target framework monikers, and pin the package README SDK example. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 006cf5a8-6525-4ecb-829f-e83a99c4de05
🧪 Expert test review — PR #10862
This advisory comment was generated automatically. Grades are heuristic
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
🧪 Expert test review — PR #10862
This advisory comment was generated automatically. Grades are heuristic
|
There was a problem hiding this comment.
🤖 Automated content by GitHub Copilot. Generated by the Test Reviewer on PR (on open / sync) workflow. · auto · 189.7 AIC · ⌖ 2.72 AIC · ⊞ 16.9K · ◷
Let custom discovery continue after the launcher exits, add launcher-child lifecycle coverage, and remove asynchronous PID publication races from cleanup tests. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 006cf5a8-6525-4ecb-829f-e83a99c4de05
There was a problem hiding this comment.
Copilot review overview
Review tier: Balanced
Findings: None
Issues resolved since last review (3)
| Severity | Finding |
|---|---|
test/IntegrationTests/MSTest.Acceptance.IntegrationTests/WindowsUIAutomationSdkTests.cs — This cleanup-failure case has the same PID race: the empty test proceeds directly to failing… View resolved comment |
|
test/IntegrationTests/MSTest.Acceptance.IntegrationTests/WindowsUIAutomationSdkTests.cs — The child writes its PID asynchronously, but this empty test can finish and enter StopApplication… View resolved comment |
|
src/TestFramework/TestFramework.Windows.UIAutomation/WindowTest.cs — FindWindow is documented as supporting launcher processes, but this unconditional check aborts… View resolved comment |
Suppressed comments (2)
test/IntegrationTests/MSTest.Acceptance.IntegrationTests/WindowsUIAutomationSdkTests.cs:479
- The cancellation timer can fire and cleanup can kill PowerShell before its asynchronous command creates the PID file. The outer test unconditionally reads that file, so slow scheduling turns this cleanup check into a
FileNotFoundException. WriteapplicationProcess.Idsynchronously before scheduling cancellation instead of relying on the child command.
"-NoProfile -NonInteractive -Command \"$PID | Set-Content -LiteralPath $env:MSTEST_UI_AUTOMATION_PID_FILE; Start-Sleep -Seconds 30\"");
test/IntegrationTests/MSTest.Acceptance.IntegrationTests/WindowsUIAutomationSdkTests.cs:417
- This PID is written asynchronously by the child, but cleanup starts after a fixed three-second discovery timeout and can terminate PowerShell before it ever executes
Set-Contenton a heavily loaded worker.AssertProcessExitedAsyncthen fails with a missing file instead of checking cleanup. RecordapplicationProcess.Idsynchronously fromFindWindowbefore polling, as the other cleanup cases do.
"-NoProfile -NonInteractive -Command \"$PID | Set-Content -LiteralPath $env:MSTEST_UI_AUTOMATION_PID_FILE; Start-Sleep -Seconds 30\"");
This comment has been minimized.
This comment has been minimized.
Rename Character Map title tests to reflect their locale-independent non-empty-title assertion. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 006cf5a8-6525-4ecb-829f-e83a99c4de05
Record timeout and cancellation process IDs synchronously during window discovery instead of relying on child PowerShell scheduling. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 006cf5a8-6525-4ecb-829f-e83a99c4de05
There was a problem hiding this comment.
Copilot review overview
Review tier: Balanced
Findings: None
Suppressed comments (3)
Previously missed (3) — in code that hasn't changed since the last review.
src/TestFramework/TestFramework.Windows.UIAutomation/ApplicationTest.cs:50
ApplicationShutdownTimeoutis first validated during disposal, after the process has been launched. If an override returns a negative value (or one aboveInt32.MaxValuemilliseconds), cleanup throws beforeCloseMainWindow/Kill;Disposethen clears and disposes theProcessobject while leaving the application running. Validate the timeout beforeProcess.Startso an invalid configuration cannot leak a process.
ProcessStartInfo startInfo = CreateProcessStartInfo();
src/TestFramework/TestFramework.Windows.UIAutomation/WindowTest.cs:88
AutomationElement.FromHandlealso throwsArgumentExceptionwhen the HWND is no longer valid. A window can be destroyed or replaced between readingMainWindowHandleand this call (especially during splash-screen transitions), so that transient race currently fails initialization instead of continuing discovery. TreatArgumentExceptionlikeElementNotAvailableExceptionand retry.
: AutomationElement.FromHandle(mainWindowHandle);
src/TestFramework/TestFramework.Windows.UIAutomation/ApplicationTest.cs:96
CloseMainWindow()returnsfalsewhen no close message was sent (for example, a windowless process or a disabled main window), but the code still waits the full shutdown timeout before killing. This adds the default five-second delay to cleanup paths where graceful shutdown was never requested. Use the return value to proceed directly toKillwhen it isfalse.
_ = applicationProcess.CloseMainWindow();
if (!applicationProcess.WaitForExit(shutdownTimeoutMilliseconds))
This comment has been minimized.
This comment has been minimized.
Validate shutdown timeout before launch, retry stale window handles, skip unnecessary graceful waits, and cover invalid configuration without process creation. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 006cf5a8-6525-4ecb-829f-e83a99c4de05
There was a problem hiding this comment.
Copilot review overview
Review tier: Balanced
Findings: None
Suppressed comments (2)
Previously missed (2) — in code that hasn't changed since the last review.
src/TestFramework/TestFramework.Windows.UIAutomation/WindowTest.cs:112
- An override returning
Timeout.InfiniteTimeSpanis treated as already expired here, and any other negative value produces a misleading application timeout instead of rejecting invalid configuration. Since this is a public customization point, validate the value once and either support infinite polling explicitly or reject it withArgumentOutOfRangeException, consistent withApplicationShutdownTimeout.
MainWindow = window;
return;
src/TestFramework/TestFramework.Windows.UIAutomation/TestFramework.Windows.UIAutomation.csproj:5
- This assignment overwrites an
EnableWindowsTargetingvalue supplied earlier (for example fromDirectory.Build.props) on every non-Windows evaluation. That breaks the PR's value-preservation contract and differs from the conditional fallback in the SDK target and sample; only default it when the property is unset.
<EnableWindowsTargeting Condition=" '$(OS)' != 'Windows_NT' ">true</EnableWindowsTargeting>
This comment has been minimized.
This comment has been minimized.
Support infinite window discovery, reject invalid negative values, preserve explicit Windows-targeting settings, and cover invalid configuration cleanup. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 006cf5a8-6525-4ecb-829f-e83a99c4de05
🧵 Parallel-safety audit — PR #10862Parallelization — one row per test assembly audited:
Findings: A (global-state) This PR's only changed test file is a brand-new class, Top actions:
Info
Advisory only — heuristic, non-blocking. Re-run with
|
🧪 Expert test review — PR #10862Reviewed the 15 new acceptance-test methods in
No high-confidence actionable inline findings were identified; assertions consistently match the exact wording and control flow of the production code in This advisory comment was generated automatically. Grades are heuristic and informational — they do not block merging. Re-run with
|
Extracts the UI Automation engine out of the CLI and ships it as a NuGet package the CLI consumes by `ProjectReference`, so the same engine is available to other projects — notably [`MSTest.Windows.UIAutomation`](microsoft/testfx#10862). CLI behavior is unchanged; `docs/cli-schema.json` is byte-identical. ## Packages | Package | Contents | Cost to a consumer | |---|---|---| | `Microsoft.Windows.SDK.BuildTools.WinApp.UIAutomation` | Element search, UIA pattern interaction, input injection, window capture | ~1.5 MB on `net10.0-windows` | | `Microsoft.Windows.SDK.BuildTools.WinApp.UIAutomation.Recording` | H.264 MP4 recording and JPEG frame bundles | adds SkiaSharp, ~9 MB native per architecture | Recording is separate so the many projects that only inspect and drive UI don't pull in SkiaSharp. The base package multi-targets: - `net10.0-windows` — screenshots via `PrintWindow` (GDI), no continuous frame capture, ~1.5 MB. - `net10.0-windows10.0.19041.0` — adds Windows Graphics Capture, which is what lets you screenshot an occluded or GPU-composited window; costs an extra ~24 MB of Windows SDK projection. Same public API either way. `IWindowCapture.IsFrameCaptureSupported` reports what you got at runtime. ## Public surface Only the supported contract is public: the service interfaces, the models they exchange, the exceptions they throw, and the DI extensions. The recording pipeline's internals — the frame sink, the encoder, the bundle writer, the artifact coordinator — are internal, and `PublicApiSurfaceTests` pins both exported type lists so nothing rejoins them by accident. A few things that look like implementation detail are public because they have to be. The CLI consumes these assemblies across an assembly boundary and cannot use `InternalsVisibleTo`: both run the CsWin32 generator, and sharing internals makes `Windows.Win32.PInvoke` ambiguous (CS0433). So the seams the CLI injects (`ISystemUiQuery`, `IPollDelay`, `IOwnedWindowFinder`) and the geometry helper the recording assembly calls stay public. Both packages ship XML documentation, and every public member is documented — Release treats warnings as errors, so CS1591 keeps it that way. The generated interop documentation is filtered out before packing: CsWin32 contributed roughly 2,000 of 2,400 entries describing types that are already internal, which would have nearly doubled the package. ## MSTest interop `MSTest.Windows.UIAutomation` is UIA2 (`System.Windows.Automation`); this library is UIA3 COM. The bridge is the window handle, so `UiTarget.FromWindowHandle(nint)` was added. Validated end to end against a real `charmap.exe` window driven through actual UIA2. ## Behavior parity Type renames for a public audience (`UiSessionInfo` → `UiTarget`, `SelectorExpression` → `UiSelector`, `IUiAutomationService` → `IUiAutomation`) with injected parameter names updated to match. User-visible message text and exit codes were diffed against `main` and are unchanged. ## Packaging and signing `package-nuget.ps1` builds all three packages and `build-cli.ps1` verifies the full set exists, so a partial set can't ship as a green build. The release pipeline packed the `.nupkg` files before any signing ran, so they embedded unsigned binaries even though the loose `winapp.exe` was signed. Signing now happens first and the packages are re-packed from the signed output (`-SkipBuild`, verified not to touch the assemblies), so the binaries inside are Authenticode-signed before the existing ESRP NuGet task signs the packages themselves. This fixes the CLI tools package the same way. ## Validation - `scripts/build-cli.ps1` end to end — all three packages produced, NuGet Pester 37/37, scripts Pester 52/52. - Release solution build: 0 warnings, 0 errors. 4258 CLI tests and 377 UI Automation tests, 0 failures. - Package-consumption tests install both packages into throwaway consumer projects, once per advertised target framework plus a base + recording combination, and resolve the services the readmes document. - Published AOT binary smoke-tested: `ui inspect`, `screenshot`, `record`, `click`, `list-windows`. - `docs/cli-schema.json` unchanged; plugin and MS Learn doc validators pass. ## Review rounds A multi-dimensional review before opening this caught three defects: `build-cli.ps1` ran only `WinApp.Cli.Tests`, so the 377 relocated tests never executed in CI; the NuGet Pester suite auto-discovered its package with a glob the new ids also match, failing every build that ran tests; and `RealRecordingTests` lost the cleanup that reset the static encoder seams. Later rounds addressed review feedback on this PR: - Raw capture moved from `IUiAutomation` to `IWindowCapture`, so encoder sizing and letterboxing are no longer on the base package's general interface. - Coverage is unioned across every test project's report. Reading only the first attributed the engine's coverage to the run that barely touches it, which showed up as an 8-point drop that was measurement rather than lost tests. Branch totals come from each line's own `condition-coverage` rather than assuming two branches per condition, which a `switch` violates. - `KeyboardInput` and `KeyTransport` documented the UIPI integrity rule backwards, including claiming `PostMessage` bypasses UIPI. A consumer following that would pick `PostMessage` to drive an elevated app and get silently dropped input. - `RecordAsync` is package API now, so its invariants no longer live only in the CLI command: `Fps = 0` divided by zero and a negative `DurationSec` recorded without end. - The implementation types above were internalized and the remaining public contracts documented. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Nikola Metulev <nmetulev@users.noreply.github.com> Copilot-Session: 58865cb0-d452-4132-9501-cae6b186fbe1



Summary
Revives and redesigns the unmerged work from #7810 as a narrowly scoped preview integration.
MSTest.Windows.UIAutomationpackage for MSTest-managed application lifecycle and UIA2 window accessMSTest.Sdkwith<EnableWindowsUIAutomation>true</EnableWindowsUIAutomation>for MTP and VSTestApplicationTestandWindowTestbases with overridable process launch, window discovery, and shutdown hooksEnableWindowsTargetingvaluesScope
This preview targets unpackaged, full-trust Win32, WinForms, and WPF applications running in an interactive Windows session. It does not provide packaged MSIX/UWP/WinUI activation, elevated-process automation, a headless desktop, locators, automatic waits, screenshots, or a multi-window object model.
Validation
build.cmd -packWindowsUIAutomationSdkTests: 26 passedOS=UnixSupersedes #7810.