test(playwright): add NotificationKpis render + refresh coverage (Wave 3)
This commit is contained in:
+79
@@ -0,0 +1,79 @@
|
||||
using Microsoft.Playwright;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Cluster;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Notifications;
|
||||
|
||||
/// <summary>
|
||||
/// End-to-end coverage for the Notification KPIs page (<c>/notifications/kpis</c>).
|
||||
///
|
||||
/// <para>
|
||||
/// This page is pure-read (no mutations, no fixture seeding, no teardown). It requires
|
||||
/// the Deployment role; the test user <c>multi-role</c> has it. The KPI values themselves
|
||||
/// are non-deterministic; these tests assert structural render only — either the 5 KPI
|
||||
/// tiles render (happy path) or the cluster-unavailable alert renders (degraded path),
|
||||
/// and the Refresh button completes a round-trip without hanging.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Collection("Playwright")]
|
||||
public class NotificationKpisTests
|
||||
{
|
||||
private const string KpisUrl = "/notifications/kpis";
|
||||
|
||||
private readonly PlaywrightFixture _pw;
|
||||
|
||||
public NotificationKpisTests(PlaywrightFixture pw)
|
||||
{
|
||||
_pw = pw;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to the Notification KPIs page and asserts that the page resolved to a
|
||||
/// real state — EITHER all 5 KPI tile labels rendered OR the 'KPIs unavailable'
|
||||
/// alert is shown.
|
||||
/// </summary>
|
||||
[SkippableFact]
|
||||
public async Task KpisPage_RendersTilesOrError()
|
||||
{
|
||||
Skip.IfNot(await ClusterAvailability.IsAvailableAsync(), ClusterAvailability.SkipReason);
|
||||
|
||||
var page = await _pw.NewAuthenticatedPageAsync();
|
||||
await page.GotoAsync($"{PlaywrightFixture.BaseUrl}{KpisUrl}");
|
||||
await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
|
||||
|
||||
await Assertions.Expect(page.Locator("h4:has-text('Notification KPIs')")).ToBeVisibleAsync();
|
||||
|
||||
var tilesOk = await page.Locator("small.text-muted:has-text('Queue Depth')").IsVisibleAsync()
|
||||
&& await page.Locator("small.text-muted:has-text('Oldest Pending Age')").IsVisibleAsync();
|
||||
var errShown = await page.Locator(".alert.alert-warning:has-text('KPIs unavailable')").IsVisibleAsync();
|
||||
Assert.True(tilesOk || errShown,
|
||||
"Expected either the 5 KPI tiles or the 'KPIs unavailable' alert to render.");
|
||||
|
||||
if (tilesOk)
|
||||
{
|
||||
await Assertions.Expect(page.Locator("small.text-muted:has-text('Stuck')")).ToBeVisibleAsync(new() { Timeout = 5_000 });
|
||||
await Assertions.Expect(page.Locator("small.text-muted:has-text('Parked')")).ToBeVisibleAsync(new() { Timeout = 5_000 });
|
||||
await Assertions.Expect(page.Locator("small.text-muted:has-text('Delivered (last interval)')")).ToBeVisibleAsync(new() { Timeout = 5_000 });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to the Notification KPIs page, clicks the Refresh button, and asserts
|
||||
/// that the button re-enables within 10 s — proving the refresh round-trip completed
|
||||
/// without hanging.
|
||||
/// </summary>
|
||||
[SkippableFact]
|
||||
public async Task KpisPage_RefreshReenables()
|
||||
{
|
||||
Skip.IfNot(await ClusterAvailability.IsAvailableAsync(), ClusterAvailability.SkipReason);
|
||||
|
||||
var page = await _pw.NewAuthenticatedPageAsync();
|
||||
await page.GotoAsync($"{PlaywrightFixture.BaseUrl}{KpisUrl}");
|
||||
await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
|
||||
|
||||
var refresh = page.Locator("button.btn.btn-outline-secondary.btn-sm:has-text('Refresh')");
|
||||
await Assertions.Expect(refresh).ToBeEnabledAsync(new() { Timeout = 10_000 });
|
||||
await refresh.ClickAsync();
|
||||
await Assertions.Expect(refresh).ToBeEnabledAsync(new() { Timeout = 10_000 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user