feat(12-01): add Base64ToImageSourceConverter, localization keys, and ClientLogoPreview property
- Base64ToImageSourceConverter converts data URI strings to BitmapImage with null-safe error handling - Registered converter in App.xaml as Base64ToImageConverter global resource - Added 9 localization keys (EN+FR) for logo UI labels in Settings and Profile dialogs - Added ClientLogoPreview string property to ProfileManagementViewModel with FormatLogoPreview helper - Updated OnSelectedProfileChanged, BrowseClientLogoAsync, ClearClientLogoAsync, AutoPullClientLogoAsync - 17 tests pass (6 converter + 11 profile VM logo tests) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
using System.Globalization;
|
||||
using SharepointToolbox.Views.Converters;
|
||||
|
||||
namespace SharepointToolbox.Tests.Converters;
|
||||
|
||||
[Trait("Category", "Unit")]
|
||||
public class Base64ToImageSourceConverterTests
|
||||
{
|
||||
private readonly Base64ToImageSourceConverter _converter = new();
|
||||
|
||||
[Fact]
|
||||
public void Convert_NullValue_ReturnsNull()
|
||||
{
|
||||
var result = _converter.Convert(null, typeof(object), null, CultureInfo.InvariantCulture);
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Convert_EmptyString_ReturnsNull()
|
||||
{
|
||||
var result = _converter.Convert(string.Empty, typeof(object), null, CultureInfo.InvariantCulture);
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Convert_NonStringValue_ReturnsNull()
|
||||
{
|
||||
var result = _converter.Convert(42, typeof(object), null, CultureInfo.InvariantCulture);
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Convert_MalformedString_NoBase64Marker_ReturnsNull()
|
||||
{
|
||||
var result = _converter.Convert("not-a-data-uri", typeof(object), null, CultureInfo.InvariantCulture);
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Convert_InvalidBase64AfterMarker_ReturnsNull()
|
||||
{
|
||||
// Has the marker but invalid base64 content — should not throw
|
||||
var result = _converter.Convert("data:image/png;base64,!!!invalid!!!", typeof(object), null, CultureInfo.InvariantCulture);
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertBack_ThrowsNotImplementedException()
|
||||
{
|
||||
Assert.Throws<NotImplementedException>(() =>
|
||||
_converter.ConvertBack(null, typeof(object), null, CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
@@ -115,4 +115,71 @@ public class ProfileManagementViewModelLogoTests : IDisposable
|
||||
var persisted = profiles.First(p => p.Name == "TestTenant");
|
||||
Assert.Null(persisted.ClientLogo);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClientLogoPreview_IsNull_WhenNoProfileSelected()
|
||||
{
|
||||
var vm = CreateViewModel();
|
||||
Assert.Null(vm.ClientLogoPreview);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClientLogoPreview_UpdatesToDataUri_WhenProfileWithLogoSelected()
|
||||
{
|
||||
var vm = CreateViewModel();
|
||||
var profile = new TenantProfile
|
||||
{
|
||||
Name = "WithLogo",
|
||||
TenantUrl = "https://test.sharepoint.com",
|
||||
ClientId = "00000000-0000-0000-0000-000000000002",
|
||||
ClientLogo = new LogoData { Base64 = "dGVzdA==", MimeType = "image/png" }
|
||||
};
|
||||
|
||||
vm.SelectedProfile = profile;
|
||||
|
||||
Assert.Equal("data:image/png;base64,dGVzdA==", vm.ClientLogoPreview);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClientLogoPreview_IsNull_WhenProfileWithoutLogoSelected()
|
||||
{
|
||||
var vm = CreateViewModel();
|
||||
var profile = new TenantProfile
|
||||
{
|
||||
Name = "NoLogo",
|
||||
TenantUrl = "https://test.sharepoint.com",
|
||||
ClientId = "00000000-0000-0000-0000-000000000003"
|
||||
};
|
||||
|
||||
vm.SelectedProfile = profile;
|
||||
|
||||
Assert.Null(vm.ClientLogoPreview);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ClearClientLogoCommand_SetsClientLogoPreviewToNull()
|
||||
{
|
||||
var profileService = new ProfileService(new ProfileRepository(_tempFile));
|
||||
var profile = new TenantProfile
|
||||
{
|
||||
Name = "ClearTest",
|
||||
TenantUrl = "https://test.sharepoint.com",
|
||||
ClientId = "00000000-0000-0000-0000-000000000004",
|
||||
ClientLogo = new LogoData { Base64 = "dGVzdA==", MimeType = "image/png" }
|
||||
};
|
||||
await profileService.AddProfileAsync(profile);
|
||||
|
||||
var vm = new ProfileManagementViewModel(
|
||||
profileService,
|
||||
_mockBranding.Object,
|
||||
_graphClientFactory,
|
||||
_logger);
|
||||
|
||||
vm.SelectedProfile = profile;
|
||||
Assert.NotNull(vm.ClientLogoPreview);
|
||||
|
||||
await vm.ClearClientLogoCommand.ExecuteAsync(null);
|
||||
|
||||
Assert.Null(vm.ClientLogoPreview);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user