Files
ScadaBridge/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Components/Shared/KpiTrendChartTests.cs
T
Joseph Doherty 513fa1aa16 docs(tests): fix CommentChecker findings across tests/ — 13,392 → 0 (repo-wide scan clean)
Comment/XML-doc changes only:
- add summaries to ~10k test methods, fixtures, fakes and helpers
  (+ <param> on theories, <returns> on async Task methods,
  <inheritdoc/> on interface implementations the checker can resolve)
- strip ~2.6k project-tracking references (WP*, P*-*, row-NN, Task N,
  #NN, Component-0NN) from comments, keeping the explanatory text;
  reword false positives in comments only
- real docs on fakes implementing BCL/library interfaces
  (IDisposable, ILogger, IOptionsMonitor, IAsyncLifetime)
- reword the last 5 src/ false positives (CWE-1236, failures-1,
  year-0001, M365)

Only non-comment edits: four one-line nested classes split across lines
so members can carry docs. Build 0 errors / 0 warnings.
2026-10-07 15:05:49 -04:00

160 lines
6.4 KiB
C#

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;
/// <summary>
/// bUnit tests for <see cref="KpiTrendChart"/> — the reusable
/// dependency-free SVG trend chart. Coverage pins the three render states
/// (multi-point chart, unavailable placeholder, single-sample note), the stable
/// <c>data-test</c> slug, and the coordinate-math edge guards (all-equal
/// timestamps, all-zero values) that must not divide by zero or throw.
/// </summary>
public class KpiTrendChartTests : BunitContext
{
private static readonly DateTime Base = new(2026, 6, 15, 10, 0, 0, DateTimeKind.Utc);
private static IReadOnlyList<KpiSeriesPoint> ThreePoints() => new[]
{
new KpiSeriesPoint(Base, 1.0),
new KpiSeriesPoint(Base.AddMinutes(5), 4.0),
new KpiSeriesPoint(Base.AddMinutes(10), 2.0),
};
/// <summary>Verifies that an available series with three points renders a polyline, a title and a data-test attribute.</summary>
[Fact]
public void Available_WithThreePoints_RendersPolylineTitleAndDataTest()
{
var cut = Render<KpiTrendChart>(p => p
.Add(c => c.Points, ThreePoints())
.Add(c => c.Title, "Queue Depth")
.Add(c => c.IsAvailable, true));
var markup = cut.Markup;
Assert.Contains("<polyline", markup);
Assert.Contains("Queue Depth", markup);
Assert.Contains("data-test=\"kpi-trend-queue-depth\"", markup);
// The SVG must carry an accessible title.
Assert.Contains("<title>", markup);
}
/// <summary>Verifies that a unit is appended to the rendered value labels.</summary>
[Fact]
public void Available_WithUnit_AppendsUnitToValueLabels()
{
var cut = Render<KpiTrendChart>(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);
}
/// <summary>Verifies that an unavailable series renders an em dash placeholder and the error text without a polyline.</summary>
[Fact]
public void Unavailable_RendersEmDashPlaceholderAndError_NoPolyline()
{
var cut = Render<KpiTrendChart>(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("<polyline", markup);
}
/// <summary>Verifies that null points render the placeholder without a polyline.</summary>
[Fact]
public void NullPoints_RendersPlaceholder_NoPolyline()
{
var cut = Render<KpiTrendChart>(p => p
.Add(c => c.Points, (IReadOnlyList<KpiSeriesPoint>?)null)
.Add(c => c.Title, "Stuck Count")
.Add(c => c.IsAvailable, true));
var markup = cut.Markup;
Assert.Contains("—", markup);
Assert.DoesNotContain("<polyline", markup);
}
/// <summary>Verifies that a single point renders the single-sample state without a polyline and does not throw.</summary>
[Fact]
public void SinglePoint_RendersSingleSampleState_NoPolyline_DoesNotThrow()
{
var cut = Render<KpiTrendChart>(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("<polyline", markup);
}
/// <summary>Verifies that points with identical timestamps render a polyline without throwing.</summary>
[Fact]
public void AllEqualTimestamps_DoesNotThrow_AndRendersPolyline()
{
// Zero time span would divide by zero without the even-spacing guard.
var cut = Render<KpiTrendChart>(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("<polyline", markup);
// The even-spacing fallback must produce finite coordinates — no NaN or Infinity
// from the divide-by-zero guard path.
Assert.DoesNotContain("NaN", markup);
Assert.DoesNotContain("Infinity", markup);
}
/// <summary>Verifies that all-zero values render a flat polyline without throwing.</summary>
[Fact]
public void AllZeroValues_DoesNotThrow_AndRendersFlatPolyline()
{
// Zero max value would divide by zero without the flat-line guard.
var cut = Render<KpiTrendChart>(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("<polyline", markup);
// The zero-max flat-line guard must produce finite coordinates — no NaN or Infinity
// from the yFrac divide-by-zero protection path.
Assert.DoesNotContain("NaN", markup);
Assert.DoesNotContain("Infinity", markup);
}
/// <summary>Verifies that a chart title is converted to a stable data-test slug.</summary>
/// <param name="title">The chart title to slugify.</param>
/// <param name="expectedDataTest">The expected data-test slug.</param>
[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)}");
}
}