using Microsoft.Playwright; using ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Cluster; namespace ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Monitoring; /// /// E2E coverage for the M6 "KPI History & Trends" feature (K17's spec slice): /// proves a KpiTrendChart trend section renders in the live Central UI. /// /// /// Page targeted — the Notification Outbox KPIs page /// (NotificationKpis.razor, @page "/notifications/kpis"). Its trend /// section is wrapped in a container marked data-test="notification-trends" /// and renders three 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 /// (/monitoring/health, container data-test="site-health-trends"). /// The page requires the Deployment policy, which the multi-role test user /// holds (Admin + Design + Deployment). /// /// /// /// Fixture / auth / skip plumbing mirrors /// — a navigate-and-read test (no instance seeding), so it takes the /// directly (not DeploymentFixture), /// authenticates with /// (the multi-role / password default), and gates on /// via Skip.IfNot so the /// suite stays green when docker is down. /// /// /// /// Why the polyline assertion is tolerant (the DebugView lesson) — a fresh /// cluster may have little or no KPI history sampled yet. /// renders its data-test="kpi-trend-*" CARD in /// ALL THREE states: the ≥2-point <polyline> chart, the single-sample /// note, and the unavailable/empty em-dash placeholder. The <polyline> /// 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 tolerated the empty alarm tree /// ([role='tree'] present vs. EmptyContent hint). /// /// [Collection("Playwright")] public class KpiTrendChartTests { /// Notification Outbox KPIs page route (from NotificationKpis.razor's @page). private const string KpisUrl = "/notifications/kpis"; private readonly PlaywrightFixture _fixture; public KpiTrendChartTests(PlaywrightFixture fixture) { _fixture = fixture; } /// /// Navigates to the Notification Outbox KPIs page and asserts the KPI trend /// section renders: the trends container is visible, at least one /// kpi-trend-* card is present, and (tolerantly) reports whether any /// polyline has been plotted yet. /// [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-" 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 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. } }