feat(kpi): K14 — Site Calls trend charts
This commit is contained in:
@@ -10,9 +10,11 @@ using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NSubstitute;
|
||||
using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;
|
||||
using ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Audit;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Kpi;
|
||||
using ZB.MOM.WW.ScadaBridge.Communication;
|
||||
using ZB.MOM.WW.ScadaBridge.Security;
|
||||
using SiteCallsReportPage = ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.SiteCalls.SiteCallsReport;
|
||||
@@ -34,6 +36,11 @@ public class SiteCallsReportPageTests : BunitContext
|
||||
private readonly ActorSystem _system = ActorSystem.Create("site-calls-report-tests");
|
||||
private readonly CommunicationService _comms;
|
||||
|
||||
// K14: KPI-history facade for the Trends section. Defaults to a known
|
||||
// non-empty (≥2-point) series so an expanded chart draws a polyline;
|
||||
// individual tests reconfigure it (e.g. to throw) before rendering.
|
||||
private readonly IKpiHistoryQueryService _kpiHistory = Substitute.For<IKpiHistoryQueryService>();
|
||||
|
||||
private static readonly Guid ParkedId = Guid.Parse("11111111-1111-1111-1111-111111111111");
|
||||
private static readonly Guid FailedId = Guid.Parse("22222222-2222-2222-2222-222222222222");
|
||||
|
||||
@@ -77,6 +84,20 @@ public class SiteCallsReportPageTests : BunitContext
|
||||
Services.AddSingleton(_comms);
|
||||
Services.AddSingleton<IDialogService>(new AlwaysConfirmDialogService());
|
||||
|
||||
// K14: by default every trend series resolves to a known ≥2-point series so
|
||||
// an expanded chart draws a polyline. Tests that need a failure reconfigure
|
||||
// this substitute before rendering.
|
||||
_kpiHistory.GetSeriesAsync(
|
||||
Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string?>(),
|
||||
Arg.Any<DateTime>(), Arg.Any<DateTime>(), Arg.Any<int?>(), Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult<IReadOnlyList<KpiSeriesPoint>>(new List<KpiSeriesPoint>
|
||||
{
|
||||
new(DateTime.UtcNow.AddHours(-2), 3),
|
||||
new(DateTime.UtcNow.AddHours(-1), 7),
|
||||
new(DateTime.UtcNow, 5),
|
||||
}));
|
||||
Services.AddSingleton(_kpiHistory);
|
||||
|
||||
var siteRepo = Substitute.For<ISiteRepository>();
|
||||
siteRepo.GetAllSitesAsync(Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult<IReadOnlyList<Site>>(new List<Site>
|
||||
@@ -510,6 +531,91 @@ public class SiteCallsReportPageTests : BunitContext
|
||||
Assert.DoesNotContain(FailedId.ToString("N")[..12], cut.Markup);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Trends (K14) — a collapsible section with one KpiTrendChart per metric
|
||||
// (buffered / parked / failedLastInterval) for SiteCallAudit / Global. The
|
||||
// section is collapsed on init; expanding it lazy-loads the series.
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void TrendsSection_Container_AlwaysRenders()
|
||||
{
|
||||
var cut = Render<SiteCallsReportPage>();
|
||||
|
||||
// The collapsible container (with its toggle) is present even while
|
||||
// collapsed — only the charts are gated behind the expand.
|
||||
cut.WaitForAssertion(() =>
|
||||
Assert.NotNull(cut.Find("[data-test='site-calls-trends']")));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TrendsSection_WhenExpanded_RendersChartsFromKnownSeries()
|
||||
{
|
||||
var cut = Render<SiteCallsReportPage>();
|
||||
cut.WaitForState(() => cut.Markup.Contains("ERP.GetOrder"));
|
||||
|
||||
// Collapsed on init — no chart yet.
|
||||
Assert.Empty(cut.FindAll("[data-test^='kpi-trend-']"));
|
||||
|
||||
// Expand the Trends section; the lazy series load fills the charts.
|
||||
cut.Find("[data-test='site-calls-trends-toggle']").Click();
|
||||
|
||||
cut.WaitForAssertion(() =>
|
||||
{
|
||||
// At least one trend chart rendered, and (because the series has ≥2
|
||||
// points) it draws a polyline rather than the unavailable placeholder.
|
||||
var charts = cut.FindAll("[data-test^='kpi-trend-']");
|
||||
Assert.NotEmpty(charts);
|
||||
Assert.Contains("<polyline", cut.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TrendsSection_WindowToggle_ReloadsSeries()
|
||||
{
|
||||
var cut = Render<SiteCallsReportPage>();
|
||||
cut.WaitForState(() => cut.Markup.Contains("ERP.GetOrder"));
|
||||
|
||||
cut.Find("[data-test='site-calls-trends-toggle']").Click();
|
||||
cut.WaitForAssertion(() => Assert.NotEmpty(cut.FindAll("[data-test^='kpi-trend-']")));
|
||||
|
||||
// First expand issued 3 GetSeriesAsync calls (one per metric).
|
||||
_kpiHistory.ReceivedCalls();
|
||||
var afterExpand = _kpiHistory.ReceivedCalls().Count();
|
||||
|
||||
// Switch the window to 7d — the series re-load (another 3 calls).
|
||||
cut.FindAll("button").First(b => b.TextContent.Trim() == "7d").Click();
|
||||
|
||||
cut.WaitForAssertion(() =>
|
||||
Assert.True(_kpiHistory.ReceivedCalls().Count() > afterExpand));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TrendsSection_WhenKpiHistoryThrows_PageStillRenders()
|
||||
{
|
||||
// Best-effort contract: a GetSeriesAsync throw must never break the page.
|
||||
_kpiHistory.GetSeriesAsync(
|
||||
Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string?>(),
|
||||
Arg.Any<DateTime>(), Arg.Any<DateTime>(), Arg.Any<int?>(), Arg.Any<CancellationToken>())
|
||||
.Returns<Task<IReadOnlyList<KpiSeriesPoint>>>(_ =>
|
||||
throw new InvalidOperationException("KPI backend down"));
|
||||
|
||||
var cut = Render<SiteCallsReportPage>();
|
||||
cut.WaitForState(() => cut.Markup.Contains("ERP.GetOrder"));
|
||||
|
||||
// Expanding triggers the failing loads — the grid and trends container
|
||||
// both survive; the charts fall back to the unavailable placeholder.
|
||||
cut.Find("[data-test='site-calls-trends-toggle']").Click();
|
||||
|
||||
cut.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("ERP.GetOrder", cut.Markup);
|
||||
Assert.NotNull(cut.Find("[data-test='site-calls-trends']"));
|
||||
// No polyline — the charts degraded to the unavailable em-dash state.
|
||||
Assert.DoesNotContain("<polyline", cut.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
|
||||
Reference in New Issue
Block a user