Refactor settings management in PLib video library manager. Replace dialog-based settings with a single-panel overlay for improved user experience. Update MainWindow and SettingsView to support new settings panel, and adjust ViewModels accordingly. Enhance README.md to reflect these changes.
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NSubstitute;
|
||||
using PLib.Application.Library;
|
||||
using PLib.Desktop.Services;
|
||||
using PLib.Desktop.Settings;
|
||||
using PLib.Desktop.ViewModels;
|
||||
using Shouldly;
|
||||
|
||||
namespace PLib.Tests.Settings;
|
||||
|
||||
public sealed class SettingsViewModelTests
|
||||
{
|
||||
private readonly IAppSettingsStore _store = Substitute.For<IAppSettingsStore>();
|
||||
|
||||
[Fact]
|
||||
public async Task Opening_and_saving_without_touching_anything_changes_nothing()
|
||||
{
|
||||
// Both of these are displayed in a lossier unit than they are stored in: 65 536 bytes
|
||||
// shows as 0,06 MB. Converting the untouched field back used to yield 62 915, which
|
||||
// silently rewrote the setting and forced a full rescan on every visit to the panel.
|
||||
var options = new LibraryOptions
|
||||
{
|
||||
Folders = [@"C:\videos"],
|
||||
MinimumFileSizeInBytes = 65_536,
|
||||
ThumbnailPositionRatio = 0.15,
|
||||
};
|
||||
|
||||
var (viewModel, outcome) = await SaveAsync(options);
|
||||
|
||||
outcome.RescanRequired.ShouldBeFalse();
|
||||
Written.MinimumFileSizeInBytes.ShouldBe(65_536);
|
||||
Written.ThumbnailPositionRatio.ShouldBe(0.15);
|
||||
viewModel.HasFolders.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Editing_the_minimum_size_does_write_the_new_value()
|
||||
{
|
||||
var (_, outcome) = await SaveAsync(
|
||||
new LibraryOptions { Folders = [@"C:\videos"], MinimumFileSizeInBytes = 65_536 },
|
||||
viewModel => viewModel.MinimumFileSizeMegabytes = 4);
|
||||
|
||||
Written.MinimumFileSizeInBytes.ShouldBe(4 * 1024 * 1024);
|
||||
outcome.RescanRequired.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Removing_a_folder_is_carried_into_the_saved_settings()
|
||||
{
|
||||
var (_, outcome) = await SaveAsync(
|
||||
new LibraryOptions { Folders = [@"C:\a", @"C:\b"] },
|
||||
viewModel => viewModel.Folders[0].RemoveCommand.Execute().Subscribe());
|
||||
|
||||
Written.Folders.ShouldBe([@"C:\b"]);
|
||||
outcome.RescanRequired.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Cancelling_writes_nothing()
|
||||
{
|
||||
var viewModel = Create(new LibraryOptions { Folders = [@"C:\videos"] });
|
||||
var closed = viewModel.Closed.FirstAsync().ToTask();
|
||||
|
||||
viewModel.MinimumFileSizeMegabytes = 999;
|
||||
await viewModel.CancelCommand.Execute().FirstAsync().ToTask(TestContext.Current.CancellationToken);
|
||||
|
||||
(await closed).Saved.ShouldBeFalse();
|
||||
_store.ReceivedCalls().ShouldBeEmpty();
|
||||
}
|
||||
|
||||
private AppSettings Written =>
|
||||
(AppSettings)_store.ReceivedCalls().Single(call => call.GetMethodInfo().Name == nameof(IAppSettingsStore.SaveAsync))
|
||||
.GetArguments()[0]!;
|
||||
|
||||
private async Task<(SettingsViewModel ViewModel, SettingsDialogOutcome Outcome)> SaveAsync(
|
||||
LibraryOptions options,
|
||||
Action<SettingsViewModel>? edit = null)
|
||||
{
|
||||
var viewModel = Create(options);
|
||||
var closed = viewModel.Closed.FirstAsync().ToTask();
|
||||
|
||||
edit?.Invoke(viewModel);
|
||||
await viewModel.SaveCommand.Execute().FirstAsync().ToTask(TestContext.Current.CancellationToken);
|
||||
|
||||
return (viewModel, await closed);
|
||||
}
|
||||
|
||||
private SettingsViewModel Create(LibraryOptions options) => new(
|
||||
Substitute.For<IServiceScopeFactory>(),
|
||||
Monitor(options),
|
||||
Monitor(new AppearanceOptions()),
|
||||
_store,
|
||||
Substitute.For<IFolderPicker>(),
|
||||
Substitute.For<IThemeService>(),
|
||||
NullLogger<SettingsViewModel>.Instance);
|
||||
|
||||
private static IOptionsMonitor<T> Monitor<T>(T value)
|
||||
{
|
||||
var monitor = Substitute.For<IOptionsMonitor<T>>();
|
||||
monitor.CurrentValue.Returns(value);
|
||||
return monitor;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user