1fc7792cd1
Rename ConfigManager to ConfigManager.Ui to match the Core/CLI/UI project structure, and split the monolithic test project into Core.Tests, Cli.Tests, and Ui.Tests to align with the source project organization.
413 lines
11 KiB
C#
413 lines
11 KiB
C#
using JdeScoping.ConfigManager.Ui.Services;
|
|
using JdeScoping.ConfigManager.Ui.ViewModels.Forms;
|
|
|
|
namespace JdeScoping.ConfigManager.Ui.Tests.ViewModels.Forms;
|
|
|
|
public class SecretFormViewModelTests
|
|
{
|
|
private readonly IClipboardService _clipboardService;
|
|
|
|
public SecretFormViewModelTests()
|
|
{
|
|
_clipboardService = Substitute.For<IClipboardService>();
|
|
}
|
|
|
|
[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<string>()).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<ArgumentNullException>(() =>
|
|
new SecretFormViewModel(null!, "value", _clipboardService, _ => { }, () => { }));
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_ThrowsOnNullClipboardService()
|
|
{
|
|
// Act & Assert
|
|
Should.Throw<ArgumentNullException>(() =>
|
|
new SecretFormViewModel("key", "value", null!, _ => { }, () => { }));
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_ThrowsOnNullValueChangedCallback()
|
|
{
|
|
// Act & Assert
|
|
Should.Throw<ArgumentNullException>(() =>
|
|
new SecretFormViewModel("key", "value", _clipboardService, null!, () => { }));
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_ThrowsOnNullDeleteCallback()
|
|
{
|
|
// Act & Assert
|
|
Should.Throw<ArgumentNullException>(() =>
|
|
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);
|
|
}
|
|
}
|