d3bca7afd4
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
61 lines
2.6 KiB
C#
61 lines
2.6 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 layout must keep its error boundary, 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 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()
|
|
{
|
|
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.");
|
|
}
|
|
}
|