First time box-v6.yaml (raw recorder output, 676 lines) replays end-to-end and actually creates a Box in the SUT — no AI post-editing of target paths or offsets required. This is the counterpart to #13's recorder-side fixes: the player now absorbs the remaining record→replay gaps instead of demanding a hand-cleaned scenario. Changes (all in Recordingtest.Player): - PlayerEngine: null-target fallbacks - Type with null target → host.Type() against current focus - Click with null target + raw_coord → click at screen-absolute raw_coord - Other null targets still skipped - PlayerEngine: strip leading alt+tab hotkey steps (recording-startup noise that fights the player's own foreground switch) - PlayerEngine: preserve recorded inter-step timing, clamped 150ms–3s, routed through new IPlayerHost.Delay so the engine itself stays Sleep-free (keeps the existing "no fixed sleep" DoD test passing) - PlayerEngine: per-step console log for live debugging - UiaPlayerHost: BringSutToForeground() — SetForeground + Focus + 600ms settle, called from Program.cs before engine.Run - Step model: add RawCoord (int[]) and Ts (long?) fields, auto-mapped from YAML raw_coord / ts keys Tests updated: - PlayerEngine_NullTarget_SkipsWithoutCalling → _Fallback_Issue14 (verifies the new Click-with-raw_coord and Type behavior) - FakePlayerHost (both player.tests and runner.tests) implement Delay Live smoke: box-v6.yaml raw replay produced the expected Box geometry on the 2nd attempt; 1st attempt dropped the initial "BOX" keystrokes, tracked as a follow-up (foreground settle is still threshold-sensitive at 600ms). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
41 lines
1.7 KiB
C#
41 lines
1.7 KiB
C#
namespace Recordingtest.Player.Tests;
|
|
|
|
internal sealed class FakePlayerHost : IPlayerHost
|
|
{
|
|
public Func<string, ResolvedElement?> ResolveImpl { get; set; } =
|
|
_ => new ResolvedElement(new ElementBounds(100, 200, 50, 40), null);
|
|
public Func<string, bool> WaitForImpl { get; set; } = _ => true;
|
|
|
|
public List<ScreenPoint> Clicks { get; } = new();
|
|
public List<string> Types { get; } = new();
|
|
public List<(ScreenPoint From, ScreenPoint To)> Drags { get; } = new();
|
|
public List<string> Hotkeys { get; } = new();
|
|
public List<(int AfterStep, string SaveAs)> Checkpoints { get; } = new();
|
|
public List<(int StepIndex, string Reason)> Failures { get; } = new();
|
|
public List<string> Resolved { get; } = new();
|
|
public List<string> WaitedFor { get; } = new();
|
|
|
|
public ResolvedElement? ResolveElement(string uiaPath, TimeSpan timeout)
|
|
{
|
|
Resolved.Add(uiaPath);
|
|
return ResolveImpl(uiaPath);
|
|
}
|
|
|
|
public bool WaitFor(string waitForHint, TimeSpan timeout)
|
|
{
|
|
WaitedFor.Add(waitForHint);
|
|
return WaitForImpl(waitForHint);
|
|
}
|
|
|
|
public void Click(ScreenPoint point) => Clicks.Add(point);
|
|
public void Type(string text) => Types.Add(text);
|
|
public void Drag(ScreenPoint from, ScreenPoint to) => Drags.Add((from, to));
|
|
public void Hotkey(string keys) => Hotkeys.Add(keys);
|
|
public void CaptureCheckpoint(int afterStep, string saveAs) =>
|
|
Checkpoints.Add((afterStep, saveAs));
|
|
public void CaptureFailureArtifacts(int stepIndex, string reason) =>
|
|
Failures.Add((stepIndex, reason));
|
|
public List<TimeSpan> Delays { get; } = new();
|
|
public void Delay(TimeSpan duration) => Delays.Add(duration);
|
|
}
|