diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Monitoring/ParkedMessagesTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Monitoring/ParkedMessagesTests.cs new file mode 100644 index 00000000..d5649b9a --- /dev/null +++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Monitoring/ParkedMessagesTests.cs @@ -0,0 +1,67 @@ +using Microsoft.Playwright; +using Xunit; +using ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Cluster; + +namespace ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Monitoring; + +/// +/// End-to-end render / no-hang guard for the central Parked Messages page +/// (/monitoring/parked-messages). +/// +/// +/// Why this is a render guard and NOT a mutation test (unlike +/// ): parked store-and-forward +/// messages live in the SITE's local SQLite buffer, not in central MS SQL. The page +/// resolves them by relaying a ParkedMessageQueryRequest to the owning site over +/// the cluster (an Akka Ask answered by the site's S&F singleton). There is no central +/// table to seed — a directly-INSERTed central row cannot produce a parked S&F message — +/// so this test cannot deterministically seed a row to act on. Instead it asserts the +/// singleton-backed query resolves (renders the results table or the empty-state +/// card) within a generous window rather than hanging on the cross-cluster Ask — the +/// regression class this guards against. Empty results are tolerated. +/// +/// +/// +/// Gated on via Skip.IfNot: when the cluster is +/// unreachable the fact reports as Skipped (not Failed), matching the established suite +/// idiom. The query relays to a live site, so the cluster (not just MSSQL) must be up. +/// +/// +[Collection("Playwright")] +public class ParkedMessagesTests +{ + private const string ParkedMessagesUrl = "/monitoring/parked-messages"; + + private readonly PlaywrightFixture _fixture; + + public ParkedMessagesTests(PlaywrightFixture fixture) + { + _fixture = fixture; + } + + [SkippableFact] + public async Task ParkedMessages_QueryForSite_RendersWithoutHang() + { + Skip.IfNot(await ClusterAvailability.IsAvailableAsync(), ClusterAvailability.SkipReason); + + var page = await _fixture.NewAuthenticatedPageAsync(); + await page.GotoAsync($"{PlaywrightFixture.BaseUrl}{ParkedMessagesUrl}"); + await page.WaitForLoadStateAsync(LoadState.NetworkIdle); + + await Assertions.Expect(page.Locator("h4:has-text('Parked Messages')")).ToBeVisibleAsync(); + + // Select site-a — the