0ce6633270
Direction B: the same FlowGraph steps as a filterable list (lane column, lane filter, failures-only), toggled from the flow panel toolbar. The mockup canvas is not in the repo, so this is the minimal faithful version. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
97 lines
4.4 KiB
C#
97 lines
4.4 KiB
C#
using Bunit;
|
|
using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Types.Debugging;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Shared;
|
|
|
|
/// <summary>Tests for <see cref="FlowTraceList"/> (Flow Debugger vertical trace list, Gitea #53).</summary>
|
|
public class FlowTraceListTests : BunitContext
|
|
{
|
|
private static readonly DateTimeOffset T0 = new(2026, 11, 9, 12, 0, 0, TimeSpan.Zero);
|
|
|
|
private static FlowStep Step(FlowStepKind kind, FlowLane lane, string title, int offsetMs,
|
|
FlowStepOutcome outcome = FlowStepOutcome.Ok, int? durationMs = 5, IReadOnlyList<FlowLogLine>? logs = null) =>
|
|
new(Guid.NewGuid(), null, null, kind, lane, title, null, T0.AddMilliseconds(offsetMs), durationMs, outcome,
|
|
null, null, null, null, null, null, null, null, Logs: logs);
|
|
|
|
private static FlowGraph Graph(params FlowStep[] steps) =>
|
|
new(steps, Array.Empty<FlowEdge>(), Predicted: false, Truncated: false);
|
|
|
|
private static FlowGraph Sample() => Graph(
|
|
Step(FlowStepKind.InboundRequest, FlowLane.Central, "POST /api/M", 0),
|
|
Step(FlowStepKind.ScriptRun, FlowLane.Site, "RunIt", 20, FlowStepOutcome.Failed, 80),
|
|
Step(FlowStepKind.AttributeWrite, FlowLane.Tags, "Inst.Flag", 30));
|
|
|
|
/// <summary>Every step renders as a row in display order with its lane and offset.</summary>
|
|
[Fact]
|
|
public void Render_ListsStepsInOrderWithLaneColumn()
|
|
{
|
|
var cut = Render<FlowTraceList>(p => p.Add(c => c.Graph, Sample()));
|
|
|
|
var rows = cut.FindAll("[data-test='flow-list-row']");
|
|
Assert.Equal(3, rows.Count);
|
|
Assert.Equal(new[] { "Central", "Site", "Tags" }, rows.Select(r => r.GetAttribute("data-lane")));
|
|
Assert.Contains("+20 ms", rows[1].TextContent);
|
|
Assert.Contains("RunIt", rows[1].TextContent);
|
|
Assert.Contains("Failed", rows[1].TextContent);
|
|
Assert.Contains("failed", rows[1].ClassList);
|
|
}
|
|
|
|
/// <summary>The lane filter narrows the rows and the count line.</summary>
|
|
[Fact]
|
|
public void LaneFilter_NarrowsRows()
|
|
{
|
|
var cut = Render<FlowTraceList>(p => p.Add(c => c.Graph, Sample()));
|
|
|
|
cut.Find("[data-test='flow-list-lane-filter']").Change("Site");
|
|
|
|
var row = Assert.Single(cut.FindAll("[data-test='flow-list-row']"));
|
|
Assert.Equal("Site", row.GetAttribute("data-lane"));
|
|
Assert.Contains("1 of 3", cut.Find("[data-test='flow-list-count']").TextContent);
|
|
}
|
|
|
|
/// <summary>The failures-only toggle keeps failed and timed-out steps.</summary>
|
|
[Fact]
|
|
public void FailuresOnly_KeepsFailedSteps()
|
|
{
|
|
var cut = Render<FlowTraceList>(p => p.Add(c => c.Graph, Sample()));
|
|
|
|
cut.Find("[data-test='flow-list-failed-only']").Change(true);
|
|
|
|
var row = Assert.Single(cut.FindAll("[data-test='flow-list-row']"));
|
|
Assert.Equal("ScriptRun", row.GetAttribute("data-kind"));
|
|
}
|
|
|
|
/// <summary>Clicking or pressing Enter on a row raises OnSelect; the selected row is marked.</summary>
|
|
[Fact]
|
|
public void ClickAndEnter_RaiseSelect_AndSelectedRowIsMarked()
|
|
{
|
|
var graph = Sample();
|
|
var picked = new List<FlowStep>();
|
|
var cut = Render<FlowTraceList>(p => p
|
|
.Add(c => c.Graph, graph)
|
|
.Add(c => c.SelectedEventId, graph.Steps[2].EventId)
|
|
.Add(c => c.OnSelect, s => picked.Add(s)));
|
|
|
|
cut.FindAll("[data-test='flow-list-row']")[1].Click();
|
|
cut.FindAll("[data-test='flow-list-row']")[0].KeyDown(new Microsoft.AspNetCore.Components.Web.KeyboardEventArgs { Key = "Enter" });
|
|
|
|
Assert.Equal(new[] { FlowStepKind.ScriptRun, FlowStepKind.InboundRequest }, picked.Select(s => s.Kind));
|
|
var rows = cut.FindAll("[data-test='flow-list-row']");
|
|
Assert.Equal("true", rows[2].GetAttribute("aria-selected"));
|
|
Assert.Contains("selected", rows[2].ClassList);
|
|
}
|
|
|
|
/// <summary>No graph shows the empty note; log lines show a count.</summary>
|
|
[Fact]
|
|
public void EmptyGraph_ShowsNoSteps_AndLogsAreCounted()
|
|
{
|
|
Assert.NotNull(Render<FlowTraceList>().Find("[data-test='flow-list-empty']"));
|
|
|
|
var logs = new[] { new FlowLogLine(Guid.NewGuid(), null, T0, "error", "script", "boom", null, null, null, 1) };
|
|
var cut = Render<FlowTraceList>(p => p.Add(c => c.Graph,
|
|
Graph(Step(FlowStepKind.ScriptRun, FlowLane.Site, "S", 0, logs: logs))));
|
|
Assert.Equal("1", cut.Find("[data-test='flow-list-logs']").TextContent.Trim());
|
|
}
|
|
}
|