PowerToys MouseUtils 模块 UI 自动化测试:发布测试清单迁移进度全解
本指南以仓库文档 Release-Test-Checklist-Migration-Progress.md 为骨架,结合 MouseUtils 四个工具模块的 UI 自动化测试源码,系统解读 MouseUtils(鼠标实用工具)发布测试清单向自动化用例迁移的覆盖情况、每一条手工验证步骤的自动化实现方式,以及尚未自动化的场景及其技术原因。读完你可完整掌握 Find My Mouse、Mouse Highlighter、Mouse Pointer Crosshairs、Mouse Jump 四模块"手工清单 → 自动化用例"的对应关系,理解其基于像素采样、窗口句柄附加与屏幕坐标计算等黑盒断言手法的实现原理,并据此评估与补齐你自己的 UI 回归覆盖。
一、文档定位:一份"迁移进行时"的覆盖矩阵
MouseUtils(鼠标实用工具集)是 Microsoft PowerToys 中与鼠标输入体验直接相关的一组工具模块,在源码中集中存放于 src/modules/MouseUtils 目录,其 UI 自动化测试工程为 MouseUtils.UITests。发布前,项目通常会有一份面向测试人员的手工发布测试清单,覆盖每个模块的启用/停用、激活方式、外观与行为设置等场景;而 UI 自动化测试的目标,就是把这些"由人按步骤手工验证"的条目一条条改写成可以自动执行的 MSTest 用例。
Release-Test-Checklist-Migration-Progress.md 正是这份迁移工作的进度台账:它以清单为底稿,用复选框标注每条手工验证条目当前是否已迁移为自动化用例:
[x]:该清单项已完成自动化迁移(并有对应测试方法或测试辅助逻辑,见下文映射);[ ]:该项仍待迁移/待补齐,属于后续工作的"欠账"。
全文档共 44 条验证点,其中 25 条已标记 [x](约 57%),19 条标记 [ ](约 43%)。文档开头的 Mouse Utils 是对共享清单模板中 "Mouse Utils" 一节的引用(该模板文件在当前仓库快照中并未随工程检出,因此本文以下内容以清单条目本身与可验证的测试源码为准)。
从测试工程属性看(MouseUtils.UITests.csproj),该工程基于 MSTest、引用通用 UI 自动化库 UITestAutomation,并显式关闭了 MSBuild 内嵌运行(<RunVSTest>false</RunVSTest>),说明它属于依赖真实 UI 会话、需要独立调度运行的 WinAppDriver/WinUI 会话级测试,而非普通的单元测试。
二、测试基础设施:清单条目是如何被自动执行的
在逐模块展开清单之前,先理解自动化用例复用的几块公共"脚手架",它们决定了清单里的条目能以多高的真实度被自动验证:
- 导航进入设置页:各测试类都实现了
LaunchFromSetting(),流程为——先通过RestartScopeExe(...)重启被测模块进程,把设置窗口调成WindowSize.Large,若导航树中找不到MouseUtilitiesNavItem就先展开InputOutputNavItem("输入和输出"分组),再点击 "Mouse utilities" 导航项进入鼠标工具设置页。 - 按可访问性标识定位控件:模块容器、开关、输入框全部通过稳定的 AutomationId 定位,统一定义在 util/MouseUtilsSettings.cs 的
AccessibilityIds常量表中,例如:- 模块容器:
MouseUtils_FindMyMouseTestId、MouseUtils_MouseHighlighterTestId、MouseUtils_MousePointerCrosshairsTestId、MouseUtils_MouseJumpTestId; - 启停开关:
MouseUtils_FindMyMouseToggleId、MouseUtils_MouseHighlighterToggleId、MouseUtils_MousePointerCrosshairsToggleId、MouseUtils_MouseJumpToggleId; - 设置项分组:
...ActivationMethodId、...AppearanceBehaviorId、...ExcludedAppsId等。
- 模块容器:
- 真实的键鼠注入:用例通过
Session.SendKeys/SendKey/SendKeySequence注入组合键(例如Win + Shift + H、双击LCtrl),通过Session.PerformMouseAction(MouseActionType.LeftDown/RightDown/ScrollDown...)与IOUtil.MoveMouseBy注入鼠标按下/抬起/滚轮/拖动。 - 像素级"所见即所得"断言:由于这些工具的视觉反馈(聚光灯、高亮圈、十字准线)本身即功能主体,测试大量使用
GetPixelColorString(x, y)在特定坐标采样屏幕像素颜色,并与设定的 RGB 值做Assert.AreEqual / AreNotEqual。这是理解后文所有"校验覆盖层出现/消失"断言的关键——断言的不是某个 UI 控件属性,而是屏幕真实像素。 - 窗口句柄附加与距离计算:Mouse Jump 用
Session.Attach("MouseJump")附加预览窗口、以窗口中心点击后计算鼠标位移的欧氏距离(MouseJumpTests.CalculateDistance),用"光标是否落到屏幕中心附近(≤10px)"来等价验证"跳转到点击位置"。
针对每个工具的"设置模型"被独立建模,供用例在改动设置与断言结果时复用,例如 util/FindMyMouseSettings.cs、util/MouseHighlighterSettings.cs、util/MousePointerCrosshairsSettings.cs。这些模型还顺带暴露了设置项的取值范围字符串(见下),与清单中的待测设置一一对应。
三、Find My Mouse(找不到鼠标时快速定位):8/15 已迁移
Find My Mouse 在鼠标静止时被触发后,会在指针周围画出一个高亮"聚光灯"式的覆盖层,帮助用户在一堆窗口中找回鼠标。对应源码目录为 src/modules/MouseUtils/FindMyMouse(FindMyMouse.cpp/FindMyMouse.h),测试实现于 FindMyMouseTests.cs。
3.1 启用/禁用与激活场景
清单中该模块"启用与触发"相关的 5 个条目均已 [x]:
| 清单条目(手工验证步骤) | 状态 | 对应自动化实现要点 |
|---|---|---|
| 启用 FindMyMouse 后,鼠标保持不动,按两次左 Ctrl,覆盖层出现 | [x] |
TestEnableFindMyMouse:先 Toggle(true) 开启 → ActivateSpotlight 连续注入两次 LCtrl → VerifySpotlightAppears 采样光标处像素断言等于聚光灯色、光标外区域断言等于背景色 |
| 按任意其他键,覆盖层消失 | [x] |
注入 Key.A 后 VerifySpotlightDisappears 反向断言聚光灯/背景色不再出现 |
| 再次按两次左 Ctrl,覆盖层出现 | `[x] | 重复上述"出现"断言 |
| 按下鼠标按键,覆盖层消失 | `[x] | Session.PerformMouseAction(LeftClick) 后执行"消失"断言 |
| 停用 FindMyMouse 后按两次左 Ctrl,覆盖层不再出现 | [x] |
TestDisableFindMyMouse 系列:启用→断言出现→Toggle(false)→断言消失→再次 Toggle(true) 恢复 |
需要注意测试的时序细节:两次 Ctrl 注入之间、以及断言之前都有 Task.Delay 等待,说明这类覆盖层存在渲染/动画时序,自动化必须容忍固定延迟(代码中多处 100ms~2000ms 不等)。
3.2 游戏模式(独占全屏)场景:尚未自动化
清单第 12~15 行专门针对 "Do not activate on game mode"(游戏模式下不激活)选项设计了正反两个场景,且均为 [ ]:
- 开启该选项并启动一个 CG native 独占全屏的游戏后,按两次左 Ctrl,验证覆盖层不出现;
- 关闭该选项并启动同一款游戏后,按两次左 Ctrl,验证覆盖层出现(清单还如实备注"though it'll likely minimize the game"——覆盖层弹出很可能会把游戏最小化)。
从代码结构看,FindMyMouse 模块内确有与游戏模式相关的分支逻辑(见 FindMyMouse.cpp 中对全屏/游戏进程的检测处理)。这两条迟迟未 [x] 也符合工程直觉:UI 自动化会话无法可靠地拉起并维持一个 CG 独占全屏游戏,且注入按键会触发最小化等副作用,因此只能保留为手工回归点。这是"清单上合理保留手工项"的典型代表。
3.3 外观与行为设置项:部分已迁移
清单 3.3 的 8 个设置项验证中 3 项已 [x]:
| 设置项 | 状态 | 备注 |
|---|---|---|
| Overlay opacity(覆盖层不透明度) | [ ] |
当前仓库 FindMyMouseTests.cs 中 FindMyMouseSettings.OverlayOpacity 虽有赋值但未见注入到对应 UI 控件的逻辑,仍未完成像素级验证 |
| Background color(背景色) | [x] |
通过颜色选择器弹窗设置 RGB 十六进制(校验形如 #RRGGBB 7 字符),再用像素断言核对 |
| Spotlight color(聚光灯色) | [x] |
同上 |
| Spotlight radius(聚光灯半径) | [x] |
通过 InputBox 直接输入半径像素值,VerifySpotlightAppears 在 cursor + radius-1 处仍采样到聚光灯色,在 radius+50 之外采样到背景色 |
| Spotlight initial zoom(初始缩放,1x 与 9x 差异明显) | [ ] |
代码已出现对该滑块的设置(spotlightInitialZoomSlider.QuickSetValue(...)),清单状态与之存在时间差 |
| Animation duration(动画时长) | [ ] |
代码中同样已有对 FindMyMouseAnimationDuration 输入框的设置逻辑 |
| 切换激活方式为"晃动鼠标(shake)"并实测 | [ ] |
FindMyMouseSettings.ActivationMethod 枚举已定义 ShakeMouse,但 ActivateSpotlight 中该分支仅留注释"Simulate shake mouse",尚未实现晃动模拟 |
| Excluded apps(排除的应用) | [ ] |
测试中只对排除应用入口做了点击展开,尚未端到端断言"在排除应用内不触发" |
这里有一个值得注意的观察:清单状态与当前代码之间存在明显的"时间差"。例如清单将 Spotlight initial zoom、Animation duration 记为 [ ],但当前 FindMyMouseTests.cs 中已经包含对 Zoom 滑块与动画时长输入框的注入与断言辅助逻辑。这说明仓库内该文件的实现进度已领先于迁移台账的记录(台账更新滞后于代码提交),阅读时建议以代码为"实现真值"、以清单为"官方待办视图"。
此外,FindMyMouse 的动画类断言还依赖系统"动画效果"开关:测试在检测到 "Animations are disabled in your system settings." 提示时会自动打开 Windows 设置开启动画,再回到 PowerToys 设置页重载用例(CheckAnimationEnable 辅助方法),从而保证覆盖层动画(淡入、缩放)能被稳定采样到。
四、Mouse Highlighter(鼠标点击高亮):8/12 已迁移
Mouse Highlighter 在按下/拖动鼠标按键时,于指针周围绘制随动的彩色光环,常用于演示/录屏场景。对应源码目录 src/modules/MouseUtils/MouseHighlighter,测试见 MouseHighlighterTests.cs。
4.1 启用、拖动跟随与停用(5/5 已迁移)
清单中的 5 个功能场景全部 [x]:
- 按下激活快捷键后点击左/右键,验证出现高亮——测试注入
Win+Shift+H开启高亮后,用LeftDown/RightDown模拟点击,在光标处与cursor + radius-1处断言命中主键/次键高亮色; - 按住左键拖动,高亮随指针移动——
VerifyMouseHighlighterDrag用IOUtil.SimulateMouseDown(true)+ 每 10ms 移动 1px 共 500 步的循环模拟拖动,在终点位置再次断言高亮色仍在光标下; - 按住右键拖动同理(
SimulateMouseDown(false)); - 再次按激活快捷键,点击不再出现高亮;
- 停用模块后按激活快捷键,模块不再被触发(
VerifyMouseHighlighterNotAppears反向断言)。
4.2 设置项:快捷键与左右键颜色已迁移
| 设置项 | 状态 | 说明 |
|---|---|---|
| 修改激活快捷键并实测 | [x] |
先故意输入单键 H 触发 "Invalid shortcut" 无效提示断言,再输入 Win+Shift+H(另一用例为 Win+Shift+O)并保存,随后用新快捷键完成全套出现/消失断言 |
| Left button highlight color | [x] |
通过颜色弹窗设置,十六进制为 8 位 ARGB(如 FFFF0000),断言时用 Substring(2) 剥离 Alpha 后与 #RRGGBB 像素值比对 |
| Right button highlight color | [x] |
同上(如 FF00FF00) |
| Opacity | [ ] |
待迁移 |
| Radius | [ ] |
从设置模型看存在 "Radius (px) Minimum5" 输入框,代码里 SetMouseHighlighterAppearanceBehavior 已有对该输入框的写入断言 |
| Fade delay | [ ] |
模型中为 "Fade delay (ms) Minimum0" |
| Fade duration | [ ] |
模型中为 "Fade duration (ms) Minimum0",且"高亮消失"断言正是依赖 Task.Delay(duration+100) 等待淡出完成后再反向采样 |
与 FindMyMouse 类似,Mouse Highlighter 的半径/淡出时长等条目在清单中虽标记为 [ ],但 MouseHighlighterTests.cs 中已存在 SetMouseHighlighterAppearanceBehavior 对 Radius、Fade delay、Fade duration 输入框及 Always highlight color 的完整注入实现——迁移台账同样落后于代码。淡出验证依赖设置模型的取值范围元数据,这是把"带单位的 UI 文本"与可执行断言解耦的典型做法("Radius (px) Minimum5"、"Fade duration (ms) Minimum0" 这样的字符串即控件自动化名称)。
五、Mouse Pointer Crosshairs(指针十字准线):5/10 已迁移
Mouse Pointer Crosshairs 以光标为中心绘制横竖十字准线,便于精确对齐与定位。源码目录 src/modules/MouseUtils/MousePointerCrosshairs,测试见 MousePointerCrosshairsTests.cs。
5.1 基本功能场景(3/3 已迁移)
- 按激活快捷键,十字线出现并跟随鼠标移动——
VerifyMousePointerCrosshairsAppears不仅在光标中心采样,还在x±50 / y±50四个方向采样,逐点断言命中十字线颜色;随后用IOUtil.MoveMouseBy(-1,0)×100 拖动鼠标后再次断言,验证"跟随移动"; - 再次按激活快捷键,十字线消失;
- 停用模块后按快捷键不再激活。
5.2 设置项:仅"颜色"与"快捷键"已迁移
| 设置项 | 状态 | 取值范围(来自设置模型) | 说明 |
|---|---|---|---|
| 修改激活快捷键 | [x] |
— | 单键 H 触发 "Invalid shortcut" 断言后改用 Win+Alt+A / Win+Alt+P |
| Crosshairs color | [x] |
— | 6 位 RGB,像素断言同样在中心与四方向采样 |
| Crosshairs opacity | [ ] |
"Crosshairs opacity (%)" 滑块 | 代码已有 opacitySlider.QuickSetValue(100) 与文本断言,清单未同步 |
| Crosshairs center radius | [ ] |
Minimum 0 / Maximum 500 (px) | 代码已有 SetText 断言 |
| Crosshairs thickness | [ ] |
Minimum 1 / Maximum 50 (px) | 同上 |
| Crosshairs border color | [ ] |
— | 代码已有颜色弹窗注入(SetColor),断言子流程完备 |
| Crosshairs border size | [ ] |
Minimum 0 / Maximum 50 (px) | 同上 |
清单记录与代码的"时间差"在这一模块体现得最明显:SetMousePointerCrosshairsAppearanceBehavior(MousePointerCrosshairsTests.cs)已经把 opacity、center radius、thickness、border size、border color、以及 "Fix crosshairs length"(固定长度,含 Minimum 1 的 FixedLength)全部写入并断言,而清单里 7 个设置项中有 5 个仍是 [ ]。此外测试还覆盖了"展开外观与行为分组后滚动定位输入框"这类真实交互细节(ScrollDown×3),说明该用例已相当接近端到端操作形态。
六、Mouse Jump(鼠标跳屏):4/7 已迁移
Mouse Jump 通过激活快捷键弹出多屏预览画布,点击预览任意位置即可把光标瞬移过去。它是五件鼠标工具中实现分层最多的一件(WinUI3 前端 + Common 模型/助手库 + HotKeys),源码散见于 src/modules/MouseUtils/MouseJump、MouseJump.Common、MouseJump.WinUI3 等工程,测试见 MouseJumpTests.cs。
6.1 已迁移场景(4/4)
| 清单条目 | 自动化实现 |
|---|---|
| 按激活快捷键,出现屏幕预览 | TestEnableMouseJump2 注入新快捷键 Win+Shift+Z 后 VerifyWindowAppears:Session.Attach("MouseJump") 确认预览窗口打开 |
| 修改激活快捷键后新快捷键生效 | TestEnableMouseJump3 用另一组 Win+Shift+J 验证同样能拉起预览窗口 |
| 点击预览任意位置,光标跳到该处 | VerifyWindowAppears 内的等价校验:预览窗口打开后,在其中心执行 LeftClick,随后比较鼠标新位置与屏幕中心,断言欧氏距离 <= 10,即"点击预览中心 → 光标被传送到屏幕中心" |
| 停用模块后快捷键不再触发 | Toggle(false) 后再次注入快捷键,VerifyWindowNotAppears 通过 IsWindowOpen("MouseJump") 反向断言窗口未打开 |
6.2 仍为手工回归的多显示器场景(3/7)
- 在 Display 设置中调整屏幕排列顺序后确认预览随之更新且功能正常——
[ ]; - 修改屏幕缩放比例后确认仍然正常——
[ ]; - 拔掉附加显示器后确认仍然正常——
[ ]。
这三条未自动化是合理的:它们要求用例运行期间动态变更系统显示拓扑(增删/重排/缩放显示器),这在共享 CI 主机与 WinAppDriver 会话中几乎不可控,属于环境破坏型手工用例。从代码证据看,Mouse Jump 对多显示器拓扑确实高度敏感——MouseJump.Common 下有完整的显示/设备/DPI 建模(DpiModeHelper、ScreenHelper、DeviceInfo/DisplayInfo/ScreenInfo),多屏布局换算正是其核心逻辑,因此最稳妥的回归仍由真实多显示器环境的人工清单承担。
七、迁移进度汇总与"欠账"归因
将四模块的 [x]/[ ] 汇总如下:
| 模块 | 已迁移 [x] |
待迁移 [ ] |
合计 |
|---|---|---|---|
| Find My Mouse | 8 | 7 | 15 |
| Mouse Highlighter | 8 | 4 | 12 |
| Mouse Pointer Crosshairs | 5 | 5 | 10 |
| Mouse Jump | 4 | 3 | 7 |
| 合计 | 25(约 57%) | 19(约 43%) | 44 |
剩余 19 条大致可按原因归为三类:
- 系统/环境依赖型(难以自动化):如 Find My Mouse 的"游戏模式 + CG 独占全屏"正反用例、Mouse Jump 的"显示器重排/缩放/热插拔"——需要真实游戏或可变的多屏拓扑;
- 手势/物理操作型(模拟成本高):如 Find My Mouse 的"晃动鼠标"激活方式——
ActivateSpotlight中该分支只有注释占位,尚无可靠的晃动轨迹模拟; - 台账滞后型(代码已就绪):Find My Mouse 的 initial zoom/animation duration、Mouse Highlighter 的 Radius/Fade delay/Fade duration、Mouse Pointer Crosshairs 的 opacity/center radius/thickness/border color/border size 等,在当前仓库的测试辅助方法中已存在完整注入与断言逻辑,仅清单勾选状态未同步。
在推进这类迁移时,可以按以下顺序自查:先在 util/ 的 Settings 模型类中补齐该设置项的自动化名称(含单位与上下限,如 "Crosshairs thickness (px) Minimum1 Maximum50");再在对应 *Tests.cs 中新增设置注入辅助函数(参考 SetFindMyMouseAppearanceBehavior/SetMousePointerCrosshairsAppearanceBehavior 的颜色弹窗 #RRGGBB/#AARRGGBB 校验、滑块 QuickSetValue、输入框 SetText 重试 5 次的既有套路);最后用屏幕像素断言验证"改了什么就看见什么",并把清单条目勾为 [x]。
八、如何运行与查看这套 UI 测试
这些用例属于需要真实 Windows UI 会话的 UI 自动化测试(项目在 Linux 上无法直接执行,需在 Windows + PowerToys 开发环境构建后运行):
- 测试工程:
src/modules/MouseUtils/MouseUtils.UITests(MSTest,IsTestProject=true,产物输出到tests\MouseUtils.UITests\); - 依赖:通用 UI 自动化框架工程
src/common/UITestAutomation(csproj 中ProjectReference),它封装了Session/By.AccessibilityId/GetPixelColorString/IOUtil等能力; - 调度方式:由于
RunVSTest=false,需使用vstest.console或测试资源管理器显式运行;用例通过[TestClass]/[TestMethod](显示名形如MouseUtils.FindMyMouse.EnableFindMyMouse)组织,并带有[TestCategory("Mouse Utils #N")]分类标签,便于按模块/场景筛选; - 前提条件:被测的 FindMyMouse、MouseHighlighter、MouseJump、MousePointerCrosshairs 等模块可执行体需就绪,且系统需开启"动画效果",测试会自动检测 "Animations are disabled" 提示并代为开启。
更细的运行与调试说明可参考仓库开发文档 ui-tests.md 与 UI 自动化框架源码 UITestAutomation;MouseUtils 模块自身的实现与设置入口则分别在 FindMyMouse、MouseHighlighter、MousePointerCrosshairs、MouseJump 各模块目录中。
结语
Release-Test-Checklist-Migration-Progress.md 不只是一份简单的勾选清单,它实际上是 MouseUtils 四模块"手工发布回归 → UI 自动化回归"迁移过程的唯一对照表:[x] 代表某项能力已被自动化用例以真实的键鼠注入 + 像素/窗口断言覆盖,[ ]] 则暴露了自动化难以触碰的边界(独占全屏游戏、多显示器拓扑、晃动手势)。结合仓库内测试源码可以发现,清单的勾选状态滞后于代码实现进度——真正判断某条能力是否已自动化,应以 *Tests.cs 中的注入与断言逻辑为准,而以本清单作为统一视图进行跟踪与核对。这套"手工清单 + 自动化迁移台账 + 像素级断言"的组合,对任何以视觉反馈为核心的用户工具(尤其是鼠标类、画笔类、屏幕标注类功能)的回归测试设计都具备直接借鉴价值。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00