Expose whole-document TextPattern formatting in get-property - #847
Conversation
Fixes #822 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Shared validation causes ui wait-for to emit internal_error for unknown property names instead of invalid_arguments.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds whole-document TextPattern formatting to ui get-property, including standardized mixed, unsupported, and unavailable states.
Changes:
- Adds six text-formatting properties and strict name validation.
- Preserves string-valued CLI/JSON contracts.
- Adds unit, real-provider, CLI, and documentation coverage.
File summaries
| File | Description |
|---|---|
src/winapp-npm/src/winapp-commands.ts |
Updates generated npm API description. |
src/winapp-CLI/WinApp.UIAutomation/Services/UiAutomationService.TextAttributes.cs |
Reads and formats TextPattern attributes. |
src/winapp-CLI/WinApp.UIAutomation/Services/UiAutomationService.cs |
Integrates attributes and validation. |
src/winapp-CLI/WinApp.UIAutomation/Models/UiPropertyNames.cs |
Defines supported property names. |
src/winapp-CLI/WinApp.UIAutomation.TestSupport/UiaTestFixture.TextAttributes.cs |
Adds formatted RichEdit fixtures. |
src/winapp-CLI/WinApp.UIAutomation.TestSupport/UiaTestFixture.ReservedTextProvider.cs |
Adds a real unsupported-value provider. |
src/winapp-CLI/WinApp.UIAutomation.TestSupport/UiaTestFixture.cs |
Supports non-activating fixtures. |
src/winapp-CLI/WinApp.UIAutomation.Tests/TextAttributeTests.cs |
Tests formatting and failure behavior. |
src/winapp-CLI/WinApp.UIAutomation.Tests/RealUiAutomationTests.TextAttributes.cs |
Tests real UIA providers. |
src/winapp-CLI/WinApp.UIAutomation.Tests/RealUiAutomationTests.cs |
Updates unknown-property expectations. |
src/winapp-CLI/WinApp.UIAutomation.Tests/RealUiAutomationTests.Coverage.cs |
Extends property coverage. |
src/winapp-CLI/WinApp.Cli/Commands/UiGetPropertyCommand.cs |
Adds validation and updated help. |
src/winapp-CLI/WinApp.Cli.Tests/UiCommandTests.TextAttributes.cs |
Tests CLI envelopes and errors. |
src/winapp-CLI/WinApp.Cli.Tests/PublicApiSurfaceTests.cs |
Registers the public names API. |
plugins/winapp/skills/winapp-ui-automation/SKILL.md |
Documents formatting usage. |
plugins/winapp/skills/winapp-ui-automation/references/ui-json-envelope.md |
Documents JSON output. |
docs/ui-automation.md |
Adds canonical formatting reference. |
docs/npm-usage.md |
Updates npm documentation. |
docs/cli-schema.json |
Regenerates command description. |
Review details
- Files reviewed: 19/19 changed files
- Comments generated: 1
- Review effort level: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Build Metrics ReportBinary Sizes
Test Results✅ 7177 passed, 37 skipped out of 7214 tests in 1201.1s (+38 tests, -157.8s vs. baseline) Test Coverage✅ 85.9% line coverage, 80% branch coverage · ✅ no change vs. baseline CLI Startup Time69ms median (x64, Try This BuildInstalls the MSIX for your architecture, replacing any previously installed build. Needs the GitHub CLI — the command offers to install it and sign you in if it is missing. & ([scriptblock]::Create((irm https://raw.githubusercontent.com/microsoft/winappCli/main/scripts/winapp-pr.ps1))) 847Switching between builds often?Put the tool on your PATH once: & ([scriptblock]::Create((irm https://raw.githubusercontent.com/microsoft/winappCli/main/scripts/winapp-pr.ps1))) -AddToPathThen this build is just: winapp-pr 847Run Updated 2026-09-17 06:18:42 UTC · commit |
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟢 Approval recommended
The implementation satisfies the issue contract with comprehensive validation, provider coverage, and updated shipping surfaces.
Review details
- Files reviewed: 20/20 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Zach Teutsch (zateutsch)
left a comment
There was a problem hiding this comment.
Request changes — one blocking item. The feature, tests, docs, and packaging surfaces look good and it builds clean; this is the only thing I'd hold the merge on.
All-properties get-property throws away everything when text-attribute enrichment fails
What is wrong: In all-properties mode (--property omitted), AddTextAttributes(comElement, …) runs unconditionally. If the live element can't be re-resolved (comElement is null) it throws InvalidOperationException; likewise any single attribute that returns an unexpected VARIANT type or an unrecognized token throws. Either exception propagates out of GetPropertiesAsync, hits the command's generic catch, and aborts the whole call — discarding the cached basic properties (Name, AutomationId, ControlType, …) that used to come back.
Show me — reproduced by running both branches against the same unresolvable element (s_getRootElement = null):
main (before) |
this PR | |
|---|---|---|
get-property (no --property) |
returns 7 props (Name, AutomationId, ControlType, ClassName, IsEnabled, IsOffscreen, BoundingRectangle), exit 0 | throws InvalidOperationException: Element is stale → generic error, exit 1 |
get-property --property Name |
Name = cached value, exit 0 |
Name = cached value, exit 0 |
Same element, opposite outcome depending only on whether --property was named. Pre-PR the all-properties path degraded gracefully; now it hard-fails.
Why it matters: This is a regression in the command's default mode. A serialized/stale element that used to yield best-effort cached data now returns nothing with a non-zero exit — and separately, one malformed formatting value from a custom/third-party UIA provider makes an otherwise-useful inspection fail completely. There's an existing test asserting the scalar path still returns cached props on this null-element setup, so graceful degradation here appears to be an intended contract.
Smallest fix: In all-properties mode only, make text-attribute enrichment best-effort — skip it when comElement is null, and isolate per-attribute decode failures so one bad attribute is omitted rather than fatal. Keep an explicit --property <FontWeight|…> request failing loudly as it does today.
Location: src/winapp-CLI/WinApp.UIAutomation/Services/UiAutomationService.cs (the unconditional AddTextAttributes call in GetPropertiesAsync) and src/winapp-CLI/WinApp.UIAutomation/Services/UiAutomationService.TextAttributes.cs (AddTextAttributes null-throw and FormatTextAttribute throw-on-unexpected-type).
Retain cached basics on null live resolution, isolate malformed attribute decoding with warnings, and keep explicit requests and provider errors strict. Integrate current main without overwriting human changes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Addressed the all-properties regression in 95f6898. Before changing the implementation, three new regressions reproduced loss of cached basics on null live resolution and loss of the whole result for VT_I8/foreign-IUnknown formatting values. All-properties mode now keeps cached basics when resolution returns null and omits only an undecodable attribute, logging each omission. Explicit text-property requests still fail; provider calls and strict retained-element resolution remain outside the decode catch. Existing Mixed/NotSupported/Unavailable values remain distinct. Also updated the feature envelope regression to preserve the typed element added by merged main. Validation: 31 library/property/retained tests, 70 CLI/property/wait-for/API tests, NativeAOT/package build, and published real-provider get-all/wait-for checks pass. Please re-review; the changes-requested review has not been dismissed. |
…idation Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Two newly added JSON examples omit the always-emitted typed element field, and one duplicates an existing canonical section.
Get a fresh assessment by requesting another Copilot review.
Review details
- Files reviewed: 20/20 changed files
- Comments generated: 2
- Review effort level: Balanced
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟢 Approval recommended
The solution builds, publishes under NativeAOT, passes focused tests, and works against real uniform and mixed-format providers.
Review details
- Files reviewed: 20/20 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Description
Read
FontWeight,FontName,FontSize,ForegroundColor,IsItalic, andStrikethroughStyleacross the element's whole TextPattern document. Uniform values are invariant strings; UIA's mixed and reserved-not-supported COM tokens becomeMixedandNotSupported, while elements without TextPattern returnUnavailable. Provider failures retain the existing error contract.Unknown case-sensitive property names now fail with
invalid_argumentsbefore querying the app. Omitting--propertyincludes all six attributes. The publishedelementIdand string-valuedpropertiesJSON envelope and existing geometry formats are unchanged.Usage Example
Related Issue
Fixes #822. Independent of #841, #842, and #844. No query predicates, explicit actions, selection/caret/range APIs, batch mode, or persistence.
Type of Change
Checklist
scripts/build-cli.ps1 -SkipTestscompletes NativeAOT x64/arm64 publishing, npm/NuGet/MSIX packaging, and generated docs/schemaAdditional Notes
Real-app coverage reads native RichEdit uniform and mixed formatting independently of selection, a Button without TextPattern, and an HWND-based provider returning UIAutomationCore's actual reserved-not-supported token through the repository COM projection. Background fixtures assert they never activate. Unit tests cover invariant conversion, actual COM sentinel identity, unknown names, and provider-error propagation; CLI tests pin string serialization and scrubbed errors.
Pre-human-review gate is pending automated review and CI; this PR is not being merged by the implementation session.