Files
Sharepoint-Toolbox/SharepointToolbox.Tests/ViewModels/FeatureViewModelBaseTests.cs
T
Dev f4cc81bb71 chore: release v2.4
- Add theme system (Dark/Light palettes, ModernTheme, ThemeManager)
- Add InputDialog, Spinner common view
- Add DuplicatesCsvExportService
- Refresh views, dialogs, and view models across tabs
- Update localization strings (en/fr)
- Tweak services (transfer, permissions, search, user access, ownership elevation, bulk operations)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 11:23:11 +02:00

143 lines
4.5 KiB
C#

using System.Globalization;
using Microsoft.Extensions.Logging.Abstractions;
using SharepointToolbox.Core.Models;
using SharepointToolbox.ViewModels;
namespace SharepointToolbox.Tests.ViewModels;
[Trait("Category", "Unit")]
public class FeatureViewModelBaseTests
{
private class TestViewModel : FeatureViewModelBase
{
public TestViewModel() : base(NullLogger<FeatureViewModelBase>.Instance) { }
public Func<CancellationToken, IProgress<OperationProgress>, Task>? OperationFunc { get; set; }
protected override Task RunOperationAsync(CancellationToken ct, IProgress<OperationProgress> progress)
=> OperationFunc?.Invoke(ct, progress) ?? Task.CompletedTask;
}
[Fact]
public async Task IsRunning_IsTrueWhileOperationExecutes_ThenFalseAfterCompletion()
{
var vm = new TestViewModel();
var tcs = new TaskCompletionSource<bool>();
bool wasRunningDuringOperation = false;
vm.OperationFunc = async (ct, p) =>
{
wasRunningDuringOperation = vm.IsRunning;
await tcs.Task;
};
var runTask = vm.RunCommand.ExecuteAsync(null);
// Give run task time to start
await Task.Delay(10);
Assert.True(wasRunningDuringOperation);
tcs.SetResult(true);
await runTask;
Assert.False(vm.IsRunning);
}
[Fact]
public async Task ProgressValue_AndStatusMessage_UpdateViaIProgress()
{
var vm = new TestViewModel();
int midProgress = -1;
string? midStatus = null;
vm.OperationFunc = async (ct, progress) =>
{
progress.Report(new OperationProgress(50, 100, "halfway"));
// Let the Progress<T> callback dispatch before sampling.
await Task.Delay(20, ct);
midProgress = vm.ProgressValue;
midStatus = vm.StatusMessage;
};
await vm.RunCommand.ExecuteAsync(null);
// Mid-operation snapshot confirms IProgress reaches bound properties.
// Post-completion, FeatureViewModelBase snaps to 100% / "Complete"
// so stale "Scanning X" labels don't linger after a successful run.
Assert.Equal(50, midProgress);
Assert.Equal("halfway", midStatus);
}
[Fact]
public async Task CancelCommand_DuringOperation_SetsStatusMessageToCancelled()
{
// Ensure EN culture so TranslationSource resolves "Operation cancelled"
var prev = CultureInfo.CurrentUICulture;
CultureInfo.CurrentUICulture = CultureInfo.GetCultureInfo("en");
try
{
var vm = new TestViewModel();
var started = new TaskCompletionSource<bool>();
vm.OperationFunc = async (ct, p) =>
{
started.SetResult(true);
await Task.Delay(5000, ct); // Will be cancelled
};
var runTask = vm.RunCommand.ExecuteAsync(null);
await started.Task;
vm.CancelCommand.Execute(null);
await runTask;
Assert.Contains("cancel", vm.StatusMessage, StringComparison.OrdinalIgnoreCase);
Assert.False(vm.IsRunning);
}
finally
{
CultureInfo.CurrentUICulture = prev;
}
}
[Fact]
public async Task OperationCanceledException_IsCaughtGracefully_IsRunningBecomesFalse()
{
var vm = new TestViewModel();
vm.OperationFunc = (ct, p) => throw new OperationCanceledException();
// Should not throw
await vm.RunCommand.ExecuteAsync(null);
Assert.False(vm.IsRunning);
}
[Fact]
public async Task ExceptionDuringOperation_SetsStatusMessageToErrorText_IsRunningBecomesFalse()
{
var vm = new TestViewModel();
vm.OperationFunc = (ct, p) => throw new InvalidOperationException("test error");
await vm.RunCommand.ExecuteAsync(null);
Assert.False(vm.IsRunning);
Assert.Contains("test error", vm.StatusMessage, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public async Task RunCommand_CannotBeInvoked_WhileIsRunning()
{
var vm = new TestViewModel();
var tcs = new TaskCompletionSource<bool>();
vm.OperationFunc = async (ct, p) => await tcs.Task;
var runTask = vm.RunCommand.ExecuteAsync(null);
await Task.Delay(10); // Let it start
Assert.False(vm.RunCommand.CanExecute(null));
tcs.SetResult(true);
await runTask;
Assert.True(vm.RunCommand.CanExecute(null));
}
}