Persist PostIt podcast device and title preferences
All checks were successful
Forgejo Release / release (push) Successful in 13m7s
All checks were successful
Forgejo Release / release (push) Successful in 13m7s
Remember detected capture devices, camera and microphone selections, and the default podcast title in user settings. Restore preferences on page creation and surface save failures. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
4a5a3c8844
commit
ff1a016c91
7 changed files with 203 additions and 12 deletions
|
|
@ -22,7 +22,7 @@ public sealed class PodcastBroadcasterTests
|
|||
var factory = Platform.CreatePodcastCaptureAsync;
|
||||
var store = new TokenStore(tokenPath);
|
||||
store.Save(new RefreshTokenRecord("test-access-token", "test-refresh-token", DateTimeOffset.UtcNow.AddHours(1), null));
|
||||
await using var api = new YavscApiClient(new Settings(), store);
|
||||
await using var api = new YavscApiClient(new Settings(tokenPath + ".settings"), store);
|
||||
await using var transport = new Transport();
|
||||
FakeCapture? capture = null;
|
||||
PodcastsPageViewModel? vm = null;
|
||||
|
|
@ -72,6 +72,7 @@ public sealed class PodcastBroadcasterTests
|
|||
Platform.RequestPodcastPermissionsAsync = permissions;
|
||||
Platform.CreatePodcastCaptureAsync = factory;
|
||||
File.Delete(tokenPath);
|
||||
File.Delete(tokenPath + ".settings");
|
||||
}
|
||||
}, TestContext.Current.CancellationToken);
|
||||
|
||||
|
|
@ -84,7 +85,7 @@ public sealed class PodcastBroadcasterTests
|
|||
var factory = Platform.CreatePodcastCaptureAsync;
|
||||
var store = new TokenStore(tokenPath);
|
||||
store.Save(new RefreshTokenRecord("test-access-token", "test-refresh-token", DateTimeOffset.UtcNow.AddHours(1), null));
|
||||
await using var api = new YavscApiClient(new Settings(), store);
|
||||
await using var api = new YavscApiClient(new Settings(tokenPath + ".settings"), store);
|
||||
await using var transport = new Transport();
|
||||
try
|
||||
{
|
||||
|
|
@ -119,6 +120,7 @@ public sealed class PodcastBroadcasterTests
|
|||
Platform.RequestPodcastPermissionsAsync = permissions;
|
||||
Platform.CreatePodcastCaptureAsync = factory;
|
||||
File.Delete(tokenPath);
|
||||
File.Delete(tokenPath + ".settings");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
79
src/PostIt/PostIt.Tests/PodcastPreferencesTests.cs
Normal file
79
src/PostIt/PostIt.Tests/PodcastPreferencesTests.cs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
using Avalonia.Headless.XUnit;
|
||||
using PostIt.Services;
|
||||
using PostIt.ViewModels;
|
||||
using Yavsc.Api.Client;
|
||||
|
||||
namespace PostIt.Tests;
|
||||
|
||||
public sealed class PodcastPreferencesTests
|
||||
{
|
||||
[AvaloniaFact]
|
||||
public async Task Detection_selection_and_title_survive_settings_reload_without_detecting_again()
|
||||
{
|
||||
var path = Path.Combine(Path.GetTempPath(), $"postit-preferences-{Guid.NewGuid():N}.json");
|
||||
var previous = Platform.GetPodcastCaptureDevices;
|
||||
try
|
||||
{
|
||||
Platform.GetPodcastCaptureDevices = () => new(
|
||||
[new("Front", "front"), new("Rear", "rear")],
|
||||
[new("None", ""), new("USB", "usb")]);
|
||||
var settings = new Settings(path);
|
||||
await using var api = new YavscApiClient(settings, new TokenStore(path + ".tokens"));
|
||||
var session = new SessionStatusViewModel();
|
||||
var vm = new PodcastsPageViewModel(new PodcastApiClient(api, () => settings.ApiUrl), api, session);
|
||||
vm.DetectDevicesCommand.Execute(null);
|
||||
vm.SelectedCamera = vm.Cameras[1];
|
||||
vm.SelectedMicrophone = vm.Microphones[0];
|
||||
vm.Title = "Mon emission habituelle";
|
||||
vm.DetectDevicesCommand.Execute(null);
|
||||
Assert.Equal("rear", vm.SelectedCamera?.Value);
|
||||
Assert.Equal("", vm.SelectedMicrophone?.Value);
|
||||
|
||||
var restored = new Settings(path);
|
||||
restored.Authentication.Authority = "";
|
||||
restored.Load();
|
||||
Assert.False(restored.IsDirty);
|
||||
Assert.Equal("Mon emission habituelle", restored.PodcastDefaultTitle);
|
||||
Assert.Equal("rear", restored.PodcastCameraId);
|
||||
Assert.Equal("", restored.PodcastMicrophoneId);
|
||||
Platform.GetPodcastCaptureDevices = () => throw new InvalidOperationException("Must use saved devices.");
|
||||
await using var otherApi = new YavscApiClient(restored, new TokenStore(path + ".tokens"));
|
||||
var other = new PodcastsPageViewModel(new PodcastApiClient(otherApi, () => restored.ApiUrl), otherApi, session);
|
||||
Assert.Equal(2, other.Cameras.Count);
|
||||
Assert.Equal(2, other.Microphones.Count);
|
||||
Assert.Equal("rear", other.SelectedCamera?.Value);
|
||||
Assert.Equal("", other.SelectedMicrophone?.Value);
|
||||
Assert.Equal(vm.Title, other.Title);
|
||||
|
||||
Platform.GetPodcastCaptureDevices = () => new([], []);
|
||||
other.DetectDevicesCommand.Execute(null);
|
||||
Assert.Null(other.SelectedCamera);
|
||||
Assert.Null(other.SelectedMicrophone);
|
||||
Assert.Contains("Aucune camera", other.Status);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Platform.GetPodcastCaptureDevices = previous;
|
||||
File.Delete(path);
|
||||
File.Delete(path + ".tokens");
|
||||
}
|
||||
}
|
||||
|
||||
[AvaloniaFact]
|
||||
public async Task Failed_save_is_reported_without_losing_the_title()
|
||||
{
|
||||
var path = Path.Combine(Path.GetTempPath(), $"postit-preferences-{Guid.NewGuid():N}");
|
||||
Directory.CreateDirectory(path);
|
||||
try
|
||||
{
|
||||
var settings = new Settings(path);
|
||||
await using var api = new YavscApiClient(settings, new TokenStore(path + ".tokens"));
|
||||
var vm = new PodcastsPageViewModel(new PodcastApiClient(api, () => settings.ApiUrl), api, new());
|
||||
vm.Title = "Titre conserve";
|
||||
Assert.Equal("Titre conserve", vm.Title);
|
||||
Assert.Contains("sauvegarde impossible", vm.Status);
|
||||
Assert.True(settings.IsDirty);
|
||||
}
|
||||
finally { Directory.Delete(path); File.Delete(path + ".tokens"); }
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ public sealed class PodcastScheduleTests
|
|||
store.Save(new RefreshTokenRecord("test-access-token", "test-refresh-token", DateTimeOffset.UtcNow.AddHours(1), null));
|
||||
try
|
||||
{
|
||||
await using var api = new ScheduleApi(store);
|
||||
await using var api = new ScheduleApi(store, tokenPath + ".settings");
|
||||
var session = new SessionStatusViewModel { Api = api };
|
||||
session.Refresh();
|
||||
var vm = new PodcastsPageViewModel(new PodcastApiClient(api, () => "https://api.example/"), api, session)
|
||||
|
|
@ -53,11 +53,16 @@ public sealed class PodcastScheduleTests
|
|||
Assert.Null(api.Saved);
|
||||
Assert.Contains("Preparation impossible", vm.Status);
|
||||
Assert.False(vm.IsBroadcasting);
|
||||
vm.SelectedPodcast = api.Plan with { Title = "Titre programme distinct" };
|
||||
Assert.Equal("Titre programme distinct", vm.Title);
|
||||
Assert.Equal("Nouveau titre", api.Settings.PodcastDefaultTitle);
|
||||
vm.NewPlanCommand.Execute(null);
|
||||
Assert.Equal("Nouveau titre", vm.Title);
|
||||
}
|
||||
finally { File.Delete(tokenPath); }
|
||||
finally { File.Delete(tokenPath); File.Delete(tokenPath + ".settings"); }
|
||||
}
|
||||
|
||||
private sealed class ScheduleApi(TokenStore store) : YavscApiClient(new Settings(), store)
|
||||
private sealed class ScheduleApi(TokenStore store, string settingsPath) : YavscApiClient(new Settings(settingsPath), store)
|
||||
{
|
||||
public PodcastResponse Plan { get; private set; } = new(Guid.NewGuid(), "Concert", "owner", "Owner",
|
||||
DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, PodcastState.Planned, 0, 0, 0);
|
||||
|
|
|
|||
Loading…
Reference in a new issue