From 52726cf30d091a349c77904a84f389fd94c0e6ee Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Tue, 8 Sep 2026 10:01:22 -0400 Subject: [PATCH] fix(central-ui): Debug View reconnect guards the stored instance; drop select-era sort; deep-link-in-area test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three final-review findings on the two picker pages: 1. DebugView's localStorage auto-reconnect restored the stored instance id with no membership check, where the ?instanceId= path has one — a stored instance that is no longer Enabled (or was deleted, or moved sites) is filtered out of the list the picker is handed, so the field rendered blank while the page held a non-zero id and auto-connected to it anyway. The guard now mirrors the deep-link path: forget the stored instance id, tell the operator, and leave the site selected and loaded so they can pick again. 2. FlowDebugger's _instances kept an OrderBy(UniqueName) from the flat . + _instances = (await TemplateEngineRepository.GetInstancesBySiteIdAsync(_selectedSiteId)).ToList(); _areas = (await TemplateEngineRepository.GetAreasBySiteIdAsync(_selectedSiteId)).ToList(); } finally diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Deployment/DebugViewInstancePickerTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Deployment/DebugViewInstancePickerTests.cs index 31b7e37c..b7273e81 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Deployment/DebugViewInstancePickerTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Deployment/DebugViewInstancePickerTests.cs @@ -186,6 +186,31 @@ public class DebugViewInstancePickerTests : BunitContext Assert.False(cut.Find("[data-test='debug-instance-input']").HasAttribute("disabled"))); } + /// + /// The localStorage auto-reconnect must apply the same membership guard the + /// ?instanceId= deep link does. Off01 is Disabled, so the page filters it out of the + /// list the picker is handed — restoring it anyway left the field blank while the page held + /// a non-zero id and auto-connected to it. The stored id is dropped instead, and the site + /// stays selected and loaded so the operator can pick again. + /// + [Fact] + public void AutoReconnect_FromStorage_SkipsAnInstanceThatIsNoLongerAvailable() + { + RegisterServices(); + JSInterop.Setup("localStorage.getItem", "debugView.siteId").SetResult(SiteId.ToString()); + JSInterop.Setup("localStorage.getItem", "debugView.instanceId").SetResult("8"); + + var cut = Render(); + + cut.WaitForAssertion(() => + Assert.Equal(string.Empty, cut.Find("[data-test='debug-instance-input']").GetAttribute("value"))); + + // Nothing is selected, so Connect stays disabled — and the stale id is forgotten. + var connect = cut.FindAll("button.btn.btn-primary.btn-sm").First(b => b.TextContent.Contains("Connect")); + Assert.True(connect.HasAttribute("disabled")); + JSInterop.VerifyInvoke("localStorage.removeItem"); + } + [Fact] public void WhileConnected_ThePickerIsDisabled() { diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Deployment/FlowDebuggerPageTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Deployment/FlowDebuggerPageTests.cs index 1b0e19d0..6bde8719 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Deployment/FlowDebuggerPageTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Deployment/FlowDebuggerPageTests.cs @@ -816,6 +816,37 @@ public class FlowDebuggerPageTests : BunitContext Assert.Equal("Sim01", cut.Find("[data-test='flow-instance-input']").GetAttribute("value"))); } + /// + /// The same deep link, but the instance sits two areas deep. The picker resolves the field + /// text from SelectedInstanceId against the flat instance list, not by walking the + /// tree — so a nested instance must show its name with the panel never opened (and therefore + /// with Line1 / Cell A never expanded). + /// + [Fact] + public void ScriptEntry_DeepLink_InstanceIdInsideAnArea_ShowsItsNameWithoutOpeningThePanel() + { + RegisterServices(); + WireInstance(); + _templates.GetAreasBySiteIdAsync(1, Arg.Any()) + .Returns(Task.FromResult>( + [new Area("Line1") { Id = 1, SiteId = 1 }, new Area("Cell A") { Id = 2, SiteId = 1, ParentAreaId = 1 }])); + var nested = new Instance("Sim01") { Id = 7, SiteId = 1, TemplateId = 10, AreaId = 2 }; + _templates.GetInstancesBySiteIdAsync(1, Arg.Any()) + .Returns(Task.FromResult>([nested])); + _templates.GetInstanceByIdAsync(7, Arg.Any()).Returns(nested); + + var nav = Services.GetRequiredService(); + nav.NavigateTo("/deployment/flow-debugger?instanceId=7"); + + var cut = Render(); + + cut.WaitForAssertion(() => + Assert.Equal("Sim01", cut.Find("[data-test='flow-instance-input']").GetAttribute("value"))); + // The panel was never opened, so nothing of the tree is rendered. + Assert.Empty(cut.FindAll("[data-test='flow-instance-panel']")); + Assert.Empty(cut.FindAll("[data-test='flow-instance-row']")); + } + /// /// The picker must stay disabled for the WHOLE site load, not just until the site id lands. /// Blazor renders as soon as OnSiteChangedAsync hits its first await, and at that point