Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Inspecteer en communiceer met het uitvoeren van Windows toepassingen vanaf de opdrachtregel. Wordt gebruikt door AI-agents en ontwikkelaars voor het testen van gebruikersinterfaces, foutopsporing en automatisering.
Overzicht
winapp ui biedt opdrachten voor het controleren en gebruiken van Windows app-API's.
Maakt gebruik van Windows UI Automation (UIA). Werkt met elke Windows-app: WPF, WinForms, Win32, Electron en WinUI 3.
De meeste opdrachten sturen de app via UIA-patronen (geen invoerinjectie); ui click is de uitzondering en maakt gebruik van echte muissimulatie voor besturingselementen die niet worden ondersteund InvokePattern.
Snel aan de slag
# Connect to any app and see its UI tree
winapp ui inspect -a notepad
# Find specific elements
winapp ui search Button -a notepad
# Activate an element
winapp ui invoke Close -a notepad
# Take a screenshot
winapp ui screenshot -a notepad
Apps instellen
Op procesnaam
winapp ui inspect -a notepad
winapp ui inspect -a slack # auto-picks visible window for multi-process apps
winapp ui inspect -a imageresizer # partial match: finds PowerToys.ImageResizer
Op venstertitel
winapp ui inspect -a "LICENSE - Notepad"
winapp ui inspect -a "Fix WinApp" # partial title match
Per PID
winapp ui inspect -a 12345
Door HWND (stabiel β overleeft tab-/titelwijzigingen)
# Discover HWNDs
winapp ui list-windows -a Terminal
β HWND 985238: "π€ Testing" (WindowsTerminal, PID 21228)
β HWND 131906: "Fix WinApp" (WindowsTerminal, PID 21228)
# Target specific window
winapp ui inspect -w 131906
winapp ui screenshot -w 131906
Gebruiken -a voor detectie, -w voor stabiele targeting. Wanneer -a deze overeenkomen met meerdere vensters, worden deze weergegeven met HWND's die u kunt kiezen.
Selectoren
Doelelementen met behulp van de selector die wordt weergegeven in [brackets] inspect/search-uitvoer.
Er zijn drie typen selectors:
| Selector | Meaning | Example |
|---|---|---|
MinimizeButton |
AutomationId (weergegeven als uniek β stabiel, voorkeur) | winapp ui invoke MinimizeButton -a myapp |
btn-close-d1a0 |
Semantische slug (weergegeven wanneer geen unieke AutomationId) | winapp ui invoke btn-close-d1a0 -a myapp |
Submit |
Zoeken in tekst zonder opmaak op naam/AutomationId (hoofdlettergevoelige subtekenreeks) | winapp ui invoke Submit -a myapp |
AutomationId-selectors zijn id's voor ontwikkelaarssets (AutomationProperties.AutomationId in XAML).
Wanneer een AutomationId uniek is in de hele UI-structuur inspect en search deze rechtstreeks als de selector weergeeft, blijven de indelingswijzigingen, lokalisatie en structuurherstructurering behouden.
Slugselectors (bijvoorbeeld btn-close-d1a0) worden gegenereerd wanneer er geen unieke AutomationId bestaat.
Indeling: prefix-name-hash. De hash valideert de elementidentiteit, maar kan verlopen nadat de gebruikersinterface is gewijzigd.
Uitvoerindeling controleren
De inspect opdracht toont de elementstructuur met gekleurde uitvoer (selector in cyaan, naam in groen, metagegevens in grijs):
TabView Tab (0,-1 1200x48)
TabListView List (4,-1 1100x48)
tab-newtab-5f5b TabItem "New Tab" (14,-1 200x48)
NewTabButton SplitButton "New Tab" [collapsed] (1104,5 96x36)
Found 10 elements (--depth 3). Use the first token as selector, e.g.: winapp ui invoke TabView -a terminal
Het eerste woord op elke regel is de selector. Gebruik het met andere ui opdrachten.
Wanneer een element een unieke AutomationId heeft, wordt het rechtstreeks gebruikt (bijvoorbeeld TabView, NewTabButton).
Wanneer er geen unieke AutomationId bestaat, wordt er een gegenereerde slug gebruikt (bijvoorbeeld tab-newtab-5f5b).
Semantische slugs
Slugs gebruiken de notatie: prefix-normalizedname-hash waarbij:
- voorvoegsel β afkorting van 3 letters (btn, txt, chk, cmb, itm, tab, img, lbl, pn, win, grp, lnk, mnu, enz.)
- normalizedname : alfanumerieke kleine letters van AutomationId (voorkeur) of Naam, maximaal 15 tekens
- hash : hexhash van 4 tekens van de RuntimeId van het element (valideert elementidentiteit)
Slugs zijn shell-safe (geen speciale tekens), uniek en kunnen rechtstreeks als argumenten worden gebruikt. De hash biedt verouderingsdetectie. Als het element is vervangen, krijgt u het volgende: 'Element is mogelijk gewijzigd. Voer de inspectie opnieuw uit."
Elementen zonder naam of AutomationId geven alleen voorvoegsel en hash weer (bijvoorbeeld pn-c8a3).
Meerdere overeenkomsten ondubbelzinnig maken
Slugs van inspect/search uitvoer zijn uniek, maar kunnen worden gewijzigd in indelingswijzigingen. Gebruik ze boven namen of tekst zonder opmaak wanneer meerdere overeenkomsten overeenkomen. Wanneer een selector dubbelzinnig is, worden alle overeenkomsten met hun slugs afgedrukt, zodat u de juiste kunt kiezen en opnieuw kunt uitvoeren met die slug.
winapp ui search Button -a myapp # shows: btn-ok-a1b2 "OK", btn-cancel-c3d4 "Cancel"
winapp ui invoke btn-ok-a1b2 -a myapp # invoke using slug (preferred)
winapp ui invoke btn-cancel-c3d4 -a myapp # invoke the other Button by its slug
Zoeken in tekst zonder opmaak
Gebruik tekst zonder opmaak om te zoeken naar elementen. Er is geen speciale syntaxis nodig:
winapp ui search Minimize -a notepad # finds elements with "Minimize" in Name or AutomationId
winapp ui search Close -a notepad # case-insensitive substring match
winapp ui invoke Minimize -a notepad # search + invoke in one step (disambiguates if needed)
winapp ui search "Save" -a notepad # find elements containing "Save"
winapp ui search "error" -a myapp # case-insensitive match
Wanneer een tekstzoekopdracht overeenkomt met meerdere elementen (bijvoorbeeld SettingsExpander waarbij groep, knop en tekst allemaal dezelfde naam delen), kiest de CLI automatisch het enige aanroepbare element. Als er meerdere aanroepbaar zijn, worden alle overeenkomsten met slugs weergegeven.
Voor niet-aanroepbare zoekresultaten (bijvoorbeeld een TextBlock binnen een knop), wordt automatisch de dichtstbijzijnde aanroepbare bovenliggende bovenliggende waarde weergegeven, het bovenliggende element waarmee invokeu kunt gebruiken.
Dit werkt voor alle zoekkiezers:
lbl-savechanges-a1b2 "Save changes" (120,40 80x20)
^ invoke via: btn-save-c3d4 "Save"
De surfaced selector kan rechtstreeks worden gebruikt:
winapp ui invoke btn-save-c3d4 -a myapp # invoke the parent Button
Opdrachten
status
Maak verbinding met een app en geef verbindingsgegevens weer.
winapp ui status -a notepad
winapp ui status -a notepad --json
Inspecteren
Bekijk de structuur van het ui-element. De uitvoer toont semantische slugs met twee spaties inspringing voor de hiΓ«rarchie:
winapp ui inspect -a notepad # full window tree, depth 3
winapp ui inspect -a notepad --depth 5 # deeper tree
winapp ui inspect txt-searchbox-e5f6 -a notepad # subtree rooted at element
winapp ui inspect --ancestors btn-close-d1a2 -a notepad # walk up from element to root
winapp ui inspect -a myapp --interactive # invokable elements only, auto-depth 8
winapp ui inspect -a myapp --hide-disabled # hide disabled elements
winapp ui inspect -a myapp --hide-offscreen # hide offscreen elements
Voorbeelduitvoer (standaard):
win-aidevgalleryp-f1a3 "AI Dev Gallery Preview" (94,206 1280x1023)
pn-c8a3 (102,207 1264x1014)
btn-minimize-d1a0 "Minimize" (1222,206 48x48)
btn-maximize-e2b1 "Maximize" (1270,206 48x48)
itm-samples-3f2c "Samples" (102,330 72x62)
Voorbeelduitvoer (--interactive β alleen aanroepbare elementen, platte lijst):
btn-minimize-d1a0 "Minimize" (1222,206 48x48)
btn-maximize-e2b1 "Maximize" (1270,206 48x48)
btn-close-d1a2 "Close" (1318,206 48x48)
itm-home-7b3e "Home" (102,268 72x62)
itm-samples-3f2c "Samples" (102,330 72x62)
itm-models-9a4f "Models" (102,392 72x62)
Elementen kunnen deze statusmarkeringen weergeven:
-
[on]/[off]/[indeterminate]β status van wisselknop/selectievakje -
[collapsed]/[expanded]β de status voor bomen, keuzelijsten met invoervak, menu-items uitvouwen/samenvouwen -
[scroll:v]/[scroll:h]/[scroll:vh]β schuifbare container (verticaal, horizontaal of beide) -
[offscreen]β element is niet zichtbaar op het scherm -
[disabled]β element is niet ingeschakeld -
value="..."β huidige tekstinhoud voor bewerkbare elementen (indien anders dan Naam)
search
Zoek elementen die overeenkomen met een selector. Uitvoer toont semantische slugs:
winapp ui search Button -a notepad # all buttons
winapp ui search Close -a notepad # finds elements with "Close" in name
winapp ui search SearchBox -a notepad # finds elements with "SearchBox" in name or AutomationId
winapp ui search Button --max 10 -a notepad # limit results
Voorbeelduitvoer:
btn-minimize-d1a0 "Minimize" (1222,206 48x48)
btn-maximize-e2b1 "Maximize" (1270,206 48x48)
btn-close-d1a2 "Close" (1318,206 48x48)
Slugs die worden weergegeven in de uitvoer (bijvoorbeeld btn-minimize-d1a0) kunnen rechtstreeks worden gebruikt met andere opdrachten:
winapp ui invoke btn-minimize-d1a0 -a notepad
get-property
Eigenschapswaarden van een element lezen. Bevat patroonspecifieke status (ToggleState, Waarde, IsSelected, enzovoort).
winapp ui get-property btn-submit-7a90 -a myapp # all properties
winapp ui get-property chk-checkbox-b2c3 -p ToggleState -a myapp # checkbox state
winapp ui get-property txt-textbox-a4b1 -p Value -a myapp # current text value
winapp ui get-property cmb-combobox-d5e6 -p ExpandCollapseState -a myapp # expanded or collapsed
schermopname
Een venster of element vastleggen als PNG. Wanneer er meerdere vensters bestaan (bijvoorbeeld app en dialoogvenster openen), worden ze samengevoegd in één PNG-bestand met elk venster dat is gestikt.
winapp ui screenshot -a notepad # saves screenshot.png in cwd
winapp ui screenshot -a notepad --output my.png # custom filename
winapp ui screenshot -a notepad --json # returns file path as JSON
winapp ui screenshot -w 131906 # target specific HWND (+ its dialogs)
winapp ui screenshot txt-searchbox-e5f6 -a myapp # crop to element bounds
winapp ui screenshot -a myapp --capture-screen # capture from screen (includes popups/overlays)
Wanneer dialoogvensters of pop-ups zijn geopend, worden alle vensters samengevoegd in één PNG,zodat u de volledige UI-status in één afbeelding kunt zien.
Gebruik --capture-screen deze optie wanneer u pop-upmenu's, vervolgkeuzelijsten, flyouts of overlays met knopinfo wilt vastleggen. In --capture-screen de modus (en wanneer u het opnieuw probeert nadat een leeg frame is gedetecteerd) wordt het doelvenster eerst naar de voorgrond gebracht; normale vensteropnamen verplaatsen het venster niet.
aanroepen
Programmatisch een element activeren (klik op de knop, schakeloptie in, keuzelijst met invoervak uitvouwen).
winapp ui invoke btn-submit-7a90 -a myapp # by slug from inspect
winapp ui invoke btn-submit-a1b2 -a myapp # by slug from inspect/search
winapp ui invoke cmb-sizecombobox-b4c5 -a myapp # expand combo box
Probeert patronen in volgorde: InvokePattern β TogglePattern β SelectionItemPattern β ExpandCollapsePattern.
click
Klik op een element op de schermcoΓΆrdinaten met behulp van muissimulatie. Gebruik dit voor besturingselementen die geen ondersteuning bieden InvokePattern (bijvoorbeeld kolomkoppen, lijstitems).
winapp ui click btn-column1-a3f2 -a myapp # single click by slug
winapp ui click "Column1" -a myapp # single click by text search
winapp ui click btn-column1-a3f2 -a myapp --double # double-click
winapp ui click btn-column1-a3f2 -a myapp --right # right-click
set-value
Stel een waarde in voor een bewerkbaar element (tekst voor tekstvak/keuzelijst met invoervak, nummer voor schuifregelaar).
winapp ui set-value txt-textbox-a4b1 "Hello world" -a notepad
winapp ui set-value sld-volume-b2c3 75 -a myapp
get-value
Lees de huidige waarde van een element. Maakt gebruik van een slimme terugvalketen: TextPattern (RichEditBox, Document) β ValuePattern (TextBox, Slider) β SelectionPattern (ComboBox, RadioButton, TabView) β Naam (labels).
winapp ui get-value doc-texteditor-53ad -a notepad # read full document text
winapp ui get-value SearchBox -a myapp # read TextBox content
winapp ui get-value CmbTheme -a myapp # read ComboBox selected item
winapp ui get-value sld-volume-b2c3 -a myapp # read Slider value
winapp ui get-value lbl-title-a1b2 -a myapp --json # JSON: { "elementId": "...", "text": "..." }
focus
Verplaats de focus van het toetsenbord naar een element.
winapp ui focus txt-textbox-a4b1 -a notepad
scroll-into-view
Schuif een element naar het zichtbare gebied.
winapp ui scroll-into-view itm-targetitem-c3d4 -a myapp
wachten op
Wacht tot een element wordt weergegeven, verdwijnt of heeft een waarde een doel bereikt.
winapp ui wait-for Button -a myapp --timeout 5000 # wait for any button
winapp ui wait-for btn-submit-7a90 -a myapp --timeout 5000 # wait for specific element
winapp ui wait-for CounterDisplay -a myapp --value "5" --timeout 5000 # wait for element value (smart fallback)
winapp ui wait-for lbl-status -a myapp --property Name --value "Done" --timeout 5000 # wait for specific property
winapp ui wait-for btn-submit-a1b2 --gone -a myapp --timeout 2000 # wait for element to disappear
winapp ui wait-for lbl-status -a myapp --value "Done" --contains # substring match instead of exact equality
Schuiven
Schuif door een containerelement. Schuifbare containers zoeken met search scroll : zoek [scroll:v] naar (verticale) of [scroll:h] (horizontale) markeringen.
# Find which elements are scrollable and in which direction
winapp ui search scroll -a myapp
# pn-scrollview-bfef Pane "scrollView" [scroll:v] (main content, vertical)
# pn-scrollviewer-bfb1 Pane "scrollViewer" [scroll:h] (horizontal list)
# Scroll the main content down
winapp ui scroll pn-scrollview-bfef --direction down -a myapp
# Jump to top/bottom
winapp ui scroll pn-scrollview-bfef --to bottom -a myapp
# If you target an element that's not scrollable, scroll walks up to find the nearest scrollable parent
winapp ui scroll itm-someitem-a1b2 --direction down -a myapp
get-focused
Het element weergeven dat momenteel de toetsenbordfocus heeft.
winapp ui get-focused -a myapp
list-windows
Alle zichtbare vensters voor een app weergeven, inclusief pop-ups en dialoogvensters.
winapp ui list-windows -a imageresizer
winapp ui list-windows -a Terminal
winapp ui list-windows # all windows (no filter)
Framework-ondersteuning
| Raamwerk | Inspecteren | search | aanroepen | set-value | schermopname |
|---|---|---|---|---|---|
| WPF | β Volledige structuur | β Alle eigenschappen | β Alle patronen | β | β |
| WinForms | β | β | β | β | β |
| Win32 | β | β | β | β | β |
| WinUI 3 | β | β | β | β | β |
| Elektron | β β Chroomboom | β οΈ Beperkt | β Ο varieert | β Ο varieert | β |
| Flutter | β οΈ Basis | β οΈ Basis | β Minimale | β | β |
Troubleshooting
| Fout | Oorzaak | Oplossing |
|---|---|---|
| "Er is geen actieve app gevonden" | App wordt niet uitgevoerd of de naam komt niet overeen | Procesnaam controleren of PID gebruiken |
| "Meerdere vensters komen overeen" | Dubbelzinnige -a waarde |
Gebruiken -w <HWND> vanuit de vermelde opties |
| "heeft meerdere vensters" | Proces heeft meerdere vensters | Gebruiken -w <HWND> om een specifiek doel te bereiken |
| "Selector matched N elements" | Dubbelzinnige verouderde selector | Slugs uit inspect uitvoer gebruiken of toevoegen [0]aan [1] verouderde selectors |
| "Element kan zijn gewijzigd" | Slug-hash komt niet overeen met het huidige element | Opnieuw uitvoeren inspect of search verse slugs krijgen |
| "biedt geen ondersteuning voor een aanroeppatroon" | Het element kan niet worden aangeroepen | Op inspect het element gebruiken om een aanroepbaar onderliggend element te zoeken |
| "Geen UIA-venster gevonden" | UIA kan het proces niet zien | Gebruik list-windows deze om de HWND te vinden en vervolgens -w |
| "Venster heeft nulgrootte" | Venster is geminimaliseerd | De app wordt automatisch hersteld |
| Pop-up/vervolgkeuzelijst niet in schermopname | PrintWindow legt geen overlays vast | Vlag gebruiken --capture-screen |
Algemene patronen
Navigeren en controleren
winapp ui invoke btn-settings-a1b2 -a myapp # click a button
winapp ui wait-for pn-settingspage-c3d4 -a myapp # wait for page to load
winapp ui screenshot -a myapp --output settings.png # verify visually
Tekst zoeken en het bovenliggende item aanroepen
# Search shows invokable ancestor; invoke auto-walks to it
winapp ui invoke 'Save changes' -a myapp
# Or search first to see what matches, then invoke
winapp ui search "Save changes" -a myapp; winapp ui invoke btn-save-c3d4 -a myapp
Dubbele elementen ondubbelzinnig maken
winapp ui search '#Image' -a myapp; winapp ui invoke itm-image-a2b3 -a myapp
Schermopname met pop-up-overlays
winapp ui set-value txt-searchbox-e5f6 "query" -a myapp; winapp ui screenshot -a myapp --capture-screen
Navigeren, wachten en controleren (enkele keten)
winapp ui invoke btn-settings-a1b2 -a myapp; winapp ui wait-for pn-settingspage-c3d4 -a myapp --timeout 3000; winapp ui screenshot -a myapp -o settings.png
Ontdekken, klikken en controleren
winapp ui inspect -a myapp --interactive; winapp ui invoke btn-submit-7a90 -a myapp; winapp ui screenshot -a myapp
Interactie in dialoogvenster Bestand
Dialoogvensters voor openen/opslaan van bestanden zijn standaarddialoogvensters Windows met UIA-ondersteuning:
# Trigger the dialog, find it, type the path, confirm
winapp ui invoke btn-openfilebtn-a2b3 -a myapp
winapp ui list-windows -a myapp # find dialog HWND
winapp ui set-value txt-1148-c4d5 "C:\path\to\file.png" -w <dialog-hwnd>
winapp ui invoke btn-open-e6f7 -w <dialog-hwnd>
Gebruik inspect -w <dialog-hwnd> --interactive deze functie om de werkelijke slugs voor een specifiek dialoogvenster te ontdekken.
Waarom ; ketenen (niet &&)
De operator van && PowerShell kan blokkeren wanneer een systeemeigen CLI schrijft naar stderr of ANSI-escapereeksen gebruikt. In ; plaats daarvan wordt elke opdracht voorwaardelijke uitgevoerd en wordt deze impasse vermeden. Dit is ook beter voor agentwerkstromen: meestal wilt u dat de schermafbeelding wordt uitgevoerd, zelfs als de aanroep een afsluit zonder nul heeft.
CI-testpatronen
Gebruik winapp ui opdrachten in CI-pijplijnen (GitHub Actions, Azure DevOps) voor betrouwbaarheidstests en ui-validatie.
wait-for met --property en --value fungeert als een assertie: het retourneert afsluitcode 1 bij time-out, waarbij de CI-stap automatisch mislukt.
Starten en testen in GitHub Actions
steps:
- name: Build
run: dotnet build MyApp.csproj -c Debug -p:Platform=x64
- name: Launch and test
run: |
$result = winapp run .\bin\x64\Debug\net8.0-windows10.0.26100.0\win-x64 --detach --json | ConvertFrom-Json
$appPid = $result.ProcessId
# Wait for window to initialize
winapp ui wait-for "Main Window" -a $appPid --timeout 30000
# Run tests β each wait-for exits non-zero on failure
winapp ui invoke "Login" -a $appPid
winapp ui wait-for "Dashboard" -a $appPid --timeout 10000
winapp ui screenshot -a $appPid -o dashboard.png
Status van element assertie met wait-for
wait-for --value pollt totdat de waarde van een element overeenkomt met de verwachte tekenreeks, waarbij dezelfde slimme terugval wordt gebruikt als get-value (TextPattern β ValuePattern β SelectionPattern β Name). Retourneert afsluitcode 0 bij overeenkomst, afsluitcode 1 bij time-out, waardoor deze een CI-vriendelijke assertie is. Gebruik --property in plaats daarvan om een specifieke UIA-eigenschap te controleren.
# Assert: button click updated the counter (smart value fallback β works for TextBlock, TextBox, etc.)
winapp ui invoke "Counter Button" -a $pid
winapp ui wait-for "Counter Display" -a $pid --value "Count: 1" -t 5000
# Assert: text input was accepted
winapp ui set-value "Search Box" "hello world" -a $pid
winapp ui wait-for "Search Box" -a $pid --value "hello world" -t 3000
# Assert: checkbox was toggled (use --property for specific UIA properties)
winapp ui invoke "Dark Mode" -a $pid
winapp ui wait-for "Dark Mode" -a $pid --property ToggleState --value "On" -t 3000
# Assert: navigation happened (new page appeared)
winapp ui invoke "Settings" -a $pid
winapp ui wait-for "Settings Page" -a $pid -t 10000
# Assert: dialog was dismissed (element disappeared)
winapp ui invoke "Close" -a $pid
winapp ui wait-for "Dialog Title" -a $pid --gone -t 5000
Assert met JSON-uitvoer
Gebruik --json met PowerShell of jq voor complexere asserties:
Afsluitcodecontract voor
searchenwait-forin--jsonde modus: als er geen element overeenkomt (search) of de wachttijd (wait-for), schrijft de opdracht een volledig parseerbare resultaatenvelop naar stdout ({ "matchCount": 0, ... }of{ "found": false, "timedOut": true, ... }) en retourneert afsluitcode 1. Stderr is leeg in--jsonde modus (loggeruitvoer wordt onderdrukt). Vertakking op de envelopvelden, of op$LASTEXITCODE, afhankelijk van welke ergonomischer is.
# Assert: search found exactly one match
$result = winapp ui search "Submit" -a $pid --json | ConvertFrom-Json
if ($result.matchCount -ne 1) { throw "Expected 1 Submit button, found $($result.matchCount)" }
# Assert: element has expected properties
# inspect --json returns { windows: [{ hwnd, title, elements: [...] }] };
# each window's elements[] is the nested tree (children rendered via .children).
$tree = winapp ui inspect "Counter Display" -a $pid --json | ConvertFrom-Json
$counter = $tree.windows[0].elements[0]
if ($counter.name -ne "Count: 3") { throw "Counter value wrong: $($counter.name)" }
Voorbeeld van een volledige betrouwbaarheidstest
# Launch
$app = winapp run .\build-output --detach --json | ConvertFrom-Json
# Verify app loaded
winapp ui wait-for "Main Page" -a $app.ProcessId -t 30000
# Interact and assert
winapp ui invoke "Add Item" -a $app.ProcessId
winapp ui set-value "Item Name" "Test Item" -a $app.ProcessId
winapp ui invoke "Save" -a $app.ProcessId
winapp ui wait-for "Test Item" -a $app.ProcessId -t 5000 # assert item appeared in list
winapp ui wait-for "Save" -a $app.ProcessId --gone -t 3000 # assert save dialog closed
# Visual verification
winapp ui screenshot -a $app.ProcessId -o smoke-test.png
Windows developer