1475991fd6
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
52 lines
2.2 KiB
C#
52 lines
2.2 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
|
|
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 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
|
|
{
|
|
/// <summary>EditForm pages whose validation messages are rendered by a child/section component.</summary>
|
|
private static readonly HashSet<string> AllowList = new(StringComparer.Ordinal);
|
|
|
|
[Fact]
|
|
public void Every_razor_with_an_EditForm_and_a_DataAnnotationsValidator_renders_validation_messages()
|
|
{
|
|
var offenders = new List<string>();
|
|
foreach (var file in Directory.EnumerateFiles(ComponentsDir(), "*.razor", SearchOption.AllDirectories))
|
|
{
|
|
var text = File.ReadAllText(file);
|
|
if (!text.Contains("<EditForm") || !text.Contains("<DataAnnotationsValidator")) { continue; }
|
|
if (text.Contains("<ValidationSummary") || text.Contains("<ValidationMessage")) { continue; }
|
|
var name = Path.GetRelativePath(ComponentsDir(), file).Replace('\\', '/');
|
|
if (!AllowList.Contains(name)) { offenders.Add(name); }
|
|
}
|
|
|
|
offenders.ShouldBeEmpty("these forms validate but never show a message: " + string.Join(", ", offenders));
|
|
}
|
|
|
|
[Fact]
|
|
public void ApiKeys_delete_button_goes_through_the_confirm_dialog()
|
|
{
|
|
var text = File.ReadAllText(Path.Combine(ComponentsDir(), "Pages", "ApiKeys.razor"));
|
|
text.ShouldContain("<RawConfirmDialog");
|
|
text.ShouldNotContain("DeleteAsync(k.ApiKeyId)");
|
|
}
|
|
|
|
private static string ComponentsDir()
|
|
{
|
|
var dir = new DirectoryInfo(AppContext.BaseDirectory);
|
|
while (dir is not null)
|
|
{
|
|
var candidate = Path.Combine(dir.FullName, "src", "Server", "ZB.MOM.WW.OtOpcUa.AdminUI", "Components");
|
|
if (Directory.Exists(candidate)) { return candidate; }
|
|
dir = dir.Parent;
|
|
}
|
|
throw new DirectoryNotFoundException("AdminUI Components directory not found above the test binaries.");
|
|
}
|
|
}
|