From 51183e431f544171491ebc2701b18e03578caf4a Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 17 Aug 2026 11:20:12 +0200 Subject: [PATCH] =?UTF-8?q?test(ui):=20automation=20runner=20gains=20`clic?= =?UTF-8?q?k=20at=20=20`=20=E2=80=94=20raw=20synthetic=20canvas=20cl?= =?UTF-8?q?ick?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vitals round's connected verify needed to click ONE specific Character-tab option row, but every toggle row is a template instance sharing the same dat element ids (0x10000218/0x10000219), so `click element` (first-match by dat id) cannot address a row. The drive script now reads the row's rect from its own `dump` line and clicks its center — same synthetic UiRoot press/release route as ClickElement, never the OS cursor (the same no-real-input constraint the morning gate's hover/mousemove verbs follow). Used live: the Side-By-Side Vitals checkbox + Apply choreography that verified db8fa328's swap both directions over a real ACE session. Co-Authored-By: Claude Fable 5 --- .../UI/Testing/RetailUiAutomationProbe.cs | 16 ++++++++++++++++ .../UI/Testing/RetailUiAutomationScriptRunner.cs | 16 ++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/AcDream.App/UI/Testing/RetailUiAutomationProbe.cs b/src/AcDream.App/UI/Testing/RetailUiAutomationProbe.cs index d3ab64c1..069f6d26 100644 --- a/src/AcDream.App/UI/Testing/RetailUiAutomationProbe.cs +++ b/src/AcDream.App/UI/Testing/RetailUiAutomationProbe.cs @@ -131,6 +131,22 @@ public sealed class RetailUiAutomationProbe return true; } + /// + /// Raw synthetic click at canvas coordinates (vitals retail-modes round, + /// 2026-08-17): the Options panel's Character-tab rows are template + /// instances sharing ONE dat element id per control + /// (0x10000218/0x10000219 for every toggle row), so + /// element-id addressing cannot reach a SPECIFIC row's checkbox — a + /// drive script instead reads the row's rect from + /// and clicks its center. Same synthetic route as + /// — never the OS cursor. + /// + public bool ClickAtPoint(int x, int y) + { + ClickAt(x, y); + return true; + } + /// /// 2026-08-17 morning gate: synthetic pointer HOVER (no click) at an /// element's center, for rollover/tooltip verification. Deliberately diff --git a/src/AcDream.App/UI/Testing/RetailUiAutomationScriptRunner.cs b/src/AcDream.App/UI/Testing/RetailUiAutomationScriptRunner.cs index 46d0ec37..7a0c9c7c 100644 --- a/src/AcDream.App/UI/Testing/RetailUiAutomationScriptRunner.cs +++ b/src/AcDream.App/UI/Testing/RetailUiAutomationScriptRunner.cs @@ -219,7 +219,7 @@ public sealed class RetailUiAutomationScriptRunner : IDisposable private bool DoClick(ScriptCommand command) { var p = command.Parts; - if (p.Length < 3) return Stop(command, "usage: click element | click item [source]"); + if (p.Length < 3) return Stop(command, "usage: click element | click item [source] | click at "); string target = p[1].ToLowerInvariant(); if (target == "element") { @@ -231,7 +231,19 @@ public sealed class RetailUiAutomationScriptRunner : IDisposable if (!TryParseUInt(p[2], out uint itemGuid)) return Stop(command, $"bad item guid '{p[2]}'"); return _probe.ClickItem(itemGuid, ParseSource(p, 3)) || Stop(command, "click item failed"); } - return Stop(command, "usage: click element | click item [source]"); + if (target == "at") + { + // `click at ` — raw synthetic click in canvas coordinates + // (see RetailUiAutomationProbe.ClickAtPoint: template-instanced + // controls share one dat id, so a specific row is addressed by + // the rect its own `dump` line reports). + if (p.Length < 4 + || !TryParseInt(p[2], out int x) + || !TryParseInt(p[3], out int y)) + return Stop(command, "usage: click at "); + return _probe.ClickAtPoint(x, y) || Stop(command, "click at failed"); + } + return Stop(command, "usage: click element | click item [source] | click at "); } /// 2026-08-17 morning gate: `hover element <datId>` — a