From f89f234558982182bf46bafc3114bcc374887d3c Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Tue, 12 May 2026 02:28:47 -0400 Subject: [PATCH] test(ui): failing bUnit tests for OpcUaEndpointEditor new sections Adds 10 new tests covering: - Authentication section label + Enable/Remove toggle (creates/nulls UserIdentity) - TokenType conditional rendering: UsernamePassword shows Username/Password, X509Certificate shows Certificate path/password, Anonymous shows no extras - Deadband Enable/Remove toggle - Advanced Subscription section labels (Discard oldest, Subscription display name, Subscription priority, Timestamps to return) - UserIdentity per-field error rendering under Username 9 new tests fail because the editor component hasn't been extended yet (TDD red phase). Layer E2 implements the sections. --- .../Forms/OpcUaEndpointEditorTests.cs | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/tests/ScadaLink.CentralUI.Tests/Forms/OpcUaEndpointEditorTests.cs b/tests/ScadaLink.CentralUI.Tests/Forms/OpcUaEndpointEditorTests.cs index e6b1da0..b8b5f53 100644 --- a/tests/ScadaLink.CentralUI.Tests/Forms/OpcUaEndpointEditorTests.cs +++ b/tests/ScadaLink.CentralUI.Tests/Forms/OpcUaEndpointEditorTests.cs @@ -82,4 +82,149 @@ public class OpcUaEndpointEditorTests : BunitContext Assert.Contains("alert-warning", cut.Markup); Assert.Contains("migrated from a legacy format", cut.Markup); } + + // ── Layer E: new editor sections ── + + [Fact] + public void Renders_Authentication_Section_Label() + { + var cut = Render(p => p.Add(c => c.Config, new OpcUaEndpointConfig())); + Assert.Contains("Authentication", cut.Markup); + } + + [Fact] + public void EnableAuthentication_CreatesUserIdentitySubObject() + { + var config = new OpcUaEndpointConfig(); + var cut = Render(p => p.Add(c => c.Config, config)); + + Assert.Null(config.UserIdentity); + cut.FindAll("button").First(b => b.TextContent.Contains("Enable Authentication")).Click(); + + Assert.NotNull(config.UserIdentity); + Assert.Equal(OpcUaUserTokenType.Anonymous, config.UserIdentity!.TokenType); + } + + [Fact] + public void RemoveAuthentication_NullsUserIdentity() + { + var config = new OpcUaEndpointConfig + { + UserIdentity = new OpcUaUserIdentityConfig + { + TokenType = OpcUaUserTokenType.UsernamePassword, + Username = "alice", + Password = "secret" + } + }; + var cut = Render(p => p.Add(c => c.Config, config)); + + cut.FindAll("button").First(b => b.TextContent.Contains("Remove Authentication")).Click(); + + Assert.Null(config.UserIdentity); + } + + [Fact] + public void UsernamePassword_RendersUsernameAndPasswordInputs() + { + var config = new OpcUaEndpointConfig + { + UserIdentity = new OpcUaUserIdentityConfig + { + TokenType = OpcUaUserTokenType.UsernamePassword + } + }; + var cut = Render(p => p.Add(c => c.Config, config)); + + Assert.Contains("Username", cut.Markup); + Assert.Contains("Password", cut.Markup); + Assert.DoesNotContain("Certificate path", cut.Markup); + } + + [Fact] + public void X509Certificate_RendersCertificateFields() + { + var config = new OpcUaEndpointConfig + { + UserIdentity = new OpcUaUserIdentityConfig + { + TokenType = OpcUaUserTokenType.X509Certificate + } + }; + var cut = Render(p => p.Add(c => c.Config, config)); + + Assert.Contains("Certificate path", cut.Markup); + Assert.Contains("Certificate password", cut.Markup); + Assert.DoesNotContain("Username", cut.Markup); + } + + [Fact] + public void AnonymousTokenType_ShowsNoExtraFields() + { + var config = new OpcUaEndpointConfig + { + UserIdentity = new OpcUaUserIdentityConfig { TokenType = OpcUaUserTokenType.Anonymous } + }; + var cut = Render(p => p.Add(c => c.Config, config)); + + Assert.DoesNotContain("Username", cut.Markup); + Assert.DoesNotContain("Certificate path", cut.Markup); + } + + [Fact] + public void EnableDeadband_CreatesDeadbandSubObject() + { + var config = new OpcUaEndpointConfig(); + var cut = Render(p => p.Add(c => c.Config, config)); + + Assert.Null(config.Deadband); + cut.FindAll("button").First(b => b.TextContent.Contains("Enable Deadband")).Click(); + + Assert.NotNull(config.Deadband); + } + + [Fact] + public void RemoveDeadband_NullsDeadband() + { + var config = new OpcUaEndpointConfig + { + Deadband = new OpcUaDeadbandConfig { Type = OpcUaDeadbandType.Percent, Value = 1.5 } + }; + var cut = Render(p => p.Add(c => c.Config, config)); + + cut.FindAll("button").First(b => b.TextContent.Contains("Remove Deadband")).Click(); + + Assert.Null(config.Deadband); + } + + [Fact] + public void AdvancedSubscription_Section_Renders() + { + var config = new OpcUaEndpointConfig(); + var cut = Render(p => p.Add(c => c.Config, config)); + + Assert.Contains("Discard oldest", cut.Markup); + Assert.Contains("Subscription display name", cut.Markup); + Assert.Contains("Subscription priority", cut.Markup); + Assert.Contains("Timestamps to return", cut.Markup); + } + + [Fact] + public void UserIdentityError_RendersPerFieldUnderUsername() + { + var config = new OpcUaEndpointConfig + { + UserIdentity = new OpcUaUserIdentityConfig + { + TokenType = OpcUaUserTokenType.UsernamePassword, + Username = "" + } + }; + var errors = OpcUaEndpointConfigValidator.Validate(config, "Primary."); + var cut = Render(p => p + .Add(c => c.Config, config) + .Add(c => c.Errors, errors)); + + Assert.Contains("Username is required", cut.Markup); + } }