using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using Recordingtest.Player.Model; using Xunit; namespace Recordingtest.Player.Tests; public class PlayerEngineTests { [Fact] public void Player_EmptyScenario_ExitsZero() { var engine = new PlayerEngine(); var host = new FakePlayerHost(); var scenario = new Scenario { Name = "empty" }; engine.Run(scenario, host); Assert.Empty(host.Clicks); Assert.Empty(host.Failures); } [Fact] public void Player_ClickStep_InvokesHostClickAtExpectedScreenPoint() { var engine = new PlayerEngine(); var host = new FakePlayerHost { ResolveImpl = _ => new ResolvedElement( new ElementBounds(100, 200, 50, 40), null), }; var scenario = new Scenario { Steps = { new Step { Kind = StepKind.Click, Target = new Target { UiaPath = "Window/Button[@AutomationId='ok']", Offset = new[] { 0.5, 0.25 }, }, }, }, }; engine.Run(scenario, host); // 100 + 50*0.5 = 125 ; 200 + 40*0.25 = 210 Assert.Single(host.Clicks); Assert.Equal(new ScreenPoint(125, 210), host.Clicks[0]); } [Fact] public void Player_ResolveFailure_CapturesArtifacts() { var engine = new PlayerEngine(); var host = new FakePlayerHost { ResolveImpl = _ => null }; var scenario = new Scenario { Steps = { new Step { Kind = StepKind.Click, Target = new Target { UiaPath = "Bogus" }, }, }, }; var ex = Assert.Throws( () => engine.Run(scenario, host)); Assert.Single(host.Failures); Assert.Equal(0, host.Failures[0].StepIndex); Assert.False(string.IsNullOrEmpty(host.Failures[0].Reason)); Assert.Contains("Bogus", ex.Message); } [Fact] public void Player_CheckpointStep_InvokesCapture() { var engine = new PlayerEngine(); var host = new FakePlayerHost(); var scenario = new Scenario { Steps = { new Step { Kind = StepKind.Checkpoint, AfterStep = 3, SaveAs = "out/cp.hmeg", }, }, }; engine.Run(scenario, host); Assert.Single(host.Checkpoints); Assert.Equal(3, host.Checkpoints[0].AfterStep); Assert.Equal("out/cp.hmeg", host.Checkpoints[0].SaveAs); } [Fact] public void Player_NoFixedSleep() { var path = LocateEngineSource(); var src = File.ReadAllText(path); Assert.DoesNotMatch(new Regex(@"Thread\.Sleep\("), src); Assert.DoesNotMatch(new Regex(@"Task\.Delay\(TimeSpan\.FromSeconds"), src); } [Fact] public void Player_ScenarioLoader_ParsesSampleYaml() { const string yaml = """ name: sample description: tiny sut: exe: "EG-BIM Modeler/EG-BIM Modeler.exe" startup_timeout_ms: 12000 steps: - kind: click target: uia_path: "Window/Button[@AutomationId='ok']" offset: [0.5, 0.5] - kind: type value: "hello" - kind: checkpoint after_step: 1 save_as: "out/cp1.hmeg" checkpoints: - after_step: 1 save_as: "out/cp1.hmeg" baselines: - path: "baselines/cp1.approved.hmeg" """; var s = ScenarioLoader.LoadFromString(yaml); Assert.Equal("sample", s.Name); Assert.Equal(12000, s.Sut.StartupTimeoutMs); Assert.Equal(3, s.Steps.Count); Assert.Equal(StepKind.Click, s.Steps[0].Kind); Assert.Equal("Window/Button[@AutomationId='ok']", s.Steps[0].Target!.UiaPath); Assert.Equal(0.5, s.Steps[0].Target!.Offset[0]); Assert.Equal(StepKind.Type, s.Steps[1].Kind); Assert.Equal("hello", s.Steps[1].Value); Assert.Equal(StepKind.Checkpoint, s.Steps[2].Kind); Assert.Equal(1, s.Steps[2].AfterStep); Assert.Single(s.Checkpoints); Assert.Single(s.Baselines); } [Fact] public void PlayerEngine_NullTarget_SkipsWithoutCalling() { var engine = new PlayerEngine(); var host = new FakePlayerHost(); var scenario = new Scenario { Steps = { new Step { Kind = StepKind.Click, Target = null }, new Step { Kind = StepKind.Drag, Target = null }, new Step { Kind = StepKind.Type, Target = null, Value = "hello" }, }, }; engine.Run(scenario, host); Assert.Empty(host.Clicks); Assert.Empty(host.Drags); Assert.Empty(host.Types); Assert.Empty(host.Failures); } [Fact] public void PlayerEngine_WheelKind_DoesNotThrow() { var engine = new PlayerEngine(); var host = new FakePlayerHost(); var scenario = new Scenario { Steps = { new Step { Kind = StepKind.Wheel, Value = "-120" }, new Step { Kind = StepKind.Focus }, }, }; engine.Run(scenario, host); Assert.Empty(host.Failures); } [Fact] public void ScenarioLoader_ParsesWheelAndFocusKinds() { const string yaml = """ name: wheel-focus steps: - kind: wheel value: "-120" - kind: focus target: uia_path: "Window/Edit" offset: [0.5, 0.5] """; var s = ScenarioLoader.LoadFromString(yaml); Assert.Equal(2, s.Steps.Count); Assert.Equal(StepKind.Wheel, s.Steps[0].Kind); Assert.Equal(StepKind.Focus, s.Steps[1].Kind); } private static string LocateEngineSource([CallerFilePath] string here = "") { // here = .../tests/Recordingtest.Player.Tests/PlayerEngineTests.cs var dir = Path.GetDirectoryName(here)!; var repo = Path.GetFullPath(Path.Combine(dir, "..", "..")); return Path.Combine(repo, "src", "Recordingtest.Player", "PlayerEngine.cs"); } }