From 07c5d53f39397f3ccbc280677d8717676d42d5c5 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Tue, 8 Sep 2026 09:43:37 -0400 Subject: [PATCH] test(playwright): drive the instance picker on Flow Debugger and Debug View Flow Debugger's script-entry instance selection and Debug View's instance selection both go through the shared InstancePicker combobox now instead of a flat -polling helper for the site select, which is unchanged. Claude-Session: https://claude.ai/code/session_01NcMG6xXdsJbmD2wYAgkK29 --- .../Deployment/DebugViewTests.cs | 27 ++++++++++--------- .../Deployment/DebugViewTreeTests.cs | 10 +++---- .../Deployment/FlowDebuggerTests.cs | 24 +++++++++++------ 3 files changed, 36 insertions(+), 25 deletions(-) diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/DebugViewTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/DebugViewTests.cs index a27674f4..c2808fb1 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/DebugViewTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/DebugViewTests.cs @@ -12,15 +12,18 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Deployment; /// without hanging. /// /// -/// The Enabled-only dropdown + deploy precondition (verified against -/// DebugView.razor::LoadInstancesForSite): the instance <select> -/// lists ONLY instances whose config state is Enabled +/// The Enabled-only instance picker + deploy precondition (verified against +/// DebugView.razor::LoadInstancesForSite): the instance +/// InstancePicker combobox (prefix debug-instance) lists ONLY +/// instances whose config state is Enabled /// (i.State == InstanceState.Enabled). A freshly-minted instance is /// NotDeployed and would never appear, so Fact B first drives the instance /// to Enabled over the CLI (instance deploy) before it can be picked -/// in the dropdown. Both <select>s carry the data-test hooks -/// (debug-site-select / debug-instance-select, option value = entity -/// id); Fact A asserts those hooks are served, which doubles as proof the cluster +/// in the picker. The site <select> carries the data-test hook +/// debug-site-select (option value = entity id); the instance picker carries +/// the shared InstancePicker hooks (debug-instance-picker/-input/ +/// -toggle/-panel/-row, row data-instance-id = entity id). +/// Fact A asserts those hooks are served, which doubles as proof the cluster /// rebuild that added them actually took. /// /// @@ -65,7 +68,7 @@ public class DebugViewTests : IClassFixture await Assertions.Expect(page.Locator("h4:has-text('Debug View')")).ToBeVisibleAsync(); // The data-test hooks from Task 4 must be served (proves the rebuild took). await Assertions.Expect(page.Locator("[data-test='debug-site-select']")).ToHaveCountAsync(1); - await Assertions.Expect(page.Locator("[data-test='debug-instance-select']")).ToHaveCountAsync(1); + await Assertions.Expect(page.Locator("[data-test='debug-instance-picker']")).ToHaveCountAsync(1); // No site/instance selected -> Connect is disabled. var connect = page.Locator("button.btn.btn-primary.btn-sm:has-text('Connect')"); @@ -90,11 +93,11 @@ public class DebugViewTests : IClassFixture await page.Locator("[data-test='debug-site-select']") .SelectOptionAsync(new SelectOptionValue { Value = _cluster.SiteAId.ToString() }); - var instanceOption = page.Locator("[data-test='debug-instance-select'] option", - new() { HasText = uniqueName }); - await Assertions.Expect(instanceOption).ToHaveCountAsync(1, new() { Timeout = 10_000 }); - await page.Locator("[data-test='debug-instance-select']") - .SelectOptionAsync(new SelectOptionValue { Value = instanceId.ToString() }); + await page.Locator("[data-test='debug-instance-input']").FillAsync(uniqueName); + var instanceRow = page.Locator( + $"[data-test='debug-instance-row'][data-instance-id='{instanceId}']"); + await Assertions.Expect(instanceRow).ToBeVisibleAsync(new() { Timeout = 10_000 }); + await instanceRow.ClickAsync(); var connect = page.Locator("button.btn.btn-primary.btn-sm:has-text('Connect')"); await Assertions.Expect(connect).ToBeEnabledAsync(new() { Timeout = 10_000 }); diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/DebugViewTreeTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/DebugViewTreeTests.cs index 189ca20d..82953cda 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/DebugViewTreeTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/DebugViewTreeTests.cs @@ -76,11 +76,11 @@ public class DebugViewTreeTests : IClassFixture // Select the seeded site + Enabled instance (same hooks as DebugViewTests). await page.Locator("[data-test='debug-site-select']") .SelectOptionAsync(new SelectOptionValue { Value = _cluster.SiteAId.ToString() }); - var instanceOption = page.Locator("[data-test='debug-instance-select'] option", - new() { HasText = uniqueName }); - await Assertions.Expect(instanceOption).ToHaveCountAsync(1, new() { Timeout = 10_000 }); - await page.Locator("[data-test='debug-instance-select']") - .SelectOptionAsync(new SelectOptionValue { Value = instanceId.ToString() }); + await page.Locator("[data-test='debug-instance-input']").FillAsync(uniqueName); + var instanceRow = page.Locator( + $"[data-test='debug-instance-row'][data-instance-id='{instanceId}']"); + await Assertions.Expect(instanceRow).ToBeVisibleAsync(new() { Timeout = 10_000 }); + await instanceRow.ClickAsync(); var connect = page.Locator("button.btn.btn-primary.btn-sm:has-text('Connect')"); await Assertions.Expect(connect).ToBeEnabledAsync(new() { Timeout = 10_000 }); diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/FlowDebuggerTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/FlowDebuggerTests.cs index c48ea0c1..99e3f395 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/FlowDebuggerTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/FlowDebuggerTests.cs @@ -193,7 +193,9 @@ public class FlowDebuggerTests : IClassFixture /// rig's , skipping the test with a rig-shaped message whenever an /// option is missing — none of these objects is provisioned by the fixture. Site is matched by /// LABEL because its id differs per rig (the seed names it "Test Plant A", identifier - /// site-a); the script is matched by option VALUE, which is its canonical name. + /// site-a); the instance goes through the shared InstancePicker combobox (typed + /// name filters the tree, exact-text row click selects); the script is matched by option VALUE, + /// which is its canonical name. /// private static async Task SelectRigScriptAsync(IPage page, string scriptName) { @@ -206,11 +208,17 @@ public class FlowDebuggerTests : IClassFixture Skip.If(siteLabel is null, "The rig's site-a ('Test Plant A') is not in the Flow Debugger site list — run docker/seed-sites.sh."); await sites.SelectOptionAsync(new SelectOptionValue { Label = siteLabel! }); - var instances = page.Locator("select[data-test='flow-instance-select']"); - var instanceLabel = await WaitForOptionLabelAsync( - page, instances, l => string.Equals(l, RigInstanceName, StringComparison.Ordinal)); - Skip.If(instanceLabel is null, $"Instance {RigInstanceName} is not on the rig's site-a — run docker/seed-delmia-sim.sh."); - await instances.SelectOptionAsync(new SelectOptionValue { Label = instanceLabel! }); + // Instance selection goes through the shared InstancePicker combobox (prefix + // "flow-instance"): typing the name filters the area tree and expands its + // ancestors, then the exact-text row is clicked to select and close the panel. + var instanceInput = page.Locator("[data-test='flow-instance-input']"); + await instanceInput.FillAsync(RigInstanceName); + var instancePanel = page.Locator("[data-test='flow-instance-panel']"); + var instanceRow = instancePanel.GetByText(RigInstanceName, new() { Exact = true }); + Skip.IfNot( + await WaitForCountAsync(page, instanceRow), + $"Instance {RigInstanceName} is not on the rig's site-a — run docker/seed-delmia-sim.sh."); + await instanceRow.ClickAsync(); var scripts = page.Locator("select[data-test='flow-script-select']"); var option = scripts.Locator($"option[value='{scriptName}']"); @@ -223,8 +231,8 @@ public class FlowDebuggerTests : IClassFixture /// /// Polls a <select>'s option labels until one matches, returning it (or null on - /// timeout). The instance and script lists are filled by a server round-trip after the previous - /// picker changes, so the options do not exist at first render. + /// timeout). Used for the site select, whose options are filled by a server round-trip after + /// the page loads, so the options do not exist at first render. /// private static async Task WaitForOptionLabelAsync( IPage page, ILocator select, Func match, int timeoutMs = 15_000)