fix(adminui,client): error boundary inside the interactive scope, stale-commit OnSaved, AbCip browse delay emit, DisposeBridge cancel (review R3)

M1: PageErrorBoundary wraps the body of every InteractiveServer page; the inert layout boundary is removed.
m1: DisposeBridge cancels its CTS when the 5 s wait times out.
m2: a CSV import that commits after the modal closed still raises OnSaved.
m3: document SecurityMode=None no-fallback in Client.CLI.md / Client.UI.md.
m4: browsePageDelayMs written only when loaded or changed; negative flagged.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-08 07:31:19 -04:00
parent ca0f1e7735
commit 1475991fd6
39 changed files with 478 additions and 48 deletions
@@ -5,7 +5,7 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Raw;
/// <summary>
/// Issue #604 source-scan guards (Razor has no bUnit): an <c>EditForm</c> with a validator but nothing
/// to show its messages is a silent no-op Save, the layout must keep its error boundary, and the
/// to show its messages is a silent no-op Save, the interactive pages' error boundary is pinned in PageErrorBoundaryTests, and the
/// API-key Delete must not fire straight from the row button.
/// </summary>
public sealed class AdminUiFormSweepGuardTests
@@ -29,15 +29,6 @@ public sealed class AdminUiFormSweepGuardTests
offenders.ShouldBeEmpty("these forms validate but never show a message: " + string.Join(", ", offenders));
}
[Fact]
public void MainLayout_wraps_the_body_in_an_ErrorBoundary()
{
var text = File.ReadAllText(Path.Combine(ComponentsDir(), "Layout", "MainLayout.razor"));
var boundary = text.IndexOf("<ErrorBoundary", StringComparison.Ordinal);
boundary.ShouldBeGreaterThan(-1);
text.IndexOf("@Body", StringComparison.Ordinal).ShouldBeGreaterThan(boundary);
}
[Fact]
public void ApiKeys_delete_button_goes_through_the_confirm_dialog()
{