test(ui): automation runner gains hover/mousemove — synthetic pointer verify without the OS cursor

Morning gate live-verify apparatus: 'hover element <datId>' and
'mousemove <x> <y>' drive UiRoot.OnMouseMove synthetically (no click, no
real cursor theft — a user is present at the machine during this round,
unlike the overnight rounds whose drive scripts moved the physical
cursor). Deliberately no probe-clock Advance: hover dwell and the
world-tooltip timing must ride the production frame tick's real
monotonic clock, which keeps running between script commands — an
Advance would stamp the idle timestamp with the probe's tiny private
counter.

(Committed from a re-attached worktree: the round's original worktree
was pruned from git's registry mid-session by an external cleanup; the
branch and all three finding commits were unaffected.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-17 09:32:06 +02:00
parent 942a02af11
commit 302d90209d
2 changed files with 56 additions and 0 deletions

View file

@ -131,6 +131,32 @@ public sealed class RetailUiAutomationProbe
return true;
}
/// <summary>
/// 2026-08-17 morning gate: synthetic pointer HOVER (no click) at an
/// element's center, for rollover/tooltip verification. Deliberately
/// does NOT <see cref="Advance"/> the probe's own micro-clock: hover
/// dwell (<see cref="UiRoot.MouseIdleMs"/> vs
/// <see cref="UiRoot.TooltipDelayMs"/>) and the world-tooltip timing
/// must ride the PRODUCTION frame tick's real monotonic clock
/// (<c>UiHost.Tick</c>'s <c>TickCount64</c>), which keeps running
/// between script commands — an <c>Advance</c> here would stamp the
/// idle timestamp with this probe's tiny private counter and make the
/// next production tick read an enormous (or negative) idle time.
/// </summary>
public bool HoverElement(uint datElementId)
{
var target = FindByDatElementId(datElementId);
if (target is null) return Fail($"element 0x{datElementId:X8} not found");
_root.OnMouseMove((int)target.CenterX, (int)target.CenterY);
return true;
}
/// <summary>Raw synthetic mouse move (canvas coordinates). Same
/// no-<see cref="Advance"/> contract as <see cref="HoverElement"/> —
/// used to sweep the pointer across the 3D world for the world-hover
/// tooltip's mouse-idle dwell verification.</summary>
public void MoveMouse(int x, int y) => _root.OnMouseMove(x, y);
public bool ClickItem(uint itemGuid, ItemDragSource? sourceKind = null)
{
var target = FindByItemId(itemGuid, sourceKind);