UI Otomasyonu

Komut satırından çalışan Windows uygulamaları inceleyin ve bunlarla etkileşime geçin. Yapay zeka aracıları ve geliştiriciler tarafından kullanıcı arabirimi testi, hata ayıklama ve otomasyon için kullanılır.

Genel bakış

winapp ui, Windows uygulama URI'lerini incelemeye ve bunlarla etkileşime başlamaya yönelik komutlar sağlar. Windows UI Otomasyonu (UIA) kullanır. WPF, WinForms, Win32, Electron ve WinUI 3 gibi tüm Windows uygulamalarıyla çalışır. Komutların çoğu uygulamayı UIA desenleri aracılığıyla yönlendirir (giriş ekleme yoktur); ui click özel durumdur ve desteklemeyen InvokePatterndenetimler için gerçek fare benzetimi kullanır.

Hızlı Başlangıç

# 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

Uygulamaları Hedefleme

İşlem adına göre

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

Pencere başlığına göre

winapp ui inspect -a "LICENSE - Notepad"
winapp ui inspect -a "Fix WinApp"     # partial title match

PID tarafından

winapp ui inspect -a 12345

HWND tarafından (kararlı — sekme/başlık değişikliklerinin devamı)

# 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

Bulma için, -w kararlı hedefleme için kullanın-a. Birden çok pencereyle eşleştiğinde -a , komut bunları seçmeniz için HWND'lerle listeler.

Seçiciler

İnceleme/arama çıkışında [brackets] gösterilen seçiciyi kullanarak öğeleri hedefleyebilirsiniz. Üç tür seçici vardır:

Selector Anlamı Example
MinimizeButton AutomationId (benzersiz olduğunda gösterilir — kararlı, tercih edilen) winapp ui invoke MinimizeButton -a myapp
btn-close-d1a0 Anlamsal bilgi (benzersiz AutomationId olmadığında gösterilir) winapp ui invoke btn-close-d1a0 -a myapp
Submit Name/AutomationId 'ye karşı düz metin araması (büyük/küçük harfe duyarlı olmayan alt dize) winapp ui invoke Submit -a myapp

AutomationId seçicileri geliştirici kümesi tanımlayıcılarıdır (AutomationProperties.AutomationId XAML'de). AutomationId kullanıcı arabirimi ağacının inspect tamamında benzersiz olduğunda ve search bunu doğrudan seçici olarak gösterdiğinde, bunlar düzen değişiklikleri, yerelleştirme ve ağaç yeniden yapılandırmasından sonra kalır.

Benzersiz AutomationId olmadığında slug seçicileri (örn. btn-close-d1a0) oluşturulur. Biçim: prefix-name-hash. Karma öğe kimliğini doğrular, ancak kullanıcı arabirimi değiştikten sonra eskiyebilir.

Çıkış biçimini inceleme

Komut, inspect renkli çıkışlı öğe ağacını gösterir (mavi seçici, yeşil ad, gri meta veriler):

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

Her satırdaki ilk sözcük seçicidir; bunu diğer ui komutlarla kullanın. Bir öğe benzersiz bir AutomationId'ye sahip olduğunda, doğrudan kullanılır (örneğin, TabView, NewTabButton). Benzersiz AutomationId olmadığında, oluşturulan bir bilgi kullanılır (örn. tab-newtab-5f5b).

Anlamsal sümüklü böcekler

Bilgi kümeleri şu biçimi kullanır: prefix-normalizedname-hash burada:

  • ön ek — 3 harfli tür kısaltması (btn, txt, chk, cmb, itm, tab, img, lbl, pn, win, grp, lnk, mnu vb.)
  • normalizedname — AutomationId (tercih edilen) veya Ad,en fazla 15 karakterden küçük alfasayısal
  • hash — öğenin RuntimeId değerinin 4 karakterli onaltılık karması (öğe kimliğini doğrular)

Bilgi kümeleri kabuk açısından güvenlidir (özel karakter yoktur), benzersizdir ve doğrudan bağımsız değişken olarak kullanılabilir. Karma eskime algılaması sağlar; öğe değiştirildiyse şunu alırsınız: "Öğe değişmiş olabilir. İncelemeyi yeniden çalıştırın."

Name veya AutomationId içermeyen öğeler yalnızca ön ek + karma (örn. pn-c8a3) gösterir.

Birden çok eşleşmeyi kesinleştirme

Çıkıştan alınan inspect/search bilgi kümeleri benzersizdir, ancak düzen değişiklikleri arasında değişiklik yapabilir; bunları birden çok eşleşme olduğunda düz tür adları veya metinler üzerinde kullanın. Seçici belirsiz olduğunda, CLI tüm eşleşmeleri kendi sümüklüböcekleriyle yazdırır, böylece doğru olanı seçip bu bilgiyle yeniden çalıştırabilirsiniz.

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

Öğeleri aramak için düz metin kullanın; özel söz dizimi gerekmez:

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

Bir metin araması birden çok öğeyle eşleştiğinde (ör. Grup, Düğme ve Metin'in aynı adı paylaştığı SettingsExpander), CLI otomatik olarak tek çağrılabilen öğeyi seçer. Birden çok çağrılabilirse, tüm eşleşmeleri sümüklü böceklerle listeler.

Çağrılamaz arama sonuçları için (örneğin, bir Düğmenin içindeki TextBlock), arama otomatik olarak en yakın çağrılabilen üst öğeyi ( ile invokekullanabileceğiniz üst öğe) ortaya çıkar. Bu, tüm arama seçicileri için çalışır:

  lbl-savechanges-a1b2 "Save changes" (120,40 80x20)
        ^ invoke via: btn-save-c3d4 "Save"

Yüzey seçici doğrudan kullanılabilir:

winapp ui invoke btn-save-c3d4 -a myapp    # invoke the parent Button

Commands

statü

Bir uygulamaya bağlanın ve bağlantı bilgilerini gösterin.

winapp ui status -a notepad
winapp ui status -a notepad --json

Incelemek

Kullanıcı arabirimi öğesi ağacını görüntüleyin. Çıktı, hiyerarşi için 2 boşluk girintili anlamsal bilgi kümelerini gösterir:

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

Örnek çıkış (varsayılan):

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)

Örnek çıkış (--interactive — yalnızca çağrılabilen öğeler, düz liste):

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)

Öğeler şu durum işaretleyicilerini gösterebilir:

  • [on] / [off] / [indeterminate] — geçiş/onay kutusu durumu
  • [collapsed] / [expanded] — ağaçlar, birleşik giriş kutuları, menü öğeleri için genişletme/daraltma durumu
  • [scroll:v] / [scroll:h] / [scroll:vh] — kaydırılabilir kapsayıcı (dikey, yatay veya her ikisi)
  • [offscreen] — öğe ekranda görünmüyor
  • [disabled] — öğesi etkinleştirilmedi
  • value="..." — düzenlenebilir öğeler için geçerli metin içeriği (Ad'dan farklı olduğunda)

Seçiciyle eşleşen öğeleri bulun. Çıkışta semantik bilgi kümeleri gösterilir:

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

Örnek çıkış:

  btn-minimize-d1a0 "Minimize" (1222,206 48x48)
  btn-maximize-e2b1 "Maximize" (1270,206 48x48)
  btn-close-d1a2 "Close" (1318,206 48x48)

Çıkışta gösterilen bilgi kümeleri (örneğin, btn-minimize-d1a0) doğrudan diğer komutlarla kullanılabilir:

winapp ui invoke btn-minimize-d1a0 -a notepad

get-property

Öğeden özellik değerlerini okuma. Desene özgü durumu (ToggleState, Value, IsSelected vb.) içerir.

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

ekran görüntüsü

Bir pencereyi veya öğeyi PNG olarak yakalayın. Birden çok pencere mevcut olduğunda (örneğin, uygulama + açık iletişim kutusu), her pencere birleştirilmiş olarak tek bir PNG'de birleştirilir.

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)

İletişim kutuları veya açılır pencereler açıkken, tüm pencereler tek bir PNG'de birleştirilir, böylece tüm kullanıcı arabirimi durumunu tek bir görüntüde görebilirsiniz.

Açılır menüleri, açılan menüleri, açılır pencereleri veya araç ipucu katmanlarını yakalamanız gerektiğinde kullanın --capture-screen . Modunda --capture-screen (ve boş çerçeve algılandıktan sonra yeniden denenirken) hedef pencere önce ön plana getirilir; normal pencere yakalamaları pencereyi taşımaz.

Çağırmak

Bir öğeyi program aracılığıyla etkinleştirin (düğmeye tıklayın, onay kutusunu değiştir, birleşik giriş kutusunu genişlet).

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

Düzenleri sırayla dener: InvokePattern → TogglePattern → SelectionItemPattern → ExpandCollapsePattern.

click

Fare benzetimini kullanarak ekran koordinatlarında bir öğeye tıklayın. Desteklemeyen InvokePattern denetimler için bunu kullanın (örneğin, sütun üst bilgileri, liste öğeleri).

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üzenlenebilir bir öğede bir değer ayarlayın (TextBox/ComboBox metni, Kaydırıcı için sayı).

winapp ui set-value txt-textbox-a4b1 "Hello world" -a notepad
winapp ui set-value sld-volume-b2c3 75 -a myapp

get-value

Bir öğeden geçerli değeri okuyun. Akıllı geri dönüş zinciri kullanır: TextPattern (RichEditBox, Document) → ValuePattern (TextBox, Slider) → SelectionPattern (ComboBox, RadioButton, TabView) → Name (etiketler).

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

Klavye odağını bir öğeye taşıma.

winapp ui focus txt-textbox-a4b1 -a notepad

görünüme kaydırma

Öğeyi görünür alana kaydırın.

winapp ui scroll-into-view itm-targetitem-c3d4 -a myapp

bekleme

Öğenin görünmesini, kaybolmasını veya bir değerin hedefe ulaşmasını bekleyin.

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

Kaydırma

Kapsayıcı öğesini kaydırma. Kaydırılabilir kapsayıcıları search scroll bulun; (dikey) veya [scroll:h] (yatay) işaretçileri arayın [scroll:v] .

# 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

odaklan

Şu anda klavye odağı olan öğeyi gösterin.

winapp ui get-focused -a myapp

list-windows

Açılır pencereler ve iletişim kutuları da dahil olmak üzere bir uygulama için tüm görünür pencereleri listeleyin.

winapp ui list-windows -a imageresizer
winapp ui list-windows -a Terminal
winapp ui list-windows                                      # all windows (no filter)

Çerçeve Desteği

Çerçeve Incelemek search Çağırmak set-value ekran görüntüsü
WPF ✅ Tam ağaç ✅ Tüm özellikler ✅ Tüm desenler
WinForms
Win32
WinUI 3
Elektron ⚠️ Krom ağacı ⚠️ Sınırlı ⚠️ Değişkenlik gösterir ⚠️ Değişkenlik gösterir
Flutter ⚠️ Temel ⚠️ Temel ❌ Minimal

Sorun giderme

Error Nedeni Çözüm
"Çalışan uygulama bulunamadı" Uygulama çalışmıyor veya ad uyuşmazlığı İşlem adını denetleme veya PID kullanma
"Birden çok pencere eşleşmesi" Belirsiz -a değer Listelenen seçeneklerden kullanın -w <HWND>
"birden çok pencere var" İşlemin birden çok penceresi var Belirli birini hedeflemek için kullanın -w <HWND>
"Seçici N öğeleriyle eşleşmiş" Belirsiz eski seçici Çıkıştan inspect gelen bilgi kümelerini kullanın veya eski seçicilere ekleyin[0]. [1]
"Öğe değişmiş olabilir" Bilgi karması geçerli öğeyle eşleşmiyor Yeniden çalıştırın inspect veya search yeni sümüklü böcekler almak için
"herhangi bir çağırma düzenini desteklemiyor" Öğe çağrılamıyor Çağrılabilen bir alt öğeyi bulmak için öğesinde kullanın inspect
"UIA penceresi bulunamadı" UIA işlemi göremiyor HWND'yi bulmak için kullanın list-windows ve ardından -w
"Pencerenin boyutu sıfır" Pencere simge durumuna küçültüldü Uygulama otomatik olarak geri yüklenecek
Açılan/açılan liste ekran görüntüsünde yok PrintWindow katman yakalamaz Bayrağı kullan --capture-screen

Ortak Desenler

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

Metin bulma ve üst öğesini çağırma

# 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

Yinelenen öğeleri kesinleştirme

winapp ui search '#Image' -a myapp; winapp ui invoke itm-image-a2b3 -a myapp

Açılır yer paylaşımlı ekran görüntüsü

winapp ui set-value txt-searchbox-e5f6 "query" -a myapp; winapp ui screenshot -a myapp --capture-screen
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

Bulma, tıklama ve doğrulama

winapp ui inspect -a myapp --interactive; winapp ui invoke btn-submit-7a90 -a myapp; winapp ui screenshot -a myapp

Dosya iletişim kutusu etkileşimi

Dosya açma/kaydetme iletişim kutuları, UIA desteğine sahip standart Windows iletişim kutularıdır:

# 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>

Belirli bir iletişim kutusunun gerçek bilgi kümelerini bulmak için kullanın inspect -w <dialog-hwnd> --interactive .

Zincirleme için neden ; (değil &&)

Yerel bir CLI stderr'a && yazdığında veya ANSI kaçış dizileri kullandığında PowerShell işleci donabilir. Bunun yerine kullanın ; ; her komutu koşulsuz olarak çalıştırır ve bu kilitlenmeyi önler. Bu, aracı iş akışları için de daha iyidir: Genellikle çağrı sıfır olmayan bir çıkışa sahip olsa bile ekran görüntüsünün çalışmasını istersiniz.

CI Test Desenleri

Duman testleri ve kullanıcı arabirimi doğrulaması için CI işlem hatlarında (GitHub Actions, Azure DevOps) winapp ui komutlarını kullanın. wait-for --value ve --property onay işlevi görür; zaman aşımında 1 çıkış kodunu döndürür ve CI adımı otomatik olarak başarısız olur.

GitHub Actions'da başlatma ve test

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

ile assert öğesi durumu wait-for

wait-for --value ( TextPattern → ValuePattern → SelectionPattern → Name) ile aynı akıllı geri dönüş get-value kullanarak öğenin değeri beklenen dizeyle eşleşene kadar yoklar. Eşleşmede 0 çıkış kodu, zaman aşımında çıkış kodu 1'i döndürür; bu da ci kullanımı kolay bir onaydır. Bunun yerine belirli bir UIA özelliğini denetlemek için kullanın --property .

# 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

JSON çıkışıyla onaylama

Daha karmaşık onaylar için PowerShell veya jq ile kullanın --json :

ve wait-for modunda --json için search çıkış kodu sözleşmesi: Hiçbir öğe eşleşmediğinde (search) veya bekleme zaman aşımına (wait-for), komut stdout'a ({ "matchCount": 0, ... } veya { "found": false, "timedOut": true, ... }) tam olarak ayrıştırılabilir bir sonuç zarfı yazar ve çıkış kodu 1'i döndürür. Stderr modunda --json boş (günlükçü çıkışı gizlendi). Zarf alanlarında dallanma veya $LASTEXITCODEdaha ergonomik olduğuna bağlı olarak üzerinde dallanma.

# 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)" }

Tam duman testi örneği

# 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