using WxAgent.Core; using Xunit; namespace WxAgent.Core.Tests; public sealed class UiDiagnosticsTests { [Fact] public void TraversalGuard_StopsCyclesBeforeRecursing() { var guard = new UiTraversalGuard(); Assert.True(guard.TryVisit("root", 0)); Assert.True(guard.TryVisit("child", 1)); Assert.False(guard.TryVisit("root", 2)); Assert.True(guard.CycleDetected); Assert.Equal(2, guard.NodeCount); } [Fact] public void TraversalGuard_BoundsDepthAndNodesEvenWithMissingRuntimeIds() { var depth = new UiTraversalGuard(maxDepth: 2); Assert.True(depth.TryVisit("", 0)); Assert.False(depth.TryVisit("", 3)); Assert.True(depth.Truncated); var count = new UiTraversalGuard(maxNodes: 2); Assert.True(count.TryVisit("", 0)); Assert.True(count.TryVisit("", 1)); Assert.False(count.TryVisit("", 2)); Assert.Equal(2, count.NodeCount); } [Fact] public void SnapshotComparison_DetectsRequiredControlRegressionWithoutLeakingDynamicIds() { var baseline = Root(WechatLocators.RequiredAutomationIds); var missing = WechatLocators.MessageList; var current = Root(WechatLocators.RequiredAutomationIds.Where(id => id != missing).Append("[id:private-hash]").Append("new_static_control")); var report = UiSnapshotComparison.Compare(baseline, current); Assert.Equal([missing], report.MissingRequiredControls); Assert.Equal([missing], report.RemovedAutomationIds); Assert.Equal(["new_static_control"], report.AddedAutomationIds); } private static UiNodeSnapshot Root(IEnumerable ids) => new("Window", "", "", true, "0,0,800,600", ids.Select(id => new UiNodeSnapshot("Pane", "", id, true, "", [])).ToArray()); }