refactor(securestoremanager): add platform service abstractions and constants

Implement deferred code review findings:
- Add IDialogService/IClipboardService interfaces for testable platform operations
- Create AvaloniaDialogService and AvaloniaClipboardService implementations
- Extract dialog strings and file extensions to centralized Constants classes
- Refactor ViewModels to use DI instead of event delegates
- Update tests to use mock services
This commit is contained in:
Joseph Doherty
2026-01-19 16:54:35 -05:00
parent 1c546c111a
commit fbe58a81e4
33 changed files with 1790 additions and 298 deletions
@@ -1,16 +1,25 @@
using NSubstitute;
using Shouldly;
using Xunit;
using JdeScoping.SecureStoreManager.Services;
using JdeScoping.SecureStoreManager.ViewModels;
namespace JdeScoping.SecureStoreManager.Tests.ViewModels;
public class SecretItemViewModelTests
{
private readonly IClipboardService _mockClipboardService;
public SecretItemViewModelTests()
{
_mockClipboardService = Substitute.For<IClipboardService>();
}
[Fact]
public void Constructor_InitializesKey()
{
// Arrange & Act
var sut = new SecretItemViewModel("testKey", "testValue");
var sut = new SecretItemViewModel("testKey", "testValue", _mockClipboardService);
// Assert
sut.Key.ShouldBe("testKey");
@@ -20,7 +29,7 @@ public class SecretItemViewModelTests
public void Constructor_InitializesActualValue()
{
// Arrange & Act
var sut = new SecretItemViewModel("testKey", "testValue");
var sut = new SecretItemViewModel("testKey", "testValue", _mockClipboardService);
// Assert
sut.ActualValue.ShouldBe("testValue");
@@ -30,7 +39,7 @@ public class SecretItemViewModelTests
public void Constructor_InitializesIsValueVisibleToFalse()
{
// Arrange & Act
var sut = new SecretItemViewModel("testKey", "testValue");
var sut = new SecretItemViewModel("testKey", "testValue", _mockClipboardService);
// Assert
sut.IsValueVisible.ShouldBeFalse();
@@ -40,7 +49,7 @@ public class SecretItemViewModelTests
public void DisplayValue_WhenNotVisible_ReturnsMaskedValue()
{
// Arrange
var sut = new SecretItemViewModel("testKey", "testValue");
var sut = new SecretItemViewModel("testKey", "testValue", _mockClipboardService);
// Act & Assert
sut.DisplayValue.ShouldBe("********");
@@ -50,7 +59,7 @@ public class SecretItemViewModelTests
public void DisplayValue_WhenVisible_ReturnsActualValue()
{
// Arrange
var sut = new SecretItemViewModel("testKey", "testValue");
var sut = new SecretItemViewModel("testKey", "testValue", _mockClipboardService);
// Act
sut.IsValueVisible = true;
@@ -63,7 +72,7 @@ public class SecretItemViewModelTests
public void ToggleVisibilityCommand_TogglesIsValueVisible()
{
// Arrange
var sut = new SecretItemViewModel("testKey", "testValue");
var sut = new SecretItemViewModel("testKey", "testValue", _mockClipboardService);
sut.IsValueVisible.ShouldBeFalse();
// Act
@@ -77,7 +86,7 @@ public class SecretItemViewModelTests
public void ToggleVisibilityCommand_TogglesBackToHidden()
{
// Arrange
var sut = new SecretItemViewModel("testKey", "testValue");
var sut = new SecretItemViewModel("testKey", "testValue", _mockClipboardService);
sut.IsValueVisible = true;
// Act
@@ -88,16 +97,10 @@ public class SecretItemViewModelTests
}
[Fact]
public async Task CopyToClipboardCommand_RaisesOnCopyToClipboardEvent()
public async Task CopyToClipboardCommand_CallsClipboardService()
{
// Arrange
var sut = new SecretItemViewModel("testKey", "secretPassword");
string? copiedValue = null;
sut.OnCopyToClipboard += value =>
{
copiedValue = value;
return Task.CompletedTask;
};
var sut = new SecretItemViewModel("testKey", "secretPassword", _mockClipboardService);
// Act
sut.CopyToClipboardCommand.Execute(null);
@@ -105,14 +108,14 @@ public class SecretItemViewModelTests
// Assert - need to wait for async handler
// Give the async void handler time to complete
await Task.Delay(100);
copiedValue.ShouldBe("secretPassword");
await _mockClipboardService.Received(1).SetTextAsync("secretPassword");
}
[Fact]
public void IsValueVisible_SetterRaisesPropertyChangedForIsValueVisible()
{
// Arrange
var sut = new SecretItemViewModel("testKey", "testValue");
var sut = new SecretItemViewModel("testKey", "testValue", _mockClipboardService);
var propertyChangedRaised = false;
sut.PropertyChanged += (s, e) =>
{
@@ -131,7 +134,7 @@ public class SecretItemViewModelTests
public void IsValueVisible_SetterRaisesPropertyChangedForDisplayValue()
{
// Arrange
var sut = new SecretItemViewModel("testKey", "testValue");
var sut = new SecretItemViewModel("testKey", "testValue", _mockClipboardService);
var propertyChangedRaised = false;
sut.PropertyChanged += (s, e) =>
{
@@ -150,7 +153,7 @@ public class SecretItemViewModelTests
public void IsValueVisible_SetToSameValue_DoesNotRaisePropertyChanged()
{
// Arrange
var sut = new SecretItemViewModel("testKey", "testValue");
var sut = new SecretItemViewModel("testKey", "testValue", _mockClipboardService);
sut.IsValueVisible = false; // Already false, but explicitly set
var propertyChangedRaised = false;
sut.PropertyChanged += (s, e) =>