From c66ef710176f9f19f2f579960d2a3c8f59d60e8a Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 21 May 2026 02:13:02 -0400 Subject: [PATCH] feat(ui): SMTP config form TlsMode field Add a TlsMode read-only row and a None/StartTLS/SSL select to the SMTP Configuration page edit form. New configs default to None; edits load and persist the chosen mode through the repository. --- .../Notifications/SmtpConfiguration.razor | 17 ++- .../Pages/SmtpConfigurationPageTests.cs | 112 ++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 tests/ScadaLink.CentralUI.Tests/Pages/SmtpConfigurationPageTests.cs diff --git a/src/ScadaLink.CentralUI/Components/Pages/Notifications/SmtpConfiguration.razor b/src/ScadaLink.CentralUI/Components/Pages/Notifications/SmtpConfiguration.razor index 3f87a6a..d938b9b 100644 --- a/src/ScadaLink.CentralUI/Components/Pages/Notifications/SmtpConfiguration.razor +++ b/src/ScadaLink.CentralUI/Components/Pages/Notifications/SmtpConfiguration.razor @@ -50,6 +50,8 @@
@smtp.Host:@smtp.Port
Auth Type
@smtp.AuthType
+
TLS Mode
+
@(string.IsNullOrWhiteSpace(smtp.TlsMode) ? "(not set)" : smtp.TlsMode)
From Address
@smtp.FromAddress
Credentials
@@ -73,13 +75,21 @@ -
+
+
+ + +
+/// bUnit rendering tests for the SMTP Configuration page — specifically the TlsMode +/// field added so the UI exposes all five user-relevant SmtpConfiguration fields. +/// +public class SmtpConfigurationPageTests : BunitContext +{ + private void WireAuth() + { + var claims = new[] + { + new Claim("Username", "tester"), + new Claim(ClaimTypes.Role, "Admin"), + }; + var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth")); + Services.AddSingleton(new TestAuthStateProvider(user)); + Services.AddAuthorizationCore(); + } + + private static SmtpConfiguration Sample() => + new("smtp.example.com", "Basic", "noreply@example.com") + { + Id = 1, + Port = 587, + TlsMode = "StartTLS", + Credentials = "user:pass", + }; + + [Fact] + public void EditForm_RendersTlsModeSelectWithAllThreeModes() + { + var repo = Substitute.For(); + repo.GetAllSmtpConfigurationsAsync() + .Returns(Task.FromResult>( + new List { Sample() })); + Services.AddSingleton(repo); + WireAuth(); + + var cut = Render(); + cut.WaitForState(() => cut.Markup.Contains("smtp.example.com")); + + cut.FindAll("button").First(b => b.TextContent.Contains("Edit")).Click(); + + cut.WaitForAssertion(() => + { + var selects = cut.FindAll("select"); + var tlsSelect = selects.Single(s => s.QuerySelectorAll("option") + .Any(o => o.TextContent == "StartTLS")); + var modes = tlsSelect.QuerySelectorAll("option").Select(o => o.TextContent).ToList(); + Assert.Equal(new[] { "None", "StartTLS", "SSL" }, modes); + }); + } + + [Fact] + public void ReadOnlyView_ShowsTlsMode() + { + var repo = Substitute.For(); + repo.GetAllSmtpConfigurationsAsync() + .Returns(Task.FromResult>( + new List { Sample() })); + Services.AddSingleton(repo); + WireAuth(); + + var cut = Render(); + + cut.WaitForAssertion(() => + { + Assert.Contains("TLS Mode", cut.Markup); + Assert.Contains("StartTLS", cut.Markup); + }); + } + + [Fact] + public void SavingEdit_PersistsChosenTlsMode() + { + var config = Sample(); + var repo = Substitute.For(); + repo.GetAllSmtpConfigurationsAsync() + .Returns(Task.FromResult>( + new List { config })); + Services.AddSingleton(repo); + WireAuth(); + + var cut = Render(); + cut.WaitForState(() => cut.Markup.Contains("smtp.example.com")); + + cut.FindAll("button").First(b => b.TextContent.Contains("Edit")).Click(); + + var tlsSelect = cut.FindAll("select") + .Single(s => s.QuerySelectorAll("option").Any(o => o.TextContent == "StartTLS")); + tlsSelect.Change("SSL"); + + cut.FindAll("button").First(b => b.TextContent.Contains("Save")).Click(); + + cut.WaitForAssertion(() => + { + repo.Received().UpdateSmtpConfigurationAsync( + Arg.Is(c => c.TlsMode == "SSL")); + repo.Received().SaveChangesAsync(); + }); + } +}