using JdeScoping.ConfigManager.Services; using JdeScoping.ConfigManager.ViewModels.Forms; namespace JdeScoping.ConfigManager.Tests.ViewModels.Forms; public class SecretFormViewModelTests { private readonly IClipboardService _clipboardService; public SecretFormViewModelTests() { _clipboardService = Substitute.For(); } [Fact] public void Constructor_SetsPropertiesCorrectly() { // Arrange & Act var sut = new SecretFormViewModel( "API_KEY", "secret-value-123", _clipboardService, _ => { }, () => { }); // Assert sut.Key.ShouldBe("API_KEY"); sut.Value.ShouldBe("secret-value-123"); sut.IsValueVisible.ShouldBeFalse(); } [Fact] public void Constructor_WithNullValue_SetsEmptyString() { // Arrange & Act var sut = new SecretFormViewModel( "API_KEY", null!, _clipboardService, _ => { }, () => { }); // Assert sut.Value.ShouldBe(string.Empty); } [Fact] public void Value_Setter_InvokesCallback() { // Arrange string? changedValue = null; var sut = new SecretFormViewModel( "API_KEY", "initial", _clipboardService, v => changedValue = v, () => { }); // Act sut.Value = "new-value"; // Assert changedValue.ShouldBe("new-value"); } [Fact] public void Value_Setter_RaisesPropertyChanged() { // Arrange var sut = new SecretFormViewModel( "API_KEY", "initial", _clipboardService, _ => { }, () => { }); var propertyChangedRaised = false; sut.PropertyChanged += (s, e) => { if (e.PropertyName == nameof(SecretFormViewModel.Value)) propertyChangedRaised = true; }; // Act sut.Value = "new-value"; // Assert propertyChangedRaised.ShouldBeTrue(); } [Fact] public void Value_Setter_RaisesDisplayValuePropertyChanged() { // Arrange var sut = new SecretFormViewModel( "API_KEY", "initial", _clipboardService, _ => { }, () => { }); var displayValueChangedRaised = false; sut.PropertyChanged += (s, e) => { if (e.PropertyName == nameof(SecretFormViewModel.DisplayValue)) displayValueChangedRaised = true; }; // Act sut.Value = "new-value"; // Assert displayValueChangedRaised.ShouldBeTrue(); } [Fact] public void IsValueVisible_Toggle_Works() { // Arrange var sut = new SecretFormViewModel( "API_KEY", "secret", _clipboardService, _ => { }, () => { }); // Act & Assert - Initial state sut.IsValueVisible.ShouldBeFalse(); // Act - Toggle on sut.IsValueVisible = true; sut.IsValueVisible.ShouldBeTrue(); // Act - Toggle off sut.IsValueVisible = false; sut.IsValueVisible.ShouldBeFalse(); } [Fact] public void IsValueVisible_RaisesPropertyChanged() { // Arrange var sut = new SecretFormViewModel( "API_KEY", "secret", _clipboardService, _ => { }, () => { }); var propertyChangedRaised = false; sut.PropertyChanged += (s, e) => { if (e.PropertyName == nameof(SecretFormViewModel.IsValueVisible)) propertyChangedRaised = true; }; // Act sut.IsValueVisible = true; // Assert propertyChangedRaised.ShouldBeTrue(); } [Fact] public void IsValueVisible_RaisesDisplayValueAndVisibilityButtonTextPropertyChanged() { // Arrange var sut = new SecretFormViewModel( "API_KEY", "secret", _clipboardService, _ => { }, () => { }); var displayValueChangedRaised = false; var visibilityButtonTextChangedRaised = false; sut.PropertyChanged += (s, e) => { if (e.PropertyName == nameof(SecretFormViewModel.DisplayValue)) displayValueChangedRaised = true; if (e.PropertyName == nameof(SecretFormViewModel.VisibilityButtonText)) visibilityButtonTextChangedRaised = true; }; // Act sut.IsValueVisible = true; // Assert displayValueChangedRaised.ShouldBeTrue(); visibilityButtonTextChangedRaised.ShouldBeTrue(); } [Fact] public void DisplayValue_ShowsMaskedValue_WhenNotVisible() { // Arrange var sut = new SecretFormViewModel( "API_KEY", "secret123", _clipboardService, _ => { }, () => { }); // Act & Assert sut.IsValueVisible.ShouldBeFalse(); sut.DisplayValue.ShouldBe("\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"); // 9 bullet points } [Fact] public void DisplayValue_ShowsActualValue_WhenVisible() { // Arrange var sut = new SecretFormViewModel( "API_KEY", "secret123", _clipboardService, _ => { }, () => { }); // Act sut.IsValueVisible = true; // Assert sut.DisplayValue.ShouldBe("secret123"); } [Fact] public void DisplayValue_LimitsMaskedLength_ToTwentyCharacters() { // Arrange var longValue = new string('x', 50); var sut = new SecretFormViewModel( "API_KEY", longValue, _clipboardService, _ => { }, () => { }); // Act & Assert sut.DisplayValue.Length.ShouldBe(20); sut.DisplayValue.ShouldBe(new string('\u2022', 20)); } [Fact] public void DisplayValue_HandlesEmptyValue() { // Arrange var sut = new SecretFormViewModel( "API_KEY", "", _clipboardService, _ => { }, () => { }); // Act & Assert sut.DisplayValue.ShouldBe(""); } [Fact] public void VisibilityButtonText_ShowsShow_WhenHidden() { // Arrange var sut = new SecretFormViewModel( "API_KEY", "secret", _clipboardService, _ => { }, () => { }); // Act & Assert sut.VisibilityButtonText.ShouldBe("Show"); } [Fact] public void VisibilityButtonText_ShowsHide_WhenVisible() { // Arrange var sut = new SecretFormViewModel( "API_KEY", "secret", _clipboardService, _ => { }, () => { }); // Act sut.IsValueVisible = true; // Assert sut.VisibilityButtonText.ShouldBe("Hide"); } [Fact] public void ToggleVisibilityCommand_TogglesVisibility() { // Arrange var sut = new SecretFormViewModel( "API_KEY", "secret", _clipboardService, _ => { }, () => { }); // Act & Assert - Toggle on sut.ToggleVisibilityCommand.Execute(null); sut.IsValueVisible.ShouldBeTrue(); // Act & Assert - Toggle off sut.ToggleVisibilityCommand.Execute(null); sut.IsValueVisible.ShouldBeFalse(); } [Fact] public async Task CopyToClipboardCommand_CopiesValueToClipboard() { // Arrange _clipboardService.SetTextAsync(Arg.Any()).Returns(Task.CompletedTask); var sut = new SecretFormViewModel( "API_KEY", "secret-to-copy", _clipboardService, _ => { }, () => { }); // Act sut.CopyToClipboardCommand.Execute(null); // Small delay to allow async command to complete await Task.Delay(50); // Assert await _clipboardService.Received(1).SetTextAsync("secret-to-copy"); } [Fact] public void DeleteCommand_InvokesCallback() { // Arrange var deleteCalled = false; var sut = new SecretFormViewModel( "API_KEY", "secret", _clipboardService, _ => { }, () => deleteCalled = true); // Act sut.DeleteCommand.Execute(null); // Assert deleteCalled.ShouldBeTrue(); } [Fact] public void Constructor_ThrowsOnNullKey() { // Act & Assert Should.Throw(() => new SecretFormViewModel(null!, "value", _clipboardService, _ => { }, () => { })); } [Fact] public void Constructor_ThrowsOnNullClipboardService() { // Act & Assert Should.Throw(() => new SecretFormViewModel("key", "value", null!, _ => { }, () => { })); } [Fact] public void Constructor_ThrowsOnNullValueChangedCallback() { // Act & Assert Should.Throw(() => new SecretFormViewModel("key", "value", _clipboardService, null!, () => { })); } [Fact] public void Constructor_ThrowsOnNullDeleteCallback() { // Act & Assert Should.Throw(() => new SecretFormViewModel("key", "value", _clipboardService, _ => { }, null!)); } [Fact] public void Key_IsReadOnly() { // Assert - Verify Key property is get-only var keyProperty = typeof(SecretFormViewModel).GetProperty(nameof(SecretFormViewModel.Key)); keyProperty!.CanWrite.ShouldBeFalse(); } [Fact] public void Value_DoesNotInvokeCallback_WhenValueUnchanged() { // Arrange var callbackCount = 0; var sut = new SecretFormViewModel( "API_KEY", "same-value", _clipboardService, _ => callbackCount++, () => { }); // Act sut.Value = "same-value"; // Same as initial value // Assert callbackCount.ShouldBe(0); } }