winui-ui-testing
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseScope — any Windows app
适用范围——所有Windows应用
winapp uiLostFocuswinapp uiLostFocusApproach
测试方法
The goal of this skill is to validate UI and app functionality automatically, without manual interaction, by exercising the app's UI elements, verifying their state, and asserting that the app behaves as expected under test conditions.
There are two main approaches:
- Interactive exploration — manually run the app, use to explore the UI tree, find AutomationIds, verify element properties, and test functionality interactively. This is useful for discovery, but slow and expensive if repeated for every test iteration.
winapp ui <command> - Scripted batch testing — generate a script that exercises all UI elements and asserts expected behavior in one pass. This allows you to run the tests automatically, capture results, and iterate quickly without manually interacting with the app each time.
ui-tests.ps1
Unless the user asked for interactive exploration, or you are unfamiliar with the code/app or need to explore the UI tree to discover AutomationIds for hidden or dynamically generated elements (flyouts, dialogs, lazy-loaded content), prefer scripted batch testing — it is faster, repeatable, and produces a record of pass/fail results that can be reviewed and acted on.
本技能的目标是通过操作应用UI元素、验证元素状态、断言应用在测试条件下的预期行为,实现UI与应用功能的自动化验证,无需人工交互。
主要有两种测试方式:
- 交互式探索——手动运行应用,使用探索UI树、查找AutomationId、验证元素属性并交互式测试功能。这种方式适用于功能探索,但如果每次测试迭代都重复操作,会非常耗时低效。
winapp ui <command> - 脚本化批量测试——生成脚本,一键完成所有UI元素的操作与预期行为断言。这种方式支持自动化运行测试、捕获结果,无需每次手动操作应用即可快速迭代。
ui-tests.ps1
除非用户明确要求交互式探索,或者你不熟悉代码/应用,需要探索UI树来发现隐藏或动态生成元素(弹出菜单、对话框、懒加载内容)的AutomationId,否则优先选择脚本化批量测试——它速度更快、可重复执行,还能生成可查看和跟进的测试通过/失败记录。
winapp ui
Verbs
winapp uiwinapp ui
命令动词
winapp ui- Query: ,
status,list-windows,inspect,search,get-property,get-value,get-focusedwait-for - Interact: ,
invoke,click,set-value,focus,scrollscroll-into-view - Advanced input: (synthetic keyboard + accelerators),
send-keys(tooltips/flyouts),hover(drag-drop, reorder, sliders),drag(tap/swipe/pinch/stretch),touch(stylus ink, pressure/tilt/eraser)pen - Capture: ,
screenshot(H.264 MP4 video)record
Run for the complete command structure as JSON, or for any single verb.
winapp ui --cli-schemawinapp ui <verb> --help- 查询类:、
status、list-windows、inspect、search、get-property、get-value、get-focusedwait-for - 交互类:、
invoke、click、set-value、focus、scrollscroll-into-view - 高级输入类:(模拟键盘+快捷键)、
send-keys(触发工具提示/弹出菜单)、hover(拖放、重排序、滑块操作)、drag(点击/滑动/捏合/拉伸)、touch(手写笔输入、压力/倾斜/擦除功能)pen - 捕获类:、
screenshot(生成H.264 MP4视频)record
运行可获取完整的JSON格式命令结构,或运行查看单个命令动词的详细说明。
winapp ui --cli-schemawinapp ui <verb> --helpStep 1: Use the Running App
步骤1:使用已运行的应用
If the app is already running, use its PID. Do NOT relaunch — use the PID already captured from the build step. If the app is not running, build and launch it using the guidance in the winui-dev-workflow skill.
如果应用已在运行,使用其PID。请勿重启应用——使用构建步骤中已捕获的PID即可。如果应用未运行,请按照winui-dev-workflow技能中的指导构建并启动应用。
Step 2: Write the Test Script
步骤2:编写测试脚本
If you wrote the code: Skip inspect — you already know all the AutomationIds and control structure from the XAML and code-behind. Write tests directly from that knowledge. Inspect misses popups, flyouts, dialogs, and lazy-loaded content anyway.
If you're verifying code you didn't write: Run inspect first to discover the UI:
powershell
winapp ui inspect -a <PID> --interactiveThen read the XAML files to find AutomationIds that aren't currently visible (flyout items, dialog buttons, secondary pages).
Create a file that tests all the app's requirements in one pass:
ui-tests.ps1powershell
undefined如果你是代码开发者:跳过inspect操作——你已经从XAML和代码后置文件中了解所有AutomationId和控件结构。直接基于这些知识编写测试即可。Inspect工具会遗漏弹窗、弹出菜单、对话框和懒加载内容。
如果你要验证他人编写的代码:先运行inspect操作探索UI:
powershell
winapp ui inspect -a <PID> --interactive然后读取XAML文件,查找当前不可见元素(弹出菜单项、对话框按钮、二级页面)的AutomationId。
创建文件,一次性测试应用的所有需求:
ui-tests.ps1powershell
undefinedui-tests.ps1
ui-tests.ps1
param([Parameter(Mandatory)][int]$AppPid)
param([Parameter(Mandatory)][int]$AppPid)
NOTE: Do NOT name the parameter $Pid — it's read-only in PowerShell
注意:请勿将参数命名为$Pid——它是PowerShell中的只读自动变量
$ErrorActionPreference = 'Continue'
$pass = 0; $fail = 0; $results = @()
$ErrorActionPreference = 'Continue'
$pass = 0; $fail = 0; $results = @()
Get main window HWND (avoids PopupHost interference with JSON parsing)
获取主窗口HWND(避免PopupHost干扰JSON解析)
$windows = winapp ui list-windows -a $AppPid --json 2>$null | ConvertFrom-Json
$hwnd = ($windows | Where-Object { $_.title -ne "PopupHost" } | Select-Object -First 1).hwnd
function Test-UI {
param([string]$Name, [scriptblock]$Script)
# IMPORTANT: Inside $Script, use 'throw' to signal failure — NOT 'exit 1'
# (exit terminates the entire script, not just the test)
try {
$output = & $Script 2>&1
if ($LASTEXITCODE -eq 0) {
$script:pass++; $script:results += @{ name = $Name; status = "PASS" }
} else {
$script:fail++; $script:results += @{ name = $Name; status = "FAIL"; detail = "$output" }
}
} catch {
$script:fail++; $script:results += @{ name = $Name; status = "FAIL"; detail = "$_" }
}
}
$windows = winapp ui list-windows -a $AppPid --json 2>$null | ConvertFrom-Json
$hwnd = ($windows | Where-Object { $_.title -ne "PopupHost" } | Select-Object -First 1).hwnd
function Test-UI {
param([string]$Name, [scriptblock]$Script)
# 重要提示:在$Script内,使用'throw'表示测试失败——不要使用'exit 1'
# (exit会终止整个脚本,而不仅是当前测试)
try {
$output = & $Script 2>&1
if ($LASTEXITCODE -eq 0) {
$script:pass++; $script:results += @{ name = $Name; status = "PASS" }
} else {
$script:fail++; $script:results += @{ name = $Name; status = "FAIL"; detail = "$output" }
}
} catch {
$script:fail++; $script:results += @{ name = $Name; status = "FAIL"; detail = "$_" }
}
}
─── Element Existence ───
─── 元素存在性测试 ───
Test-UI "NavHome exists" { winapp ui wait-for "NavHome" -a $AppPid -t 3000 }
Test-UI "NavSettings exists" { winapp ui wait-for "NavSettings" -a $AppPid -t 3000 }
Test-UI "导航主页存在" { winapp ui wait-for "NavHome" -a $AppPid -t 3000 }
Test-UI "导航设置页存在" { winapp ui wait-for "NavSettings" -a $AppPid -t 3000 }
─── Navigation ───
─── 导航测试 ───
Test-UI "Navigate to Settings" { winapp ui invoke "NavSettings" -a $AppPid }
Test-UI "Settings page loaded" { winapp ui wait-for "TxtUserName" -a $AppPid -t 3000 }
Test-UI "跳转到设置页" { winapp ui invoke "NavSettings" -a $AppPid }
Test-UI "设置页加载完成" { winapp ui wait-for "TxtUserName" -a $AppPid -t 3000 }
─── Interactions ───
─── 交互操作测试 ───
Test-UI "Set username" { winapp ui set-value "TxtUserName" "TestUser" -a $AppPid }
Test-UI "Click Save" { winapp ui invoke "BtnSave" -a $AppPid } # commits the TextBox binding
Test-UI "Username value set" {
winapp ui wait-for "TxtUserName" -a $AppPid --value "TestUser" -t 2000
}
Test-UI "设置用户名" { winapp ui set-value "TxtUserName" "TestUser" -a $AppPid }
Test-UI "点击保存按钮" { winapp ui invoke "BtnSave" -a $AppPid } # 提交TextBox绑定值
Test-UI "用户名已设置" {
winapp ui wait-for "TxtUserName" -a $AppPid --value "TestUser" -t 2000
}
─── Value assertions for different control types ───
─── 不同控件类型的值断言测试 ───
Test-UI "Theme is System default" {
winapp ui wait-for "CmbTheme" -a $AppPid --value "System default" -t 2000
}
Test-UI "Logging is off" {
winapp ui wait-for "TglLogging" -a $AppPid --value "Off" -t 2000
}
Test-UI "主题为系统默认" {
winapp ui wait-for "CmbTheme" -a $AppPid --value "System default" -t 2000
}
Test-UI "日志功能已关闭" {
winapp ui wait-for "TglLogging" -a $AppPid --value "Off" -t 2000
}
─── Accessibility Audit ───
─── 可访问性审计 ───
Only audit controls in the app's main window (exclude OS picker/popup controls)
仅审计应用主窗口内的控件(排除系统选择器/弹窗控件)
$allElements = (winapp ui inspect -a $AppPid --interactive --json 2>$null | ConvertFrom-Json).elements
$appElements = @($allElements | Where-Object {
$.type -match 'Button|TextBox|ComboBox|CheckBox|ToggleSwitch|TabItem|Edit' -and
$.name -notmatch 'Minimize|Maximize|Close|System' -and # window chrome
$.className -notmatch 'PickerHost|#32770|CabinetWClass' # OS dialogs
})
$missingId = @($appElements | Where-Object { -not $.automationId })
if ($missingId.Count -eq 0) {
$pass++; $results += @{ name = "All app controls have AutomationId"; status = "PASS" }
} else {
$fail++
$names = ($missingId | ForEach-Object { "$($.type) '$($.name)'" }) -join ", "
$results += @{ name = "AutomationId coverage"; status = "FAIL"; detail = "Missing: $names" }
}
$allElements = (winapp ui inspect -a $AppPid --interactive --json 2>$null | ConvertFrom-Json).elements
$appElements = @($allElements | Where-Object {
$.type -match 'Button|TextBox|ComboBox|CheckBox|ToggleSwitch|TabItem|Edit' -and
$.name -notmatch 'Minimize|Maximize|Close|System' -and # 窗口边框控件
$.className -notmatch 'PickerHost|#32770|CabinetWClass' # 系统对话框
})
$missingId = @($appElements | Where-Object { -not $.automationId })
if ($missingId.Count -eq 0) {
$pass++; $results += @{ name = "所有应用控件均包含AutomationId"; status = "PASS" }
} else {
$fail++
$names = ($missingId | ForEach-Object { "$($.type) '$($.name)'" }) -join ", "
$results += @{ name = "AutomationId覆盖率"; status = "FAIL"; detail = "缺失:$names" }
}
─── State Screenshots (capture each meaningful state for visual review) ───
─── 状态截图(捕获每个关键状态用于视觉审查) ───
New-Item -ItemType Directory -Force -Path "screenshots" | Out-Null
winapp ui screenshot -a $AppPid -o "screenshots/01-initial.png" 2>$null
New-Item -ItemType Directory -Force -Path "screenshots" | Out-Null
winapp ui screenshot -a $AppPid -o "screenshots/01-initial.png" 2>$null
...take more screenshots after key interactions above (mode switches, dialogs opened, etc.)
...在上述关键交互后截取更多截图(模式切换、对话框打开等场景)
─── Final Screenshot ───
─── 最终截图 ───
winapp ui screenshot -a $AppPid -o "test-screenshot.png" 2>$null
winapp ui screenshot -a $AppPid -o "test-screenshot.png" 2>$null
─── Results ───
─── 测试结果 ───
Write-Host "`nPassed: $pass | Failed: $fail"
$results | Where-Object { $.status -eq "FAIL" } | ForEach-Object {
Write-Host " FAIL: $($.name) — $($_.detail)" -ForegroundColor Red
}
$results | ConvertTo-Json | Out-File "test-results.json"
if ($fail -gt 0) { exit 1 } else { exit 0 }
undefinedWrite-Host "`n通过:$pass | 失败:$fail"
$results | Where-Object { $.status -eq "FAIL" } | ForEach-Object {
Write-Host " 失败:$($.name) — $($_.detail)" -ForegroundColor Red
}
$results | ConvertTo-Json | Out-File "test-results.json"
if ($fail -gt 0) { exit 1 } else { exit 0 }
undefinedWhat to Test
测试覆盖范围
Write tests for every requirement from the user's prompt:
| Requirement type | Test approach |
|---|---|
| "Has a button that does X" | |
| "Text field shows value" | |
| "Status bar contains text" | |
| "Dropdown is set to X" | |
| "Toggle is on/off" | |
| "Navigation between pages" | |
| "Open file dialog" | |
| "Save file dialog" | Same as open — find picker with |
| "Right-click context menu" | |
| "Keyboard shortcut (Ctrl+S, etc.)" | |
| "Type into a TextBox/RichEditBox" | |
| "Tooltip / hover flyout appears" | |
| "Drag to reorder / resize / slider" | |
| "Touch gesture (swipe/pinch/stretch)" | |
| "Capture a repro clip of a flow" | |
| "Confirmation dialog" | |
| "Data persists" | Set values, |
| "All controls accessible" | |
针对用户需求中的每一项要求编写测试:
| 需求类型 | 测试方法 |
|---|---|
| "存在执行X操作的按钮" | 使用 |
| "文本字段显示指定值" | |
| "状态栏包含指定文本" | |
| "下拉框已设置为X" | |
| "开关处于开/关状态" | |
| "页面间导航" | |
| "打开文件对话框" | |
| "保存文件对话框" | 与打开对话框操作相同 — 使用 |
| "右键上下文菜单" | |
| "键盘快捷键(如Ctrl+S)" | |
| "在TextBox/RichEditBox中输入文本" | |
| "工具提示/悬停弹出菜单显示" | |
| "拖放重排序/调整大小/滑块操作" | |
| "触摸手势(滑动/捏合/拉伸)" | |
| "捕获流程复现视频" | |
| "确认对话框" | |
| "数据持久化" | 设置值, |
| "所有控件可访问" | |
Step 3: Run and Read Results
步骤3:运行测试并查看结果
powershell
.\ui-tests.ps1 -AppPid <PID>Read for structured pass/fail. Only fix code if tests fail.
test-results.jsonpowershell
.\ui-tests.ps1 -AppPid <PID>查看获取结构化的测试通过/失败结果。仅当测试失败时才需要修复代码。
test-results.jsonStep 3.5: Look at the Screenshots
步骤3.5:查看截图
UIA assertions don't see clipping, overlap, wrong theming, or controls bleeding past their container — UIA returns while the app is visually broken. Capture screenshots with and view each PNG.
PASSwinapp ui screenshotCapture the initial state and any state after a major interaction (the State Screenshots block in the script template above handles this).
Visual checklist — fail the run if any item is :
no- No unintended scrollbars
- No text ending in that shouldn't be
… - Hero elements fully visible (not sliced)
- Right-edge controls fully visible
- No overlapping rows
- Content uses the available width — no asymmetric dead zones (e.g. content pinned to one edge leaving empty space on the other)
- Spacing intentional — not cramped, not unintentionally vast
- Theming matches the user's ask (Light/Dark/HighContrast if relevant)
- Focus/hover/error states render if tested
If the checklist fails, it's a bug — fix before declaring done. Window too small → grow per Step 4.
winui-designUIA断言无法检测到控件裁剪、重叠、主题错误或控件超出容器边界等问题——即使应用视觉上已损坏,UIA仍会返回。务必使用捕获截图并查看每张PNG图片。
PASSwinapp ui screenshot捕获初始状态以及每次重大交互后的状态(上述脚本模板中的「状态截图」模块已处理此需求)。
视觉检查清单——若任意项为「否」则判定测试失败:
- 无意外出现的滚动条
- 不应显示省略号的文本未以结尾
… - 核心元素完全可见(未被截断)
- 右侧边缘控件完全可见
- 无重叠行
- 内容充分利用可用宽度——无不对称空白区域(如内容固定在一侧,另一侧留有大量空白)
- 间距合理——既不拥挤也不过于宽松
- 主题符合用户要求(若相关则检查浅色/深色/高对比度模式)
- 测试的焦点/悬停/错误状态正常渲染
如果清单中有未通过项,则属于bug——修复后再完成测试。若窗口过小,请按照步骤4调整窗口大小。
winui-designStep 4: Fix and Rerun (if the user asked for it)
步骤4:修复并重新运行(若用户要求)
If tests fail:
- Read the failure details from
test-results.json - Batch-fix all issues in one pass
- Rebuild with (blocking mode — shows crash info if the fix broke something)
.\BuildAndRun.ps1 - Rerun (parse PID from the
.\ui-tests.ps1 -AppPid <PID>output)launched (PID: XXXXX)
Maximum 2 fix-and-rerun cycles. If the same tests keep failing after 2 cycles, report them as known issues and move on — do not keep iterating.
如果测试失败:
- 从中读取失败详情
test-results.json - 批量修复所有问题
- 使用重新构建应用(阻塞模式——若修复导致崩溃会显示错误信息)
.\BuildAndRun.ps1 - 重新运行(从
.\ui-tests.ps1 -AppPid <PID>输出中解析PID)launched (PID: XXXXX)
最多进行2次修复-重运行循环。如果经过2次循环后相同测试仍失败,将其报告为已知问题并继续后续工作——不要持续迭代。
Assertion Reference
断言参考
Use as the primary assertion — it uses a smart fallback chain that reads the right value for any control type:
wait-for --value| Control type | | Example |
|---|---|---|
| TextBlock / Label | Name property | |
| TextBox / NumberBox | ValuePattern | |
| RichEditBox | TextPattern | |
| ComboBox | Selected item (SelectionPattern) | |
| ToggleSwitch | Toggle state (On/Off) | |
| CheckBox | Toggle state (On/Off) | |
Full assertion commands:
| Assertion | Command |
|---|---|
| Element exists | |
| Element has exact value | |
| Value contains text | |
| Element gone | |
| Specific property | |
| Button clickable | |
| Set then verify | |
| Screenshot | |
| Dialog appeared | |
| Right-click menu | |
| Read raw property | |
| Read current value (no wait) | |
| Scroll item into view | |
| Set keyboard focus | |
| Type real keystrokes into a control | |
| Fire a keyboard accelerator/shortcut | |
| Hover to show tooltip/flyout | |
| Drag / reorder / slider gesture | |
| Touch gesture | |
| Pen / ink stroke | |
| Record a video clip | |
优先使用作为主要断言方式——它会智能选择合适的UIA模式读取值,适配所有控件类型:
wait-for --value| 控件类型 | | 示例 |
|---|---|---|
| TextBlock / Label | Name属性 | |
| TextBox / NumberBox | ValuePattern | |
| RichEditBox | TextPattern | |
| ComboBox | 选中项(SelectionPattern) | |
| ToggleSwitch | 开关状态(On/Off) | |
| CheckBox | 勾选状态(On/Off) | |
完整断言命令:
| 断言类型 | 命令 |
|---|---|
| 元素存在 | |
| 元素值完全匹配 | |
| 值包含指定文本 | |
| 元素已消失 | |
| 指定属性匹配 | |
| 按钮可点击 | |
| 设置值后验证 | |
| 截图 | |
| 对话框已弹出 | |
| 右键菜单 | |
| 读取原始属性 | |
| 读取当前值(无需等待) | |
| 将元素滚动到视图中 | |
| 设置键盘焦点 | |
| 向控件输入真实按键 | |
| 触发键盘快捷键 | |
| 悬停显示工具提示/弹出菜单 | |
| 拖放/重排序/滑块操作 | |
| 触摸手势 | |
| 手写笔输入 | |
| 录制视频 | |
Testing File Pickers
文件选择器测试
File/folder pickers (FileOpenPicker, FileSavePicker, FolderPicker) run in a separate process but are fully interactable. The picker appears as an owned dialog window.
PickerHostpowershell
undefined文件/文件夹选择器(FileOpenPicker、FileSavePicker、FolderPicker)运行在独立的进程中,但完全支持交互操作。选择器会作为应用窗口的从属对话框显示。
PickerHostpowershell
undefined1. Trigger the picker
1. 触发选择器
winapp ui invoke "BtnOpenFile" -a $AppPid
winapp ui invoke "BtnOpenFile" -a $AppPid
2. Find the picker window (it's a dialog owned by the app window)
2. 查找选择器窗口(它是应用窗口的从属对话框)
Start-Sleep 1
$allWindows = winapp ui list-windows -a $AppPid --json 2>$null | ConvertFrom-Json
$picker = $allWindows | Where-Object { $_.title -match "Open|Save" }
$pickerHwnd = $picker.hwnd
Start-Sleep 1
$allWindows = winapp ui list-windows -a $AppPid --json 2>$null | ConvertFrom-Json
$picker = $allWindows | Where-Object { $_.title -match "Open|Save" }
$pickerHwnd = $picker.hwnd
3. Interact with the picker using -w <HWND>
3. 使用-w <HWND>参数与选择器交互
Type a filename:
输入文件名:
winapp ui set-value "FileNameControlHost" "test.txt" -w $pickerHwnd
winapp ui set-value "FileNameControlHost" "test.txt" -w $pickerHwnd
Click Open/Save:
点击打开/保存:
winapp ui invoke "Open" -w $pickerHwnd # or "Save", "Cancel"
winapp ui invoke "Open" -w $pickerHwnd # 或"Save"、"Cancel"
Or cancel:
或取消:
winapp ui invoke "Cancel" -w $pickerHwnd
winapp ui invoke "Cancel" -w $pickerHwnd
4. Verify the app processed the file
4. 验证应用已处理文件
winapp ui wait-for "StatusBar" -a $AppPid -p Name --value "opened" -t 3000
**Tip:** Use `winapp ui inspect -w <pickerHwnd> --interactive` to discover the picker's controls — they include the folder tree, file list, filename textbox, and Open/Cancel buttons.winapp ui wait-for "StatusBar" -a $AppPid -p Name --value "opened" -t 3000
**提示**:使用`winapp ui inspect -w <pickerHwnd> --interactive`探索选择器的控件——包括文件夹树、文件列表、文件名文本框以及打开/取消按钮。Testing Context Menus and Flyouts
上下文菜单与弹出菜单测试
MenuFlyouts and ContextFlyouts are fully testable. They appear in the UI automation tree when open.
powershell
undefinedMenuFlyouts和ContextFlyouts完全可测试。它们打开后会立即出现在UI自动化树中。
powershell
undefined1. Right-click to open a ContextFlyout
1. 右键点击打开ContextFlyout
winapp ui click "LstItems" -a $AppPid --right
Start-Sleep 0.5
winapp ui click "LstItems" -a $AppPid --right
Start-Sleep 0.5
2. The flyout MenuItems appear in the tree immediately
2. 弹出菜单项会立即出现在树中
Find them with inspect or search:
使用inspect或search查找:
winapp ui inspect -a $AppPid --interactive # shows MnuCopy, MnuDelete, etc.
winapp ui inspect -a $AppPid --interactive # 会显示MnuCopy、MnuDelete等项
3. Click a flyout item
3. 点击弹出菜单项
winapp ui invoke "MnuCopy" -a $AppPid
winapp ui invoke "MnuCopy" -a $AppPid
4. Verify the action
4. 验证操作结果
winapp ui wait-for "StatusText" -a $AppPid -p Name --value "Copied" -t 2000
**For MenuBar flyouts** (File, Edit, View menus):
```powershellwinapp ui wait-for "StatusText" -a $AppPid -p Name --value "Copied" -t 2000
**对于MenuBar弹出菜单**(文件、编辑、视图菜单):
```powershellClick the menu header to open
点击菜单标题打开
winapp ui invoke "FileMenu" -a $AppPid
Start-Sleep 0.5
winapp ui invoke "FileMenu" -a $AppPid
Start-Sleep 0.5
Click the sub-item
点击子项
winapp ui invoke "MenuSaveAs" -a $AppPid
undefinedwinapp ui invoke "MenuSaveAs" -a $AppPid
undefinedTesting ContentDialogs
ContentDialog测试
ContentDialogs are in-app controls (same window) — they appear directly in the UI tree when shown.
powershell
undefinedContentDialog是应用内控件(与主窗口同进程)——显示后会直接出现在UI树中。
powershell
undefined1. Trigger the dialog
1. 触发对话框
winapp ui invoke "BtnDelete" -a $AppPid
Start-Sleep 0.5
winapp ui invoke "BtnDelete" -a $AppPid
Start-Sleep 0.5
2. The dialog buttons appear in the tree
2. 对话框按钮会出现在树中
For a standard confirmation dialog:
对于标准确认对话框:
winapp ui search "Primary" -a $AppPid --json # finds the primary button
winapp ui invoke "Primary" -a $AppPid # click "Yes"/"Delete"/"Save"
winapp ui search "Primary" -a $AppPid --json # 查找主按钮
winapp ui invoke "Primary" -a $AppPid # 点击"是"/"删除"/"保存"
Or:
或:
winapp ui invoke "Secondary" -a $AppPid # click "No"/"Don't Save"
winapp ui invoke "Close" -a $AppPid # click "Cancel"
winapp ui invoke "Secondary" -a $AppPid # 点击"否"/"不保存"
winapp ui invoke "Close" -a $AppPid # 点击"取消"
3. Wait for dialog to dismiss
3. 等待对话框关闭
winapp ui wait-for "Primary" -a $AppPid --gone -t 3000
**Tip:** ContentDialog buttons often don't have custom AutomationIds — use `inspect` to find the actual selector (slug or text match).winapp ui wait-for "Primary" -a $AppPid --gone -t 3000
**提示**:ContentDialog按钮通常没有自定义AutomationId——使用`inspect`查找实际选择器(别名或文本匹配)。Advanced Input: keyboard, hover, drag, touch & pen
高级输入:键盘、悬停、拖放、触摸与手写笔
Synthetic input beyond //. Each verb takes / like the rest.
invokeclickset-value-a <PID>-w <HWND>send-keysentertabf5ctrl+shift+tvk=0x42--via- (default) — HWND-targeted, no foreground needed; raises
post-messagebut not per-characterTextChanged.KeyDown - — OS-wide; real per-character
send-input+KeyDown. Required for accelerators/shortcuts (TextChanged, e.g.KeyboardAccelerator) and for reliable typing into a WinUI 3 / WPFctrl+t.TextBox
powershell
winapp ui send-keys "ctrl+s" -a $AppPid --via send-input # fire a Ctrl+S accelerator
winapp ui send-keys "hello world" --target "TxtName" -a $AppPid --via send-input # focus then type
winapp ui send-keys --verbatim "down down enter" -a $AppPid # type the words, not the keys--targettext=<literal>--verbatimwin+ralt+f4--allow-system-keys--via send-inputwin+lhover--dwell-timepowershell
winapp ui hover "BtnInfo" -a $AppPid
winapp ui wait-for "InfoTooltip" -a $AppPid -t 2000drag<from><to>x,yinspect--hold-ms--dwell-mspowershell
winapp ui drag "ItemA" "ItemB" -a $AppPid # reorder ItemA onto ItemB
winapp ui drag "SldVolume" 300,120 -a $AppPid # drag a slider thumb to a pointtouch-gtapdouble-taplong-pressswipepinchstretch--direction--distance--to-point--fingerspowershell
winapp ui touch "LstFeed" -g swipe --direction up --distance 400 -a $AppPid
winapp ui touch "ImgPhoto" -g stretch --distance 200 -a $AppPid # pinch-to-zoompen--path "x,y x,y …"--pressure--tilt-x--tilt-y--eraserpowershell
winapp ui pen "InkCanvas" --path "50,50 120,80 200,60" --pressure 0.8 -a $AppPid
winapp ui pen "InkCanvas" --path "50,50 200,60" --eraser -a $AppPid除//之外的模拟输入操作。每个命令动词都支持 / 参数,与其他命令一致。
invokeclickset-value-a <PID>-w <HWND>send-keysentertabf5ctrl+shift+tvk=0x42--via- (默认)——针对HWND发送消息,无需窗口处于前台;会触发
post-message事件,但不会触发逐字符的TextChanged事件。KeyDown - ——系统级输入;模拟真实的逐字符
send-input+KeyDown事件。对于快捷键(TextChanged,如KeyboardAccelerator)以及WinUI 3 / WPFctrl+t的可靠输入是必需的。TextBox
powershell
winapp ui send-keys "ctrl+s" -a $AppPid --via send-input # 触发Ctrl+S快捷键
winapp ui send-keys "hello world" --target "TxtName" -a $AppPid --via send-input # 先聚焦再输入
winapp ui send-keys --verbatim "down down enter" -a $AppPid # 输入文本而非按键--targettext=<文本内容>--verbatimwin+ralt+f4--allow-system-keys--via send-inputwin+lhover--dwell-timepowershell
winapp ui hover "BtnInfo" -a $AppPid
winapp ui wait-for "InfoTooltip" -a $AppPid -t 2000drag<from><to>x,y--hold-ms--dwell-mspowershell
winapp ui drag "ItemA" "ItemB" -a $AppPid # 将ItemA拖到ItemB位置重排序
winapp ui drag "SldVolume" 300,120 -a $AppPid # 将滑块拖动到指定坐标touch-gtapdouble-taplong-pressswipepinchstretch--direction--distance--to-point--fingerspowershell
winapp ui touch "LstFeed" -g swipe --direction up --distance 400 -a $AppPid
winapp ui touch "ImgPhoto" -g stretch --distance 200 -a $AppPid # 捏合缩放pen--path "x,y x,y …"--pressure--tilt-x--tilt-y--eraserpowershell
winapp ui pen "InkCanvas" --path "50,50 120,80 200,60" --pressure 0.8 -a $AppPid
winapp ui pen "InkCanvas" --path "50,50 200,60" --eraser -a $AppPidRecording a Video
视频录制
winapp ui record--duration-sec Npowershell
winapp ui record -a $AppPid --duration-sec 6 --fps 30 -o "flow.mp4"--max-edge N--capture-screenscreenshotwinapp ui record--duration-sec Npowershell
winapp ui record -a $AppPid --duration-sec 6 --fps 30 -o "flow.mp4"--max-edge N--capture-screenscreenshotKey Gotchas
关键注意事项
- does NOT commit default TextBox bindings — WinUI 3
set-valueon TextBox.Text updates the ViewModel onx:Bind TwoWayby default. UIALostFocuschanges the text but doesn't trigger focus events. Fix: apps should useset-valueon TextBox bindings (see design skill). If the app doesn't,UpdateSourceTrigger=PropertyChangeda button orinvoke/clickanother element afterfocusto triggerset-value.LostFocus - Set a with
RichEditBox, notsend-keys— WinUI 3set-value/ WPFRichEditBoxdon't support UIA value-setting.RichTextBox(orfocus), then--target— which also raises real per-keysend-keys "…" --via send-input, so use it whenever a control reacts to individual keystrokes (or aKeyDown) rather than a bulk value change.KeyboardAccelerator - Verify persistence via the data file, not UI relaunch — killing and relaunching a packaged app from a test script is fragile (MSIX registration timing, PID issues). Instead, check the data file on disk: and verify expected values.
Get-Content $dataFile | ConvertFrom-Json - Use not
$AppPid—$Pidis a read-only automatic variable in PowerShell$Pid - Use without
--value— it auto-detects the right UIA pattern (TextPattern → ValuePattern → TogglePattern → SelectionPattern → Name). Only use-pwhen you need a specific property like-p PropertyName --valueIsEnabled - File pickers need — they run in a separate PickerHost process, so
-w <HWND>won't find them. Use-a PIDto discover the picker HWND firstlist-windows - Flyouts need a short after triggering — the menu items appear in the tree asynchronously
Start-Sleep
- 不会提交默认TextBox绑定值——WinUI 3中TextBox.Text的
set-value绑定默认在x:Bind TwoWay事件时更新ViewModel。UIA的LostFocus会修改文本,但不会触发焦点事件。解决方法:应用应在TextBox绑定中使用set-value(参考设计技能)。如果应用未设置,在UpdateSourceTrigger=PropertyChanged后set-value按钮或invoke/click其他元素来触发focus事件。LostFocus - 使用设置RichEditBox值,而非
send-keys——WinUI 3set-value/ WPFRichEditBox不支持UIA值设置。先RichTextBox(或使用focus参数),然后执行--target——这也会触发真实的逐字符send-keys "…" --via send-input事件,因此当控件需要响应单个按键(或KeyDown)而非批量值更改时,应使用此方法。KeyboardAccelerator - 通过数据文件验证持久化,而非重启UI——从测试脚本中终止并重启打包应用容易出现问题(MSIX注册时序、PID问题)。相反,直接检查磁盘上的数据文件:并验证预期值。
Get-Content $dataFile | ConvertFrom-Json - 使用而非
$AppPid——$Pid是PowerShell中的只读自动变量$Pid - 使用不带参数的
-p——它会自动检测合适的UIA模式(TextPattern → ValuePattern → TogglePattern → SelectionPattern → Name)。仅当需要验证特定属性(如--value)时,才使用IsEnabled参数-p PropertyName --value - 文件选择器需要参数——它们运行在独立的PickerHost进程中,因此
-w <HWND>无法找到它们。需先使用-a PID查找选择器的HWNDlist-windows - 弹出菜单触发后需短暂——菜单项会异步出现在UI树中
Start-Sleep