Files
recordingtest/tests/Recordingtest.Runner.Tests/Fakes.cs
minsung 70bf5703b3 player: raw scenario replay without manual cleanup (#14)
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>
2026-04-08 19:26:41 +09:00

88 lines
2.6 KiB
C#

using Recordingtest.DiffReporter;
using Recordingtest.Player;
using Recordingtest.Player.Model;
using Recordingtest.Runner;
namespace Recordingtest.Runner.Tests;
public sealed class FakePlayerHost : IPlayerHost
{
private readonly string _outDir;
private readonly string _resultContent;
private readonly bool _throwOnClick;
public FakePlayerHost(string outDir, string resultContent, bool throwOnClick = false)
{
_outDir = outDir;
_resultContent = resultContent;
_throwOnClick = throwOnClick;
}
public ResolvedElement? ResolveElement(string uiaPath, TimeSpan timeout)
=> new ResolvedElement(new ElementBounds(0, 0, 10, 10), null);
public bool WaitFor(string waitForHint, TimeSpan timeout) => true;
public void Click(ScreenPoint point)
{
if (_throwOnClick) throw new InvalidOperationException("fake click failure");
}
public void Type(string text) { }
public void Drag(ScreenPoint from, ScreenPoint to) { }
public void Hotkey(string keys)
{
// simulate save
Directory.CreateDirectory(_outDir);
File.WriteAllText(Path.Combine(_outDir, "result.json"), _resultContent);
}
public void CaptureCheckpoint(int afterStep, string saveAs) { }
public void CaptureFailureArtifacts(int stepIndex, string reason) { }
public void Delay(TimeSpan duration) { }
}
public sealed class FakeHostFactory : IRunnerHostFactory
{
private readonly string _resultContent;
private readonly bool _throwOnClick;
public FakeHostFactory(string resultContent, bool throwOnClick = false)
{
_resultContent = resultContent;
_throwOnClick = throwOnClick;
}
public IPlayerHost Create(Scenario scenario, string outDir)
=> new FakePlayerHost(outDir, _resultContent, _throwOnClick);
}
public sealed class SpyNormalizer : INormalizer
{
public List<string> Profiles { get; } = new();
public string Normalize(string input, string profile, string? sidecarPath)
{
Profiles.Add(profile);
return input;
}
}
public sealed class StubDiffer : IDiffer
{
private readonly bool _identical;
private readonly int _hunkCount;
public StubDiffer(bool identical, int hunkCount = 0)
{
_identical = identical;
_hunkCount = hunkCount;
}
public DiffResult Compare(string approvedPath, string receivedPath)
{
var hunks = new List<Hunk>();
for (int i = 0; i < _hunkCount; i++)
hunks.Add(new Hunk(i, "changed", "a", "b"));
return new DiffResult(Path.GetFileName(receivedPath), _identical, hunks, new DiffSummary(0, 0, _hunkCount));
}
}