using Bunit; using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Deployment; using ZB.MOM.WW.ScadaBridge.Commons.Types.Debugging; namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Deployment; /// /// ScadaBridge#56: the Flow Debugger's node-log section lists the lines other actors wrote for each /// traced execution — grouped per execution under that execution's run step, with level filtering and /// the logger category — and renders nothing when the graph has no node lines. /// public class FlowNodeLogPanelTests : BunitContext { private static readonly DateTimeOffset T0 = new(2026, 11, 10, 12, 0, 0, TimeSpan.Zero); private static FlowStep Run(Guid execution, string title) => new(Guid.NewGuid(), execution, null, FlowStepKind.ScriptRun, FlowLane.Site, title, null, T0, 10, FlowStepOutcome.Ok, null, null, null, "site-a", "node-a", "M1", title, null); private static FlowLogLine Node(Guid execution, string level, string message, int ms, string category = "ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Actors.DataConnectionActor", string? exceptionMessage = null) => new(Guid.NewGuid(), execution, T0.AddMilliseconds(ms), level, FlowLogLine.NodeSource, message, exceptionMessage, exceptionMessage is null ? null : "System.Exception: " + exceptionMessage, null, ms, category); private static FlowGraph Graph(IReadOnlyList steps, IReadOnlyList? nodeLogs) => new(steps, [], Predicted: false, Truncated: false, NodeLogs: nodeLogs); /// No node lines (or no graph): the section is not rendered at all. [Fact] public void NoNodeLines_RendersNothing() { var execution = Guid.NewGuid(); Assert.Empty(Render(p => p.Add(x => x.Graph, Graph([Run(execution, "Runner")], null))) .FindAll("[data-test=flow-nodelog-panel]")); Assert.Empty(Render().FindAll("[data-test=flow-nodelog-panel]")); } /// Lines are grouped per execution, titled by its run step, with short category, level and counts. [Fact] public void Lines_AreGroupedPerExecution_UnderTheRunStep() { var a = Guid.NewGuid(); var b = Guid.NewGuid(); var graph = Graph([Run(a, "OnRecipe"), Run(b, "SharedHelper")], [ Node(a, "debug", "Writing to tag T1", 1), Node(b, "warn", "tag T2 bad quality", 2, category: "ZB.MOM.WW.ScadaBridge.SiteRuntime.Actors.InstanceActor"), Node(a, "error", "write failed", 3, exceptionMessage: "timeout"), ]); var cut = Render(p => p.Add(x => x.Graph, graph)); Assert.Equal("3 lines · 1 error · 1 warning · 1 info/debug", cut.Find("[data-test=flow-nodelog-count]").TextContent); var groups = cut.FindAll("[data-test=flow-nodelog-execution]"); Assert.Equal(2, groups.Count); Assert.Contains("OnRecipe", groups[0].TextContent); Assert.Contains(a.ToString(), groups[0].TextContent); var aRows = groups[0].QuerySelectorAll("tbody tr"); Assert.Equal(2, aRows.Length); Assert.Contains("DataConnectionActor", aRows[0].TextContent); Assert.DoesNotContain("ZB.MOM.WW", aRows[0].TextContent); Assert.Contains("timeout", aRows[1].TextContent); Assert.Contains("InstanceActor", groups[1].TextContent); Assert.Contains("tag T2 bad quality", groups[1].TextContent); } /// Warn + hides debug/info lines; an execution left with none says so; All restores them. [Fact] public void WarnPlusFilter_HidesLowLevels() { var a = Guid.NewGuid(); var b = Guid.NewGuid(); var graph = Graph([Run(a, "OnRecipe"), Run(b, "Other")], [Node(a, "info", "connected", 1), Node(a, "warn", "slow write", 2), Node(b, "debug", "chatter", 3)]); var cut = Render(p => p.Add(x => x.Graph, graph)); cut.Find("[data-test=flow-nodelog-filter-warn]").Click(); var groups = cut.FindAll("[data-test=flow-nodelog-execution]"); Assert.Equal("warn", Assert.Single(groups[0].QuerySelectorAll("tbody tr")).GetAttribute("data-level")); Assert.NotNull(groups[1].QuerySelector("[data-test=flow-nodelog-none-shown]")); cut.Find("[data-test=flow-nodelog-filter-all]").Click(); Assert.Equal(3, cut.FindAll("[data-test=flow-nodelog-lines] tbody tr").Count); } /// Clicking the execution's title selects its run step. [Fact] public void ExecutionTitle_SelectsItsRunStep() { var a = Guid.NewGuid(); var run = Run(a, "OnRecipe"); FlowStep? selected = null; var cut = Render(p => p .Add(x => x.Graph, Graph([run], [Node(a, "warn", "slow", 1)])) .Add(x => x.OnSelectStep, s => selected = s)); cut.Find("[data-test=flow-nodelog-step]").Click(); Assert.Same(run, selected); } }