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 9f0d5119..b7d36afb 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/TopologyAreaTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Deployment/TopologyAreaTests.cs @@ -6,8 +6,11 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Deployment; /// /// E2E coverage for the Topology page's area authoring surface — creating an area -/// from the toolbar "+ Area" dialog (the site-picker variant), and the inline -/// double-click rename on an area node's label. +/// from the toolbar "+ Area" dialog (the site-picker variant), the inline +/// double-click rename on an area node's label, the right-click "Move to Area…" +/// flows for both an area (MoveAreaDialog) and an instance +/// (MoveInstanceDialog), and the read-only "Diff" comparison +/// (DiffDialog) for a deployed instance. /// /// /// Every fact opens the page through , which @@ -41,6 +44,26 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Deployment; /// ). A CLI read-back after each action /// confirms the change actually persisted, not just that the toast appeared. /// +/// +/// +/// Move flows: both "Move to Area…" context items open a one-<select> +/// modal whose options carry the area name as their text (root-level areas render +/// without indentation, so an exact visible-text match resolves). Unlike a text +/// input, an HTML <select> fires onchange immediately on +/// selection, so +/// commits the @bind directly (no DispatchEventAsync dance). The +/// area dialog title is "Move area '…' to…" (h6) and the instance dialog +/// "Move '…' to…" (h6); both succeed with a single "moved" toast. +/// +/// +/// +/// Diff: the instance "Diff" item is disabled for a NotDeployed +/// instance, so the Diff fact deploys first (over the CLI) to enable it. The +/// resulting DiffDialog is computed centrally (no site relay), so the +/// comparison is deterministic for a deployed instance; its title is +/// "Deployment Diff — <uniqueName>" (h5, em-dash) and it closes via +/// the footer Close button. +/// /// [Collection("Playwright")] public class TopologyAreaTests : IClassFixture @@ -157,6 +180,113 @@ public class TopologyAreaTests : IClassFixture } } + [SkippableFact] + public async Task MoveArea_UnderAnotherArea_ShowsMovedToast() + { + Skip.IfNot(_cluster.Available, ClusterAvailability.SkipReason); + + var parentName = CliRunner.UniqueName("mvpar"); + var childName = CliRunner.UniqueName("mvchild"); + var parentId = await CliRunner.CreateAreaAsync(_cluster.SiteAId, parentName); + var childId = await CliRunner.CreateAreaAsync(_cluster.SiteAId, childName); + try + { + var page = await OpenStableTopologyAsync(_pw); + + var childRow = page.Locator("div.tv-row", new() { HasText = childName }); + await Assertions.Expect(childRow).ToBeVisibleAsync(new() { Timeout = 10_000 }); + await childRow.ScrollIntoViewIfNeededAsync(); + await childRow.ClickAsync(new() { Button = MouseButton.Right }); + await page.Locator(".dropdown-menu.show button.dropdown-item:has-text('Move to Area')").ClickAsync(); + + // MoveAreaDialog title is "Move area '' to…" (h6) — distinct from the + // instance dialog ("Move '' to…"), so scope by the "Move area" text. + var dialog = page.Locator(".modal.show:has(h6.modal-title:has-text('Move area'))"); + await Assertions.Expect(dialog).ToBeVisibleAsync(); + // Root-level area options render without indentation, so the parent's name is + // the option's exact visible text. A