From 4ae3ed3fdd67d7d2a43274f1dc1879b48cc8f126 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 22 Oct 2026 13:48:52 -0400 Subject: [PATCH] test(playwright): DeploymentActionTests load the lazy Topology tree via a shared helper The Topology page has loaded lazily (bounded instance pages behind 'Load instances' loaders) since 9ee402bb (2026-09-09); TopologyAreaTests was updated then but DeploymentActionTests still relied on one Expand-all click revealing instance rows, so all four facts timed out locating the row. Move OpenStableTopologyAsync into TopologyPageHelper and use it from both. --- .../Deployment/DeploymentActionTests.cs | 16 ++---- .../Deployment/TopologyAreaTests.cs | 36 +------------ .../Deployment/TopologyPageHelper.cs | 54 +++++++++++++++++++ 3 files changed, 59 insertions(+), 47 deletions(-) create mode 100644 tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/TopologyPageHelper.cs diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/DeploymentActionTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/DeploymentActionTests.cs index abccd403..6b422ecb 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/DeploymentActionTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/DeploymentActionTests.cs @@ -199,19 +199,9 @@ public class DeploymentActionTests : IClassFixture /// private static async Task OpenInstanceContextMenuAsync(IPage page, string uniqueName) { - await page.GotoAsync($"{PlaywrightFixture.BaseUrl}/deployment/topology"); - await page.WaitForLoadStateAsync(LoadState.NetworkIdle); - - // Stop the 15s live-updates timer from rebuilding the tree under us. - var liveToggle = page.Locator("#live-updates"); - if (await liveToggle.IsCheckedAsync()) - { - await liveToggle.UncheckAsync(); - } - - // ExpandAll() recurses every branch (site → area → …), so one click reveals - // the nested instance row under site-a's zztest area. - await page.Locator("button[aria-label='Expand all areas']").ClickAsync(); + // The tree loads lazily (since the 2026-09-09 low-findings wave): one Expand-all + // click no longer reveals instance rows, which sit behind "Load instances" loaders. + await TopologyPageHelper.OpenStableTopologyAsync(page); var row = page.Locator("div.tv-row", new() { HasText = uniqueName }); await Assertions.Expect(row).ToBeVisibleAsync(new() { Timeout = 15_000 }); diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/TopologyAreaTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/TopologyAreaTests.cs index de857d5e..6e31b89b 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/TopologyAreaTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/TopologyAreaTests.cs @@ -302,38 +302,6 @@ public class TopologyAreaTests : IClassFixture } } - /// - /// Navigates to the Topology page and returns a page whose tree is stable and - /// fully expanded: it unchecks the #live-updates switch (stopping the 15s - /// timer that would otherwise reload the tree and collapse expansions / tear down - /// open dialogs and rename inputs), then clicks Expand all areas so nested - /// area nodes are rendered and locatable. - /// - private static async Task OpenStableTopologyAsync(PlaywrightFixture pw) - { - var page = await pw.NewAuthenticatedPageAsync(); - await page.GotoAsync($"{PlaywrightFixture.BaseUrl}/deployment/topology"); - await page.WaitForLoadStateAsync(LoadState.NetworkIdle); - var live = page.Locator("#live-updates"); - if (await live.IsCheckedAsync()) await live.UncheckAsync(); // stop the 15s tree reload - // Lazy topology loads a bounded first page per branch. These small authoring - // fixtures need their sites/areas loaded before addressing labels or instances. - // "Expand all" re-renders over the SignalR circuit, so the click returns before the - // newly-expanded branches (and their "Load instances" loaders) exist. Wait until no - // collapsed branch remains before counting loaders — counting immediately raced the - // render and returned an unexpanded tree whenever the round-trip lost. - var collapsed = page.Locator("li[role='treeitem'][aria-expanded='false']"); - for (var branch = 0; branch < 300; branch++) - { - await page.Locator("button[aria-label='Expand all areas']").ClickAsync(); - await Assertions.Expect(collapsed).ToHaveCountAsync(0); - var load = page.GetByRole(AriaRole.Button, new() { Name = "Load instances", Exact = true }); - if (await load.CountAsync() == 0) return page; - var pages = page.GetByText("Page 1", new() { Exact = true }); - var count = await pages.CountAsync(); - await load.First.ClickAsync(); - await Assertions.Expect(pages).ToHaveCountAsync(count + 1); - } - throw new InvalidOperationException("Topology fixture exceeded 300 branches."); - } + private static Task OpenStableTopologyAsync(PlaywrightFixture pw) => + TopologyPageHelper.OpenStableTopologyAsync(pw); } diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/TopologyPageHelper.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/TopologyPageHelper.cs new file mode 100644 index 00000000..83edf74b --- /dev/null +++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/TopologyPageHelper.cs @@ -0,0 +1,54 @@ +using Microsoft.Playwright; + +namespace ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Deployment; + +/// +/// Shared Topology-page navigation for the Playwright suites. The page loads lazily +/// (bounded instance pages per branch, behind explicit "Load instances" loaders), so +/// any test that addresses an area or instance row must first expand and load every +/// branch. +/// +internal static class TopologyPageHelper +{ + /// + /// Navigates to the Topology page and returns a page whose tree is stable and + /// fully expanded: it unchecks the #live-updates switch (stopping the 15s + /// timer that would otherwise reload the tree and collapse expansions / tear down + /// open dialogs and rename inputs), then clicks Expand all areas so nested + /// area nodes are rendered and locatable. + /// + /// The Playwright fixture used to open an authenticated page. + /// The authenticated page showing the fully-loaded Topology tree. + public static async Task OpenStableTopologyAsync(PlaywrightFixture pw) => + await OpenStableTopologyAsync(await pw.NewAuthenticatedPageAsync()); + + /// Navigates an existing authenticated to the fully-loaded Topology tree. + /// An authenticated page. + /// The same page, showing the fully-loaded Topology tree. + public static async Task OpenStableTopologyAsync(IPage page) + { + await page.GotoAsync($"{PlaywrightFixture.BaseUrl}/deployment/topology"); + await page.WaitForLoadStateAsync(LoadState.NetworkIdle); + var live = page.Locator("#live-updates"); + if (await live.IsCheckedAsync()) await live.UncheckAsync(); // stop the 15s tree reload + // Lazy topology loads a bounded first page per branch. These small authoring + // fixtures need their sites/areas loaded before addressing labels or instances. + // "Expand all" re-renders over the SignalR circuit, so the click returns before the + // newly-expanded branches (and their "Load instances" loaders) exist. Wait until no + // collapsed branch remains before counting loaders — counting immediately raced the + // render and returned an unexpanded tree whenever the round-trip lost. + var collapsed = page.Locator("li[role='treeitem'][aria-expanded='false']"); + for (var branch = 0; branch < 300; branch++) + { + await page.Locator("button[aria-label='Expand all areas']").ClickAsync(); + await Assertions.Expect(collapsed).ToHaveCountAsync(0); + var load = page.GetByRole(AriaRole.Button, new() { Name = "Load instances", Exact = true }); + if (await load.CountAsync() == 0) return page; + var pages = page.GetByText("Page 1", new() { Exact = true }); + var count = await pages.CountAsync(); + await load.First.ClickAsync(); + await Assertions.Expect(pages).ToHaveCountAsync(count + 1); + } + throw new InvalidOperationException("Topology fixture exceeded 300 branches."); + } +}