using Bunit; using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared; using ZB.MOM.WW.ScadaBridge.Commons.Types.Kpi; namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components.Shared; /// /// bUnit tests for — the reusable /// dependency-free SVG trend chart. Coverage pins the three render states /// (multi-point chart, unavailable placeholder, single-sample note), the stable /// data-test slug, and the coordinate-math edge guards (all-equal /// timestamps, all-zero values) that must not divide by zero or throw. /// public class KpiTrendChartTests : BunitContext { private static readonly DateTime Base = new(2026, 6, 15, 10, 0, 0, DateTimeKind.Utc); private static IReadOnlyList ThreePoints() => new[] { new KpiSeriesPoint(Base, 1.0), new KpiSeriesPoint(Base.AddMinutes(5), 4.0), new KpiSeriesPoint(Base.AddMinutes(10), 2.0), }; /// Verifies that an available series with three points renders a polyline, a title and a data-test attribute. [Fact] public void Available_WithThreePoints_RendersPolylineTitleAndDataTest() { var cut = Render(p => p .Add(c => c.Points, ThreePoints()) .Add(c => c.Title, "Queue Depth") .Add(c => c.IsAvailable, true)); var markup = cut.Markup; Assert.Contains("", markup); } /// Verifies that a unit is appended to the rendered value labels. [Fact] public void Available_WithUnit_AppendsUnitToValueLabels() { var cut = Render(p => p .Add(c => c.Points, ThreePoints()) .Add(c => c.Title, "Oldest Pending") .Add(c => c.Unit, "s") .Add(c => c.IsAvailable, true)); // Max value label is "4 s" with the unit suffix applied. Assert.Contains("max 4 s", cut.Markup); } /// Verifies that an unavailable series renders an em dash placeholder and the error text without a polyline. [Fact] public void Unavailable_RendersEmDashPlaceholderAndError_NoPolyline() { var cut = Render(p => p .Add(c => c.Points, ThreePoints()) .Add(c => c.Title, "Queue Depth") .Add(c => c.IsAvailable, false) .Add(c => c.ErrorMessage, "KPI query failed")); var markup = cut.Markup; Assert.Contains("—", markup); // em dash Assert.Contains("KPI query failed", markup); Assert.DoesNotContain("Verifies that null points render the placeholder without a polyline. [Fact] public void NullPoints_RendersPlaceholder_NoPolyline() { var cut = Render(p => p .Add(c => c.Points, (IReadOnlyList?)null) .Add(c => c.Title, "Stuck Count") .Add(c => c.IsAvailable, true)); var markup = cut.Markup; Assert.Contains("—", markup); Assert.DoesNotContain("Verifies that a single point renders the single-sample state without a polyline and does not throw. [Fact] public void SinglePoint_RendersSingleSampleState_NoPolyline_DoesNotThrow() { var cut = Render(p => p .Add(c => c.Points, new[] { new KpiSeriesPoint(Base, 7.0) }) .Add(c => c.Title, "Backlog") .Add(c => c.IsAvailable, true)); var markup = cut.Markup; Assert.Contains("Only one sample in range.", markup); Assert.DoesNotContain("Verifies that points with identical timestamps render a polyline without throwing. [Fact] public void AllEqualTimestamps_DoesNotThrow_AndRendersPolyline() { // Zero time span would divide by zero without the even-spacing guard. var cut = Render(p => p .Add(c => c.Points, new[] { new KpiSeriesPoint(Base, 1.0), new KpiSeriesPoint(Base, 2.0), new KpiSeriesPoint(Base, 3.0), }) .Add(c => c.Title, "Flat Time") .Add(c => c.IsAvailable, true)); var markup = cut.Markup; Assert.Contains("Verifies that all-zero values render a flat polyline without throwing. [Fact] public void AllZeroValues_DoesNotThrow_AndRendersFlatPolyline() { // Zero max value would divide by zero without the flat-line guard. var cut = Render(p => p .Add(c => c.Points, new[] { new KpiSeriesPoint(Base, 0.0), new KpiSeriesPoint(Base.AddMinutes(5), 0.0), new KpiSeriesPoint(Base.AddMinutes(10), 0.0), }) .Add(c => c.Title, "Quiet") .Add(c => c.IsAvailable, true)); var markup = cut.Markup; Assert.Contains("Verifies that a chart title is converted to a stable data-test slug. /// The chart title to slugify. /// The expected data-test slug. [Theory] [InlineData("Queue Depth", "kpi-trend-queue-depth")] [InlineData("Oldest Pending (s)", "kpi-trend-oldest-pending-s")] [InlineData(" Trailing Spaces ", "kpi-trend-trailing-spaces")] [InlineData("!!!", "kpi-trend-chart")] [InlineData("", "kpi-trend-chart")] public void Slugify_ProducesStableSlug(string title, string expectedDataTest) { Assert.Equal(expectedDataTest, $"kpi-trend-{KpiTrendChart.Slugify(title)}"); } }