Fix smoke 2차 gaps: hotkey tests, focus filter, viewport picking (#13)

Gap E — Hotkey named key
- UiaPlayerHost: extract ParsedHotkey record + ParseHotkey static
- HotkeyParseTests: 8 tests (enter/tab/a/ctrl+c/ctrl+shift+s/f5/alt+f4/empty)

Gap F — recorder focus_change SUT filter
- FocusEventFilter.ShouldAccept static rule (same/zero/unknown/unknown-sut)
- Program.cs wires it inside RegisterFocusChangedEvent callback
- FocusEventFilterTests: 4 tests

Gap G — viewport picking foreign-process fallback
- IWindowPointSource + WindowPointResolver pure resolver
- FlaUiPointSource wired in Program.cs (best-effort hit test, honest partial for live SUT)
- WindowPointResolverTests: 5 tests

Tests: 77 → 94, build 0/0 (TreatWarningsAsErrors preserved).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
minsung
2026-04-08 18:21:36 +09:00
parent 7db9cd08e1
commit b139f2b169
10 changed files with 377 additions and 11 deletions

View File

@@ -12,4 +12,7 @@
<PackageReference Include="FlaUI.UIA3" Version="4.0.0" />
<PackageReference Include="YamlDotNet" Version="16.1.3" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="Recordingtest.Player.Tests" />
</ItemGroup>
</Project>

View File

@@ -77,9 +77,10 @@ public sealed class UiaPlayerHost : IPlayerHost, IDisposable
new System.Drawing.Point(from.X, from.Y),
new System.Drawing.Point(to.X, to.Y));
public void Hotkey(string keys)
internal sealed record ParsedHotkey(IReadOnlyList<VirtualKeyShort> Modifiers, VirtualKeyShort? Main);
internal static ParsedHotkey ParseHotkey(string keys)
{
// Minimal: support "ctrl+s" style.
var parts = keys.Split('+', StringSplitOptions.RemoveEmptyEntries);
var modifiers = new List<VirtualKeyShort>();
VirtualKeyShort? main = null;
@@ -120,9 +121,15 @@ public sealed class UiaPlayerHost : IPlayerHost, IDisposable
break;
}
}
foreach (var m in modifiers) Keyboard.Press(m);
if (main is not null) Keyboard.Type(main.Value);
foreach (var m in modifiers) Keyboard.Release(m);
return new ParsedHotkey(modifiers, main);
}
public void Hotkey(string keys)
{
var parsed = ParseHotkey(keys);
foreach (var m in parsed.Modifiers) Keyboard.Press(m);
if (parsed.Main is not null) Keyboard.Type(parsed.Main.Value);
foreach (var m in parsed.Modifiers) Keyboard.Release(m);
}
public void CaptureCheckpoint(int afterStep, string saveAs)