test(kpi): K17 — Playwright KPI trend-chart spec (tolerant, SkippableFact)
This commit is contained in:
+106
@@ -0,0 +1,106 @@
|
||||
using Microsoft.Playwright;
|
||||
using ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Cluster;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Monitoring;
|
||||
|
||||
/// <summary>
|
||||
/// E2E coverage for the M6 "KPI History & Trends" feature (K17's spec slice):
|
||||
/// proves a <c>KpiTrendChart</c> trend section renders in the live Central UI.
|
||||
///
|
||||
/// <para>
|
||||
/// <b>Page targeted</b> — the Notification Outbox KPIs page
|
||||
/// (<c>NotificationKpis.razor</c>, <c>@page "/notifications/kpis"</c>). Its trend
|
||||
/// section is wrapped in a container marked <c>data-test="notification-trends"</c>
|
||||
/// and renders three <see cref="KpiTrendChart"/> cards (Queue Depth, Parked,
|
||||
/// Delivered / interval). The NotificationOutbox KPI source samples on every
|
||||
/// recorder tick even at zero values, so this page is the most reliably-populated
|
||||
/// trend surface on a fresh cluster — preferred over the Health dashboard fallback
|
||||
/// (<c>/monitoring/health</c>, container <c>data-test="site-health-trends"</c>).
|
||||
/// The page requires the Deployment policy, which the <c>multi-role</c> test user
|
||||
/// holds (Admin + Design + Deployment).
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// <b>Fixture / auth / skip plumbing mirrors <see cref="HealthDashboardTests"/></b>
|
||||
/// — a navigate-and-read test (no instance seeding), so it takes the
|
||||
/// <see cref="PlaywrightFixture"/> directly (not <c>DeploymentFixture</c>),
|
||||
/// authenticates with <see cref="PlaywrightFixture.NewAuthenticatedPageAsync()"/>
|
||||
/// (the <c>multi-role</c> / <c>password</c> default), and gates on
|
||||
/// <see cref="ClusterAvailability.IsAvailableAsync"/> via <c>Skip.IfNot</c> so the
|
||||
/// suite stays green when docker is down.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// <b>Why the polyline assertion is tolerant</b> (the DebugView lesson) — a fresh
|
||||
/// cluster may have little or no KPI history sampled yet.
|
||||
/// <see cref="KpiTrendChart"/> renders its <c>data-test="kpi-trend-*"</c> CARD in
|
||||
/// ALL THREE states: the ≥2-point <c><polyline></c> chart, the single-sample
|
||||
/// note, and the unavailable/empty em-dash placeholder. The <c><polyline></c>
|
||||
/// only exists in the first (≥2 samples) state. So the hard gate is card presence
|
||||
/// (renders regardless of data); the polyline is asserted TOLERANTLY — present is
|
||||
/// logged, absent is fine (recorder hasn't sampled twice yet). This mirrors exactly
|
||||
/// how <see cref="DebugViewTreeTests"/> tolerated the empty alarm tree
|
||||
/// (<c>[role='tree']</c> present vs. EmptyContent hint).
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Collection("Playwright")]
|
||||
public class KpiTrendChartTests
|
||||
{
|
||||
/// <summary>Notification Outbox KPIs page route (from <c>NotificationKpis.razor</c>'s <c>@page</c>).</summary>
|
||||
private const string KpisUrl = "/notifications/kpis";
|
||||
|
||||
private readonly PlaywrightFixture _fixture;
|
||||
|
||||
public KpiTrendChartTests(PlaywrightFixture fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to the Notification Outbox KPIs page and asserts the KPI trend
|
||||
/// section renders: the trends container is visible, at least one
|
||||
/// <c>kpi-trend-*</c> card is present, and (tolerantly) reports whether any
|
||||
/// polyline has been plotted yet.
|
||||
/// </summary>
|
||||
[SkippableFact]
|
||||
public async Task NotificationKpis_RendersTrendSection_WithKpiTrendCards()
|
||||
{
|
||||
Skip.IfNot(await ClusterAvailability.IsAvailableAsync(), ClusterAvailability.SkipReason);
|
||||
|
||||
var page = await _fixture.NewAuthenticatedPageAsync();
|
||||
await page.GotoAsync($"{PlaywrightFixture.BaseUrl}{KpisUrl}");
|
||||
await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
|
||||
|
||||
// ── Hard gate 1: the trend section container renders. ──
|
||||
// NotificationKpis.razor wraps the Trends heading + window toggle in a flex
|
||||
// container marked data-test="notification-trends"; the three trend cards sit
|
||||
// in the div.row immediately below it.
|
||||
var trends = page.Locator("[data-test='notification-trends']");
|
||||
await Assertions.Expect(trends).ToBeVisibleAsync(new() { Timeout = 20_000 });
|
||||
|
||||
// ── Hard gate 2: at least one kpi-trend-* card is present. ──
|
||||
// KpiTrendChart renders its data-test="kpi-trend-<slug>" card in EVERY state
|
||||
// (polyline chart, single-sample note, OR em-dash placeholder), so this holds
|
||||
// regardless of whether any history has been sampled yet on a fresh cluster.
|
||||
var trendCards = page.Locator("[data-test^='kpi-trend-']");
|
||||
await Assertions.Expect(trendCards.First).ToBeVisibleAsync(new() { Timeout = 20_000 });
|
||||
var cardCount = await trendCards.CountAsync();
|
||||
Assert.True(cardCount >= 1, $"Expected at least one kpi-trend-* card; found {cardCount}.");
|
||||
|
||||
// ── Tolerant data assertion: the polyline (actual plotted series). ──
|
||||
// The <polyline> renders ONLY in KpiTrendChart's ≥2-point chart state. On a
|
||||
// fresh cluster the recorder may not have sampled twice yet (single-sample or
|
||||
// empty), so its absence is NOT a failure — card presence above is the hard
|
||||
// gate. Mirrors DebugViewTreeTests tolerating the empty alarm tree.
|
||||
var polylineCount = await trendCards.Locator("polyline").CountAsync();
|
||||
if (polylineCount > 0)
|
||||
{
|
||||
// History has been sampled — at least one card plotted a series.
|
||||
Assert.True(
|
||||
polylineCount > 0,
|
||||
$"Observed {polylineCount} plotted trend polyline(s) across the kpi-trend-* cards.");
|
||||
}
|
||||
// else: no polyline yet (recorder hasn't accumulated ≥2 samples / empty
|
||||
// history) — tolerated by design; the card-presence gate above is authoritative.
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user