lightweight variant (#1)
Reviewed-on: #1 Co-authored-by: Aleksandr Neychev <alexnejchev73@gmail.com>
This commit was merged in pull request #1.
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
using CursorLang.Core.Models;
|
||||
using CursorLang.Settings.ViewModels;
|
||||
|
||||
namespace CursorLang.Settings.Tests.ViewModels;
|
||||
|
||||
public sealed class EnumOptionTests
|
||||
{
|
||||
[Fact]
|
||||
public void An_option_remembers_its_value_and_caption()
|
||||
{
|
||||
var option = new EnumOption<AppTheme>(AppTheme.Dark, "Dark theme");
|
||||
|
||||
Assert.Equal(AppTheme.Dark, option.Value);
|
||||
Assert.Equal("Dark theme", option.Display);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_changed_caption_is_announced_to_subscribers()
|
||||
{
|
||||
var option = new EnumOption<AppTheme>(AppTheme.Dark, "Dark");
|
||||
List<string?> changed = [];
|
||||
option.PropertyChanged += (_, e) => changed.Add(e.PropertyName);
|
||||
|
||||
option.Display = "Dark theme";
|
||||
|
||||
Assert.Equal("Dark theme", option.Display);
|
||||
Assert.Equal([nameof(EnumOption<>.Display)], changed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_same_caption_is_not_announced_again()
|
||||
{
|
||||
var option = new EnumOption<AppTheme>(AppTheme.Dark, "Dark theme");
|
||||
List<string?> changed = [];
|
||||
option.PropertyChanged += (_, e) => changed.Add(e.PropertyName);
|
||||
|
||||
option.Display = "Dark theme";
|
||||
|
||||
Assert.Empty(changed);
|
||||
}
|
||||
|
||||
// Accessibility tools take the name of a list item from here
|
||||
[Fact]
|
||||
public void An_option_presents_itself_by_its_caption()
|
||||
{
|
||||
Assert.Equal("Dark theme", new EnumOption<AppTheme>(AppTheme.Dark, "Dark theme").ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_changed_caption_changes_the_presentation_too()
|
||||
{
|
||||
var option = new EnumOption<ScreenPosition>(ScreenPosition.Center, "Center") { Display = "In the centre" };
|
||||
|
||||
Assert.Equal("In the centre", option.ToString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
using CursorLang.Core.Models;
|
||||
using CursorLang.Core.Services;
|
||||
using CursorLang.Settings.Tests.Infrastructure;
|
||||
using CursorLang.Settings.ViewModels;
|
||||
using CursorLang.Tests.Shared;
|
||||
|
||||
namespace CursorLang.Settings.Tests.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// The settings window in terms of what it shows and what it is in charge of.
|
||||
/// </summary>
|
||||
public sealed class SettingsViewModelTests
|
||||
{
|
||||
[Fact]
|
||||
public void The_interface_language_comes_from_the_settings()
|
||||
{
|
||||
var settings = new AppSettings { Language = "ru" };
|
||||
var localization = new FakeLocalizationService();
|
||||
|
||||
using var viewModel = Create(settings, localization);
|
||||
|
||||
Assert.Equal("ru", localization.CurrentLanguage);
|
||||
Assert.Same(settings, viewModel.Settings);
|
||||
Assert.Same(localization, viewModel.Localization);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Changing_the_language_setting_switches_the_interface()
|
||||
{
|
||||
var settings = new AppSettings { Language = "en" };
|
||||
var localization = new FakeLocalizationService();
|
||||
using var viewModel = Create(settings, localization);
|
||||
|
||||
settings.Language = "ru";
|
||||
|
||||
Assert.Equal("ru", localization.CurrentLanguage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Other_settings_leave_the_language_alone()
|
||||
{
|
||||
var settings = new AppSettings { Language = "en" };
|
||||
var localization = new FakeLocalizationService();
|
||||
using var viewModel = Create(settings, localization);
|
||||
|
||||
settings.FontSize = 30;
|
||||
|
||||
Assert.Equal("en", localization.CurrentLanguage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_lists_are_built_from_every_value_of_the_enums()
|
||||
{
|
||||
using SettingsViewModel viewModel = Create();
|
||||
|
||||
Assert.Equal(Enum.GetValues<AppTheme>(), viewModel.Themes.Select(option => option.Value));
|
||||
Assert.Equal(Enum.GetValues<PopupPlacementMode>(), viewModel.PlacementModes.Select(option => option.Value));
|
||||
Assert.Equal(Enum.GetValues<AnchorSide>(), viewModel.AnchorSides.Select(option => option.Value));
|
||||
Assert.Equal(Enum.GetValues<ScreenPosition>(), viewModel.ScreenPositions.Select(option => option.Value));
|
||||
}
|
||||
|
||||
// A caption key is built from the type name and the value
|
||||
[Fact]
|
||||
public void The_option_captions_come_from_the_resources()
|
||||
{
|
||||
var localization = new FakeLocalizationService();
|
||||
using var viewModel = Create(localization: localization);
|
||||
|
||||
Assert.Equal("en:AppTheme_System", viewModel.Themes[0].Display);
|
||||
Assert.Contains("PopupPlacementMode_AtCursor", localization.RequestedKeys);
|
||||
}
|
||||
|
||||
// If the list items were recreated, the ComboBox would drop the selected value
|
||||
[Fact]
|
||||
public void Changing_the_language_changes_the_captions_not_the_options()
|
||||
{
|
||||
var settings = new AppSettings { Language = "en" };
|
||||
var localization = new FakeLocalizationService();
|
||||
using var viewModel = Create(settings, localization);
|
||||
|
||||
EnumOption<AppTheme> first = viewModel.Themes[0];
|
||||
|
||||
settings.Language = "ru";
|
||||
|
||||
Assert.Same(first, viewModel.Themes[0]);
|
||||
Assert.Equal("ru:AppTheme_System", first.Display);
|
||||
Assert.Equal("ru:AnchorSide_TopLeft", viewModel.AnchorSides[0].Display);
|
||||
Assert.Equal("ru:ScreenPosition_TopLeft", viewModel.ScreenPositions[0].Display);
|
||||
Assert.Equal("ru:PopupPlacementMode_AtCursor", viewModel.PlacementModes[0].Display);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_background_and_text_palettes_are_non_empty_and_different()
|
||||
{
|
||||
using SettingsViewModel viewModel = Create();
|
||||
|
||||
Assert.NotEmpty(viewModel.BackgroundPalette);
|
||||
Assert.NotEmpty(viewModel.TextPalette);
|
||||
Assert.NotEqual(viewModel.BackgroundPalette, viewModel.TextPalette);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_palettes_hold_no_duplicates()
|
||||
{
|
||||
using SettingsViewModel viewModel = Create();
|
||||
|
||||
Assert.Equal(viewModel.BackgroundPalette.Count, viewModel.BackgroundPalette.Distinct().Count());
|
||||
Assert.Equal(viewModel.TextPalette.Count, viewModel.TextPalette.Distinct().Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_default_colours_are_present_in_the_palettes()
|
||||
{
|
||||
var settings = new AppSettings();
|
||||
using var viewModel = Create(settings);
|
||||
|
||||
Assert.Contains(settings.BackgroundColor, viewModel.BackgroundPalette);
|
||||
Assert.Contains(settings.ForegroundColor, viewModel.TextPalette);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_palettes_are_the_same_for_every_window()
|
||||
{
|
||||
using SettingsViewModel first = Create();
|
||||
using SettingsViewModel second = Create();
|
||||
|
||||
Assert.Same(first.BackgroundPalette, second.BackgroundPalette);
|
||||
Assert.Same(first.TextPalette, second.TextPalette);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_background_palette_consists_of_colours()
|
||||
{
|
||||
using SettingsViewModel viewModel = Create();
|
||||
|
||||
Assert.All(viewModel.BackgroundPalette, color => Assert.IsType<System.Drawing.Color>(color));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Until_Windows_answers_the_startup_setting_stays_hidden()
|
||||
{
|
||||
using SettingsViewModel viewModel = Create();
|
||||
|
||||
Assert.False(viewModel.IsStartupAvailable);
|
||||
Assert.False(viewModel.CanChangeStartup);
|
||||
Assert.False(viewModel.IsStartupLocked);
|
||||
Assert.False(viewModel.RunAtStartup);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(StartupState.Enabled, true, true, false, true)]
|
||||
[InlineData(StartupState.Disabled, true, true, false, false)]
|
||||
[InlineData(StartupState.DisabledByUser, true, false, true, false)]
|
||||
[InlineData(StartupState.DisabledByPolicy, true, false, true, false)]
|
||||
[InlineData(StartupState.EnabledByPolicy, true, false, true, true)]
|
||||
[InlineData(StartupState.Unavailable, false, false, false, false)]
|
||||
public async Task The_startup_state_decides_how_the_setting_looks(
|
||||
StartupState state, bool available, bool canChange, bool locked, bool enabled)
|
||||
{
|
||||
var startup = new FakeStartupService { State = state };
|
||||
using var viewModel = Create(startup: startup);
|
||||
|
||||
await viewModel.InitializeAsync();
|
||||
|
||||
Assert.Equal(available, viewModel.IsStartupAvailable);
|
||||
Assert.Equal(canChange, viewModel.CanChangeStartup);
|
||||
Assert.Equal(locked, viewModel.IsStartupLocked);
|
||||
Assert.Equal(enabled, viewModel.RunAtStartup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task The_startup_setting_is_announced_after_Windows_answers()
|
||||
{
|
||||
var startup = new FakeStartupService { State = StartupState.Enabled };
|
||||
using var viewModel = Create(startup: startup);
|
||||
|
||||
List<string?> changed = [];
|
||||
viewModel.PropertyChanged += (_, e) => changed.Add(e.PropertyName);
|
||||
|
||||
await viewModel.InitializeAsync();
|
||||
|
||||
Assert.Contains(nameof(SettingsViewModel.RunAtStartup), changed);
|
||||
Assert.Contains(nameof(SettingsViewModel.IsStartupAvailable), changed);
|
||||
Assert.Contains(nameof(SettingsViewModel.CanChangeStartup), changed);
|
||||
Assert.Contains(nameof(SettingsViewModel.IsStartupLocked), changed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Enabling_startup_reaches_Windows()
|
||||
{
|
||||
var startup = new FakeStartupService { State = StartupState.Disabled };
|
||||
using var viewModel = Create(startup: startup);
|
||||
await viewModel.InitializeAsync();
|
||||
|
||||
viewModel.RunAtStartup = true;
|
||||
|
||||
await WaitForStartupRequests(startup, 1);
|
||||
Assert.Equal([true], startup.Requests);
|
||||
Assert.True(viewModel.RunAtStartup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Disabling_startup_reaches_Windows()
|
||||
{
|
||||
var startup = new FakeStartupService { State = StartupState.Enabled };
|
||||
using var viewModel = Create(startup: startup);
|
||||
await viewModel.InitializeAsync();
|
||||
|
||||
viewModel.RunAtStartup = false;
|
||||
|
||||
await WaitForStartupRequests(startup, 1);
|
||||
Assert.Equal([false], startup.Requests);
|
||||
Assert.False(viewModel.RunAtStartup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Setting_the_same_value_again_leaves_Windows_alone()
|
||||
{
|
||||
var startup = new FakeStartupService { State = StartupState.Enabled };
|
||||
using var viewModel = Create(startup: startup);
|
||||
await viewModel.InitializeAsync();
|
||||
|
||||
viewModel.RunAtStartup = true;
|
||||
|
||||
Assert.Empty(startup.Requests);
|
||||
}
|
||||
|
||||
// A ban by the user is not for the app to argue with: the tick has to come back
|
||||
[Fact]
|
||||
public async Task A_refused_request_puts_the_tick_back()
|
||||
{
|
||||
var startup = new FakeStartupService
|
||||
{
|
||||
State = StartupState.Disabled,
|
||||
AnswerOnEnable = StartupState.DisabledByUser,
|
||||
};
|
||||
|
||||
using var viewModel = Create(startup: startup);
|
||||
await viewModel.InitializeAsync();
|
||||
|
||||
List<string?> changed = [];
|
||||
viewModel.PropertyChanged += (_, e) => changed.Add(e.PropertyName);
|
||||
|
||||
viewModel.RunAtStartup = true;
|
||||
|
||||
await WaitForStartupRequests(startup, 1);
|
||||
|
||||
Assert.False(viewModel.RunAtStartup);
|
||||
Assert.True(viewModel.IsStartupLocked);
|
||||
Assert.Contains(nameof(SettingsViewModel.RunAtStartup), changed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Closing_unsubscribes_from_the_settings_and_the_language()
|
||||
{
|
||||
var settings = new AppSettings { Language = "en" };
|
||||
var localization = new FakeLocalizationService();
|
||||
SettingsViewModel viewModel = Create(settings, localization);
|
||||
|
||||
EnumOption<AppTheme> option = viewModel.Themes[0];
|
||||
string display = option.Display;
|
||||
|
||||
viewModel.Dispose();
|
||||
|
||||
settings.Language = "ru";
|
||||
|
||||
// Neither the interface language nor the option captions change any more
|
||||
Assert.Equal("en", localization.CurrentLanguage);
|
||||
Assert.Equal(display, option.Display);
|
||||
}
|
||||
|
||||
private static SettingsViewModel Create(
|
||||
AppSettings? settings = null,
|
||||
ILocalizationService? localization = null,
|
||||
IStartupService? startup = null) =>
|
||||
new(settings ?? new AppSettings(),
|
||||
localization ?? new FakeLocalizationService(),
|
||||
startup ?? new FakeStartupService(),
|
||||
Fake.Updates());
|
||||
|
||||
// The setting travels to Windows without being awaited: the window must not freeze
|
||||
private static async Task WaitForStartupRequests(FakeStartupService startup, int count)
|
||||
{
|
||||
for (int i = 0; i < 100 && startup.Requests.Count < count; i++)
|
||||
{
|
||||
await Task.Delay(5);
|
||||
}
|
||||
|
||||
Assert.Equal(count, startup.Requests.Count);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using CursorLang.Core.Models;
|
||||
using CursorLang.Core.Services;
|
||||
using CursorLang.Settings.ViewModels;
|
||||
using CursorLang.Tests.Shared;
|
||||
|
||||
namespace CursorLang.Settings.Tests.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// The updates section of the settings window: what it shows at every step and
|
||||
/// what it asks of the service behind it.
|
||||
/// </summary>
|
||||
public sealed class UpdateViewModelTests
|
||||
{
|
||||
[Fact]
|
||||
public void Before_the_first_check_the_section_says_nothing()
|
||||
{
|
||||
using UpdateViewModel viewModel = Create(new FakeUpdateService());
|
||||
|
||||
Assert.Equal(UpdateStatus.Idle, viewModel.Status);
|
||||
Assert.False(viewModel.HasStatus);
|
||||
Assert.False(viewModel.IsDownloadOffered);
|
||||
Assert.False(viewModel.IsInstallOffered);
|
||||
Assert.True(viewModel.CanCheck);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task An_update_found_is_offered_for_download()
|
||||
{
|
||||
var updates = new FakeUpdateService { Release = Release("2.0.0.0") };
|
||||
using UpdateViewModel viewModel = Create(updates);
|
||||
|
||||
await viewModel.CheckCommand.ExecuteAsync(null);
|
||||
|
||||
Assert.Equal(UpdateStatus.Available, viewModel.Status);
|
||||
Assert.True(viewModel.IsDownloadOffered);
|
||||
Assert.False(viewModel.IsInstallOffered);
|
||||
Assert.Equal("https://host/releases/tag", viewModel.ReleaseUrl?.ToString());
|
||||
Assert.True(viewModel.IsReleaseLinkShown);
|
||||
Assert.Equal("en:UpdateAvailable", viewModel.StatusText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task With_the_latest_version_installed_there_is_nothing_to_offer()
|
||||
{
|
||||
using UpdateViewModel viewModel = Create(new FakeUpdateService());
|
||||
|
||||
await viewModel.CheckCommand.ExecuteAsync(null);
|
||||
|
||||
Assert.Equal(UpdateStatus.UpToDate, viewModel.Status);
|
||||
Assert.False(viewModel.IsDownloadOffered);
|
||||
Assert.False(viewModel.IsReleaseLinkShown);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_check_by_the_button_says_when_it_did_not_work_out()
|
||||
{
|
||||
var updates = new FakeUpdateService { Failure = new HttpRequestException("no network") };
|
||||
using UpdateViewModel viewModel = Create(updates);
|
||||
|
||||
await viewModel.CheckCommand.ExecuteAsync(null);
|
||||
|
||||
Assert.Equal(UpdateStatus.Failed, viewModel.Status);
|
||||
Assert.True(viewModel.HasStatus);
|
||||
}
|
||||
|
||||
// The app does not always start with a live network, and the user who never
|
||||
// asked about updates has no use for the complaint
|
||||
[Fact]
|
||||
public async Task A_check_at_startup_keeps_a_failure_to_itself()
|
||||
{
|
||||
var updates = new FakeUpdateService { Failure = new HttpRequestException("no network") };
|
||||
using UpdateViewModel viewModel = Create(updates);
|
||||
|
||||
await viewModel.StartAsync();
|
||||
|
||||
Assert.Equal(UpdateStatus.Idle, viewModel.Status);
|
||||
Assert.False(viewModel.HasStatus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_successful_check_is_remembered_in_the_settings()
|
||||
{
|
||||
var settings = new AppSettings();
|
||||
var updates = new FakeUpdateService { Release = Release("2.0.0.0") };
|
||||
using UpdateViewModel viewModel = Create(updates, settings);
|
||||
|
||||
await viewModel.CheckCommand.ExecuteAsync(null);
|
||||
|
||||
Assert.NotNull(settings.LastUpdateCheck);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_check_that_did_not_work_out_is_not_remembered()
|
||||
{
|
||||
var settings = new AppSettings();
|
||||
var updates = new FakeUpdateService { Failure = new HttpRequestException("no network") };
|
||||
using UpdateViewModel viewModel = Create(updates, settings);
|
||||
|
||||
await viewModel.CheckCommand.ExecuteAsync(null);
|
||||
|
||||
Assert.Null(settings.LastUpdateCheck);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_recent_check_is_not_repeated_at_startup()
|
||||
{
|
||||
var settings = new AppSettings { LastUpdateCheck = DateTimeOffset.UtcNow };
|
||||
var updates = new FakeUpdateService();
|
||||
using UpdateViewModel viewModel = Create(updates, settings);
|
||||
|
||||
await viewModel.StartAsync();
|
||||
|
||||
Assert.Equal(0, updates.CheckCalls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_check_of_yesterday_is_repeated_at_startup()
|
||||
{
|
||||
var settings = new AppSettings { LastUpdateCheck = DateTimeOffset.UtcNow - TimeSpan.FromDays(2) };
|
||||
var updates = new FakeUpdateService();
|
||||
using UpdateViewModel viewModel = Create(updates, settings);
|
||||
|
||||
await viewModel.StartAsync();
|
||||
|
||||
Assert.Equal(1, updates.CheckCalls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_ban_on_checking_by_itself_is_obeyed()
|
||||
{
|
||||
var settings = new AppSettings { CheckForUpdates = false };
|
||||
var updates = new FakeUpdateService();
|
||||
using UpdateViewModel viewModel = Create(updates, settings);
|
||||
|
||||
await viewModel.StartAsync();
|
||||
|
||||
Assert.Equal(0, updates.CheckCalls);
|
||||
|
||||
// The button still works: the setting is about the app doing it on its own
|
||||
await viewModel.CheckCommand.ExecuteAsync(null);
|
||||
Assert.Equal(1, updates.CheckCalls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_ban_on_checking_travels_to_the_settings()
|
||||
{
|
||||
var settings = new AppSettings { CheckForUpdates = true };
|
||||
using UpdateViewModel viewModel = Create(new FakeUpdateService(), settings);
|
||||
|
||||
viewModel.CheckAutomatically = false;
|
||||
|
||||
Assert.False(settings.CheckForUpdates);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_package_from_Store_is_left_to_Store()
|
||||
{
|
||||
var updates = new FakeUpdateService { IsSupported = false, Release = Release("2.0.0.0") };
|
||||
using UpdateViewModel viewModel = Create(updates);
|
||||
|
||||
await viewModel.StartAsync();
|
||||
await viewModel.CheckCommand.ExecuteAsync(null);
|
||||
|
||||
Assert.False(viewModel.IsSupported);
|
||||
Assert.Equal(0, updates.CheckCalls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_downloaded_package_is_offered_for_installation()
|
||||
{
|
||||
using TempFolder folder = new();
|
||||
string package = folder.File("CursorLang-2.0.0.0.msixbundle");
|
||||
await File.WriteAllTextAsync(package, "package", TestContext.Current.CancellationToken);
|
||||
|
||||
var updates = new FakeUpdateService { Release = Release("2.0.0.0"), PackagePath = package };
|
||||
using UpdateViewModel viewModel = Create(updates);
|
||||
|
||||
await viewModel.CheckCommand.ExecuteAsync(null);
|
||||
await viewModel.DownloadCommand.ExecuteAsync(null);
|
||||
|
||||
Assert.Equal(UpdateStatus.Ready, viewModel.Status);
|
||||
Assert.True(viewModel.IsInstallOffered);
|
||||
Assert.False(viewModel.IsDownloadOffered);
|
||||
|
||||
viewModel.InstallCommand.Execute(null);
|
||||
|
||||
Assert.Equal([package], updates.Installed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task While_the_package_is_downloading_the_section_shows_it()
|
||||
{
|
||||
var updates = new FakeUpdateService
|
||||
{
|
||||
Release = Release("2.0.0.0"),
|
||||
DownloadGate = new TaskCompletionSource(),
|
||||
};
|
||||
|
||||
using UpdateViewModel viewModel = Create(updates);
|
||||
await viewModel.CheckCommand.ExecuteAsync(null);
|
||||
|
||||
Task download = viewModel.DownloadCommand.ExecuteAsync(null);
|
||||
|
||||
Assert.Equal(UpdateStatus.Downloading, viewModel.Status);
|
||||
Assert.True(viewModel.IsProgressShown);
|
||||
Assert.True(viewModel.IsBusy);
|
||||
Assert.False(viewModel.CanCheck);
|
||||
|
||||
updates.DownloadGate.SetResult();
|
||||
await download;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_download_that_did_not_work_out_is_told_about()
|
||||
{
|
||||
var updates = new FakeUpdateService { Release = Release("2.0.0.0") };
|
||||
using UpdateViewModel viewModel = Create(updates);
|
||||
await viewModel.CheckCommand.ExecuteAsync(null);
|
||||
|
||||
updates.Failure = new HttpRequestException("the connection dropped");
|
||||
await viewModel.DownloadCommand.ExecuteAsync(null);
|
||||
|
||||
Assert.Equal(UpdateStatus.Failed, viewModel.Status);
|
||||
}
|
||||
|
||||
// The temp folder is cleared by Windows as it sees fit, and the app has no
|
||||
// business handing a file that is gone to the installer
|
||||
[Fact]
|
||||
public async Task A_package_gone_from_the_disk_is_offered_for_download_again()
|
||||
{
|
||||
var updates = new FakeUpdateService
|
||||
{
|
||||
Release = Release("2.0.0.0"),
|
||||
PackagePath = Path.Combine(Path.GetTempPath(), "CursorLang.Tests", "never-written.msixbundle"),
|
||||
};
|
||||
|
||||
using UpdateViewModel viewModel = Create(updates);
|
||||
await viewModel.CheckCommand.ExecuteAsync(null);
|
||||
await viewModel.DownloadCommand.ExecuteAsync(null);
|
||||
|
||||
viewModel.InstallCommand.Execute(null);
|
||||
|
||||
Assert.Empty(updates.Installed);
|
||||
Assert.Equal(UpdateStatus.Available, viewModel.Status);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task The_status_is_written_in_the_chosen_language()
|
||||
{
|
||||
var localization = new FakeLocalizationService();
|
||||
using UpdateViewModel viewModel = Create(new FakeUpdateService(), localization: localization);
|
||||
|
||||
await viewModel.CheckCommand.ExecuteAsync(null);
|
||||
Assert.Equal("en:UpdateUpToDate", viewModel.StatusText);
|
||||
|
||||
localization.CurrentLanguage = "ru";
|
||||
Assert.Equal("ru:UpdateUpToDate", viewModel.StatusText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Closing_unsubscribes_from_the_language()
|
||||
{
|
||||
var localization = new FakeLocalizationService();
|
||||
UpdateViewModel viewModel = Create(new FakeUpdateService(), localization: localization);
|
||||
await viewModel.CheckCommand.ExecuteAsync(null);
|
||||
|
||||
List<string?> changed = [];
|
||||
viewModel.PropertyChanged += (_, e) => changed.Add(e.PropertyName);
|
||||
|
||||
viewModel.Dispose();
|
||||
localization.CurrentLanguage = "ru";
|
||||
|
||||
Assert.Empty(changed);
|
||||
}
|
||||
|
||||
private static ReleaseInfo Release(string version) => new(
|
||||
Version.Parse(version),
|
||||
$"v{version}",
|
||||
new Uri("https://host/releases/tag"),
|
||||
new ReleaseAsset($"CursorLang-{version}.msixbundle", new Uri("https://host/package"), 0));
|
||||
|
||||
private static UpdateViewModel Create(
|
||||
IUpdateService updates,
|
||||
AppSettings? settings = null,
|
||||
ILocalizationService? localization = null) =>
|
||||
new(updates,
|
||||
localization ?? new FakeLocalizationService(),
|
||||
settings ?? new AppSettings(),
|
||||
new UpdateOptions());
|
||||
}
|
||||
Reference in New Issue
Block a user