Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Inspectez et interagissez avec l’exécution d’applications Windows à partir de la ligne de commande. Utilisé par les agents et développeurs d’IA pour les tests, le débogage et l’automatisation de l’interface utilisateur.
Aperçu
winapp ui fournit des commandes permettant d’inspecter et d’interagir avec des interfaces utilisateur d’application Windows.
Utilise Windows UI Automation (UIA). Fonctionne avec n’importe quelle application Windows : WPF, WinForms, Win32, Electron et WinUI 3.
La plupart des commandes pilotent l’application via des modèles UIA (aucune injection d’entrée) ; ui click est l’exception et utilise la simulation de souris réelle pour les contrôles qui ne prennent pas en charge InvokePattern.
Démarrage rapide
# 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
Ciblage d’applications
Par nom de processus
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
Par titre de la fenêtre
winapp ui inspect -a "LICENSE - Notepad"
winapp ui inspect -a "Fix WinApp" # partial title match
Par PID
winapp ui inspect -a 12345
Par HWND (stable — survive aux modifications de tabulation/titre)
# 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
Utilisation -a pour la découverte, -w pour le ciblage stable. Quand -a elle correspond à plusieurs fenêtres, la commande les répertorie avec des HWND pour que vous choisissiez.
Sélecteurs
Éléments cibles utilisant le sélecteur affiché dans [brackets] la sortie inspect/search.
Il existe trois types de sélecteurs :
| Selector | Sens | Example |
|---|---|---|
MinimizeButton |
AutomationId (indiqué lorsqu’il est unique — stable, préféré) | winapp ui invoke MinimizeButton -a myapp |
btn-close-d1a0 |
Slug sémantique (affichée lorsqu’aucun AutomationId unique) | winapp ui invoke btn-close-d1a0 -a myapp |
Submit |
Recherche en texte brut sur Name/AutomationId (sous-chaîne ne respectant pas la casse) | winapp ui invoke Submit -a myapp |
Les sélecteurs AutomationId sont des identificateurs de jeu de développeurs (AutomationProperties.AutomationId en XAML).
Lorsqu’un AutomationId est unique sur l’ensemble de l’arborescence de l’interface utilisateur et inspectsearch qu’il s’affiche directement en tant que sélecteur, ces modifications de disposition survivent aux modifications de disposition, à la localisation et à la restructuration des arborescences.
Les sélecteurs slug (par exemple, btn-close-d1a0) sont générés lorsqu’aucun AutomationId unique n’existe.
Format : prefix-name-hash. Le hachage valide l’identité de l’élément, mais peut être obsolète après les modifications de l’interface utilisateur.
Inspecter le format de sortie
La inspect commande affiche l’arborescence d’éléments avec une sortie colorée (sélecteur en cyan, nom en vert, métadonnées en gris) :
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
Le premier mot de chaque ligne est le sélecteur : utilisez-le avec d’autres ui commandes.
Lorsqu’un élément a un AutomationId unique, il est utilisé directement (par exemple, TabView, NewTabButton).
Lorsqu’aucun AutomationId unique n’existe, une slug générée est utilisée (par exemple tab-newtab-5f5b).
Slugs sémantiques
Les slugs utilisent le format : où : prefix-normalizedname-hash
- préfixe — abréviation de type 3 lettres (btn, txt, chk, cmb, itm, tab, img, lbl, pn, win, grp, lnk, mnu, etc.)
- normalizedname — alphanumérique minuscule à partir d’AutomationId (préféré) ou Name, max 15 chars
- hash — Hachage hexadécimal 4 caractères du RuntimeId de l’élément (valide l’identité de l’élément)
Les slugs sont sécurisés par l’interpréteur de commandes (aucun caractère spécial), uniques et peuvent être utilisés directement comme arguments. Le hachage fournit une détection d’obsolescence : si l’élément a été remplacé, vous obtenez : « L’élément peut avoir changé. Réexécutez l’inspection.
Les éléments sans nom ou AutomationId n’affichent que le préfixe + hachage (par exemple pn-c8a3).
Ambiguïté de plusieurs correspondances
Les slugs de sortie sont uniques, mais peuvent changer entre les modifications de inspect/search disposition : utilisez-les sur des noms de type brut ou du texte lorsque plusieurs correspondances sont effectuées. Lorsqu’un sélecteur est ambigu, l’interface CLI imprime toutes les correspondances avec leurs slugs afin que vous puissiez choisir celle qui convient et réexécuter avec ce 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
Recherche en texte brut
Utilisez du texte brut pour rechercher des éléments , aucune syntaxe spéciale n’est nécessaire :
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
Lorsqu’une recherche de texte correspond à plusieurs éléments (par exemple, SettingsExpander où Group, Button et Text partagent tous le même nom), l’interface CLI sélectionne automatiquement le seul élément appelant. Si plusieurs sont invoquables, il répertorie toutes les correspondances avec des slugs.
Pour les résultats de recherche non appelants (par exemple, un TextBlock à l’intérieur d’un bouton), la recherche surface automatiquement l’ancêtre appelant le plus proche , l’élément parent avec invokelequel vous pouvez utiliser .
Cela fonctionne pour tous les sélecteurs de recherche :
lbl-savechanges-a1b2 "Save changes" (120,40 80x20)
^ invoke via: btn-save-c3d4 "Save"
Le sélecteur surface peut être utilisé directement :
winapp ui invoke btn-save-c3d4 -a myapp # invoke the parent Button
Commandes
status
Connectez-vous à une application et affichez les informations de connexion.
winapp ui status -a notepad
winapp ui status -a notepad --json
Inspecter
Affichez l’arborescence des éléments d’interface utilisateur. La sortie affiche des slugs sémantiques avec mise en retrait de 2 espaces pour la 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
Exemple de sortie (par défaut) :
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)
Exemple de sortie ( —--interactive éléments invocables uniquement, liste plate) :
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)
Les éléments peuvent afficher ces marqueurs d’état :
-
[on]/[off]/[indeterminate]— activer/cocher l’état -
[collapsed]/[expanded]— développer/réduire l’état des arborescences, zones de liste modifiable, éléments de menu -
[scroll:v]/[scroll:h]/[scroll:vh]— conteneur à défilement (vertical, horizontal ou les deux) -
[offscreen]— l’élément n’est pas visible à l’écran -
[disabled]— l’élément n’est pas activé -
value="..."— contenu de texte actuel pour les éléments modifiables (lorsqu’ils sont différents de Name)
search
Recherchez des éléments correspondant à un sélecteur. La sortie affiche des slugs sémantiques :
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
Exemple de sortie :
btn-minimize-d1a0 "Minimize" (1222,206 48x48)
btn-maximize-e2b1 "Maximize" (1270,206 48x48)
btn-close-d1a2 "Close" (1318,206 48x48)
Les slugs affichés dans la sortie (par exemple, btn-minimize-d1a0) peuvent être utilisés directement avec d’autres commandes :
winapp ui invoke btn-minimize-d1a0 -a notepad
get-property
Lit les valeurs de propriété à partir d’un élément. Inclut l’état spécifique au modèle (ToggleState, Value, IsSelected, etc.).
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
capture d'écran
Capturez une fenêtre ou un élément en tant que PNG. Lorsqu’il existe plusieurs fenêtres (par exemple, l’application + boîte de dialogue Ouvrir), elles sont composites en une seule PNG avec chaque fenêtre insérée.
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)
Lorsque des boîtes de dialogue ou des fenêtres contextuelles sont ouvertes, toutes les fenêtres sont composites en une seule image PNG afin de voir l’état complet de l’interface utilisateur dans une seule image.
Utilisez --capture-screen quand vous devez capturer des menus contextuels, des listes déroulantes, des menu volants ou des superpositions d’info-bulles. En --capture-screen mode (et lors de la nouvelle tentative après la détection d’un cadre vide), la fenêtre cible est introduite au premier plan ; les captures de fenêtre normales ne déplacent pas la fenêtre.
Invoquer
Activez par programmation un élément (cliquez sur le bouton, activez la case à cocher bascule, développez la zone de liste déroulante).
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
Tente des modèles dans l’ordre : InvokePattern → TogglePattern → SelectionItemPattern → ExpandCollapsePattern.
cliquez
Cliquez sur un élément à ses coordonnées d’écran à l’aide de la simulation de souris. Utilisez-le pour les contrôles qui ne prennent pas en charge InvokePattern (par exemple, les en-têtes de colonne, les éléments de liste).
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
Définissez une valeur sur un élément modifiable (texte pour TextBox/ComboBox, numéro pour Slider).
winapp ui set-value txt-textbox-a4b1 "Hello world" -a notepad
winapp ui set-value sld-volume-b2c3 75 -a myapp
get-value
Lisez la valeur actuelle à partir d’un élément. Utilise une chaîne de secours intelligente : TextPattern (RichEditBox, Document) → ValuePattern (TextBox, Slider) → SelectionPattern (ComboBox, RadioButton, TabView) → Name (étiquettes).
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": "..." }
mise au point
Déplacez le focus clavier vers un élément.
winapp ui focus txt-textbox-a4b1 -a notepad
défilement dans la vue
Faites défiler un élément dans la zone visible.
winapp ui scroll-into-view itm-targetitem-c3d4 -a myapp
wait-for
Attendez qu’un élément apparaisse, disparaisse ou qu’une valeur atteigne une cible.
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
Faire défiler
Faites défiler un élément conteneur. Recherchez des conteneurs à défilement avec : search scroll recherchez [scroll:v] des marqueurs (verticaux) ou [scroll:h] (horizontaux).
# 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-focus
Afficher l’élément qui a actuellement le focus clavier.
winapp ui get-focused -a myapp
list-windows
Répertorier toutes les fenêtres visibles d’une application, y compris les fenêtres contextuelles et les dialogues.
winapp ui list-windows -a imageresizer
winapp ui list-windows -a Terminal
winapp ui list-windows # all windows (no filter)
Prise en charge du framework
| Cadre | Inspecter | search | Invoquer | set-value | capture d'écran |
|---|---|---|---|---|---|
| WPF | ✅ Arborescence complète | ✅ Toutes les propriétés | ✅ Tous les modèles | ✅ | ✅ |
| WinForms | ✅ | ✅ | ✅ | ✅ | ✅ |
| Win32 | ✅ | ✅ | ✅ | ✅ | ✅ |
| WinUI 3 | ✅ | ✅ | ✅ | ✅ | ✅ |
| Électron | ⚠️ Arbre chromium | ⚠️ Limité | ⚠️ Varie | ⚠️ Varie | ✅ |
| Flutter | ⚠️ Basique | ⚠️ Basique | ❌ Minimal | ❌ | ✅ |
Résolution des problèmes
| Error | Cause | Solution |
|---|---|---|
| « Aucune application en cours d’exécution trouvée » | L’application n’est pas en cours d’exécution ou ne correspond pas au nom | Vérifier le nom du processus ou utiliser le PID |
| « Correspondance de plusieurs fenêtres » | Valeur ambiguë -a |
Utiliser -w <HWND> à partir des options répertoriées |
| « a plusieurs fenêtres » | Le processus comporte plusieurs fenêtres | Utiliser -w <HWND> pour cibler une cible spécifique |
| « Sélecteur correspondant aux éléments N » | Sélecteur hérité ambigu | Utiliser des slugs à partir de la inspect sortie, ou ajouter [0], [1] aux sélecteurs hérités |
| « L’élément a peut-être changé » | Le hachage Slug ne correspond pas à l’élément actuel | Réexécuter inspect ou search pour obtenir des slugs frais |
| « ne prend pas en charge un modèle d’appel » | L’élément ne peut pas être appelé | Utiliser inspect sur l’élément pour rechercher un enfant appelant |
| « Aucune fenêtre UIA trouvée » | UIA ne peut pas voir le processus | Permet list-windows de rechercher le HWND, puis -w |
| « La fenêtre a une taille nulle » | La fenêtre est réduite | L’application sera restaurée automatiquement |
| Fenêtre contextuelle/liste déroulante non dans la capture d’écran | PrintWindow ne capture pas les superpositions | Utiliser l’indicateur --capture-screen |
Modèles courants
Naviguer et vérifier
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
Rechercher du texte et appeler son parent
# 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
Désambiguer les éléments dupliqués
winapp ui search '#Image' -a myapp; winapp ui invoke itm-image-a2b3 -a myapp
Capture d’écran avec superpositions contextuelles
winapp ui set-value txt-searchbox-e5f6 "query" -a myapp; winapp ui screenshot -a myapp --capture-screen
Naviguer, attendre et vérifier (chaîne unique)
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
Découvrir, cliquer et vérifier
winapp ui inspect -a myapp --interactive; winapp ui invoke btn-submit-7a90 -a myapp; winapp ui screenshot -a myapp
Interaction entre la boîte de dialogue fichier
Les dialogues d’ouverture/d’enregistrement de fichiers sont des boîtes de dialogue standard Windows avec prise en charge de l’UIA :
# 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>
Permet inspect -w <dialog-hwnd> --interactive de découvrir les slugs réels d’une boîte de dialogue spécifique.
Pourquoi ; le chaînage (pas &&)
L’opérateur powerShell && peut figer lorsqu’une interface CLI native écrit dans stderr ou utilise des séquences d’échappement ANSI. Utilisez ; plutôt : elle exécute chaque commande de manière inconditionnelle et évite ce blocage. Cela est également préférable pour les flux de travail de l’agent : vous souhaitez généralement que la capture d’écran s’exécute même si l’appel avait une sortie non nulle.
Modèles de test CI
Utilisez winapp ui commandes dans les pipelines CI (GitHub Actions, Azure DevOps) pour les tests de fumée et la validation de l’interface utilisateur.
wait-for avec --property et --value agit comme une assertion : elle retourne le code de sortie 1 lors du délai d’expiration, en cas d’échec automatique de l’étape CI.
Lancer et tester dans 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
État de l’élément Assert avec wait-for
wait-for --value interroge jusqu’à ce que la valeur d’un élément corresponde à la chaîne attendue, en utilisant la même secours intelligente que get-value (TextPattern → ValuePattern → SelectionPattern → Name). Retourne le code de sortie 0 lors de la correspondance, le code de sortie 1 sur le délai d’expiration, ce qui en fait une assertion conviviale pour l’intégration continue. Permet --property de vérifier une propriété UIA spécifique à la place.
# 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
Assertion avec la sortie JSON
Utilisez --json PowerShell ou jq pour des assertions plus complexes :
Contrat de code de sortie pour
searchetwait-foren--jsonmode : quand aucun élément ne correspond (search) ou le délai d’attente (wait-for), la commande écrit une enveloppe de résultat entièrement analysée dans stdout ({ "matchCount": 0, ... }ou{ "found": false, "timedOut": true, ... }) et retourne le code de sortie 1. Stderr est vide en--jsonmode (la sortie de l’enregistreur d’événements est supprimée). Branchez sur les champs d’enveloppe, ou sur$LASTEXITCODE, selon ce qui est plus ergonomique.
# 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)" }
Exemple de test de fumée complet
# 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