Merge branch 'worktree-agent-acdd9dc794506dee9' into feat/instance-picker
This commit is contained in:
+15
-12
@@ -12,15 +12,18 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Deployment;
|
|||||||
/// without hanging.
|
/// without hanging.
|
||||||
///
|
///
|
||||||
/// <para>
|
/// <para>
|
||||||
/// <b>The Enabled-only dropdown + deploy precondition (verified against
|
/// <b>The Enabled-only instance picker + deploy precondition (verified against
|
||||||
/// <c>DebugView.razor::LoadInstancesForSite</c>):</b> the instance <c><select></c>
|
/// <c>DebugView.razor::LoadInstancesForSite</c>):</b> the instance
|
||||||
/// lists ONLY instances whose config state is <c>Enabled</c>
|
/// <c>InstancePicker</c> combobox (prefix <c>debug-instance</c>) lists ONLY
|
||||||
|
/// instances whose config state is <c>Enabled</c>
|
||||||
/// (<c>i.State == InstanceState.Enabled</c>). A freshly-minted instance is
|
/// (<c>i.State == InstanceState.Enabled</c>). A freshly-minted instance is
|
||||||
/// <c>NotDeployed</c> and would never appear, so Fact B first drives the instance
|
/// <c>NotDeployed</c> and would never appear, so Fact B first drives the instance
|
||||||
/// to <c>Enabled</c> over the CLI (<c>instance deploy</c>) before it can be picked
|
/// to <c>Enabled</c> over the CLI (<c>instance deploy</c>) before it can be picked
|
||||||
/// in the dropdown. Both <c><select></c>s carry the <c>data-test</c> hooks
|
/// in the picker. The site <c><select></c> carries the <c>data-test</c> hook
|
||||||
/// (<c>debug-site-select</c> / <c>debug-instance-select</c>, option value = entity
|
/// <c>debug-site-select</c> (option value = entity id); the instance picker carries
|
||||||
/// id); Fact A asserts those hooks are served, which doubles as proof the cluster
|
/// the shared <c>InstancePicker</c> hooks (<c>debug-instance-picker</c>/<c>-input</c>/
|
||||||
|
/// <c>-toggle</c>/<c>-panel</c>/<c>-row</c>, row <c>data-instance-id</c> = entity id).
|
||||||
|
/// Fact A asserts those hooks are served, which doubles as proof the cluster
|
||||||
/// rebuild that added them actually took.
|
/// rebuild that added them actually took.
|
||||||
/// </para>
|
/// </para>
|
||||||
///
|
///
|
||||||
@@ -65,7 +68,7 @@ public class DebugViewTests : IClassFixture<DeploymentFixture>
|
|||||||
await Assertions.Expect(page.Locator("h4:has-text('Debug View')")).ToBeVisibleAsync();
|
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).
|
// 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-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.
|
// No site/instance selected -> Connect is disabled.
|
||||||
var connect = page.Locator("button.btn.btn-primary.btn-sm:has-text('Connect')");
|
var connect = page.Locator("button.btn.btn-primary.btn-sm:has-text('Connect')");
|
||||||
@@ -90,11 +93,11 @@ public class DebugViewTests : IClassFixture<DeploymentFixture>
|
|||||||
|
|
||||||
await page.Locator("[data-test='debug-site-select']")
|
await page.Locator("[data-test='debug-site-select']")
|
||||||
.SelectOptionAsync(new SelectOptionValue { Value = _cluster.SiteAId.ToString() });
|
.SelectOptionAsync(new SelectOptionValue { Value = _cluster.SiteAId.ToString() });
|
||||||
var instanceOption = page.Locator("[data-test='debug-instance-select'] option",
|
await page.Locator("[data-test='debug-instance-input']").FillAsync(uniqueName);
|
||||||
new() { HasText = uniqueName });
|
var instanceRow = page.Locator(
|
||||||
await Assertions.Expect(instanceOption).ToHaveCountAsync(1, new() { Timeout = 10_000 });
|
$"[data-test='debug-instance-row'][data-instance-id='{instanceId}']");
|
||||||
await page.Locator("[data-test='debug-instance-select']")
|
await Assertions.Expect(instanceRow).ToBeVisibleAsync(new() { Timeout = 10_000 });
|
||||||
.SelectOptionAsync(new SelectOptionValue { Value = instanceId.ToString() });
|
await instanceRow.ClickAsync();
|
||||||
|
|
||||||
var connect = page.Locator("button.btn.btn-primary.btn-sm:has-text('Connect')");
|
var connect = page.Locator("button.btn.btn-primary.btn-sm:has-text('Connect')");
|
||||||
await Assertions.Expect(connect).ToBeEnabledAsync(new() { Timeout = 10_000 });
|
await Assertions.Expect(connect).ToBeEnabledAsync(new() { Timeout = 10_000 });
|
||||||
|
|||||||
+5
-5
@@ -76,11 +76,11 @@ public class DebugViewTreeTests : IClassFixture<DeploymentFixture>
|
|||||||
// Select the seeded site + Enabled instance (same hooks as DebugViewTests).
|
// Select the seeded site + Enabled instance (same hooks as DebugViewTests).
|
||||||
await page.Locator("[data-test='debug-site-select']")
|
await page.Locator("[data-test='debug-site-select']")
|
||||||
.SelectOptionAsync(new SelectOptionValue { Value = _cluster.SiteAId.ToString() });
|
.SelectOptionAsync(new SelectOptionValue { Value = _cluster.SiteAId.ToString() });
|
||||||
var instanceOption = page.Locator("[data-test='debug-instance-select'] option",
|
await page.Locator("[data-test='debug-instance-input']").FillAsync(uniqueName);
|
||||||
new() { HasText = uniqueName });
|
var instanceRow = page.Locator(
|
||||||
await Assertions.Expect(instanceOption).ToHaveCountAsync(1, new() { Timeout = 10_000 });
|
$"[data-test='debug-instance-row'][data-instance-id='{instanceId}']");
|
||||||
await page.Locator("[data-test='debug-instance-select']")
|
await Assertions.Expect(instanceRow).ToBeVisibleAsync(new() { Timeout = 10_000 });
|
||||||
.SelectOptionAsync(new SelectOptionValue { Value = instanceId.ToString() });
|
await instanceRow.ClickAsync();
|
||||||
|
|
||||||
var connect = page.Locator("button.btn.btn-primary.btn-sm:has-text('Connect')");
|
var connect = page.Locator("button.btn.btn-primary.btn-sm:has-text('Connect')");
|
||||||
await Assertions.Expect(connect).ToBeEnabledAsync(new() { Timeout = 10_000 });
|
await Assertions.Expect(connect).ToBeEnabledAsync(new() { Timeout = 10_000 });
|
||||||
|
|||||||
+16
-8
@@ -193,7 +193,9 @@ public class FlowDebuggerTests : IClassFixture<FlowDebuggerFixture>
|
|||||||
/// rig's <see cref="RigInstanceName"/>, skipping the test with a rig-shaped message whenever an
|
/// rig's <see cref="RigInstanceName"/>, 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
|
/// 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
|
/// LABEL because its id differs per rig (the seed names it "Test Plant A", identifier
|
||||||
/// <c>site-a</c>); the script is matched by option VALUE, which is its canonical name.
|
/// <c>site-a</c>); the instance goes through the shared <c>InstancePicker</c> combobox (typed
|
||||||
|
/// name filters the tree, exact-text row click selects); the script is matched by option VALUE,
|
||||||
|
/// which is its canonical name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static async Task<ILocator> SelectRigScriptAsync(IPage page, string scriptName)
|
private static async Task<ILocator> SelectRigScriptAsync(IPage page, string scriptName)
|
||||||
{
|
{
|
||||||
@@ -206,11 +208,17 @@ public class FlowDebuggerTests : IClassFixture<FlowDebuggerFixture>
|
|||||||
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.");
|
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! });
|
await sites.SelectOptionAsync(new SelectOptionValue { Label = siteLabel! });
|
||||||
|
|
||||||
var instances = page.Locator("select[data-test='flow-instance-select']");
|
// Instance selection goes through the shared InstancePicker combobox (prefix
|
||||||
var instanceLabel = await WaitForOptionLabelAsync(
|
// "flow-instance"): typing the name filters the area tree and expands its
|
||||||
page, instances, l => string.Equals(l, RigInstanceName, StringComparison.Ordinal));
|
// ancestors, then the exact-text row is clicked to select and close the panel.
|
||||||
Skip.If(instanceLabel is null, $"Instance {RigInstanceName} is not on the rig's site-a — run docker/seed-delmia-sim.sh.");
|
var instanceInput = page.Locator("[data-test='flow-instance-input']");
|
||||||
await instances.SelectOptionAsync(new SelectOptionValue { Label = instanceLabel! });
|
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 scripts = page.Locator("select[data-test='flow-script-select']");
|
||||||
var option = scripts.Locator($"option[value='{scriptName}']");
|
var option = scripts.Locator($"option[value='{scriptName}']");
|
||||||
@@ -223,8 +231,8 @@ public class FlowDebuggerTests : IClassFixture<FlowDebuggerFixture>
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Polls a <c><select></c>'s option labels until one matches, returning it (or null on
|
/// Polls a <c><select></c>'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
|
/// timeout). Used for the site select, whose options are filled by a server round-trip after
|
||||||
/// picker changes, so the options do not exist at first render.
|
/// the page loads, so the options do not exist at first render.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static async Task<string?> WaitForOptionLabelAsync(
|
private static async Task<string?> WaitForOptionLabelAsync(
|
||||||
IPage page, ILocator select, Func<string, bool> match, int timeoutMs = 15_000)
|
IPage page, ILocator select, Func<string, bool> match, int timeoutMs = 15_000)
|
||||||
|
|||||||
Reference in New Issue
Block a user