await certificate from ssl.com Reviewed-on: #4 Co-authored-by: Aleksandr Neychev <alexnejchev73@gmail.com>
This commit was merged in pull request #4.
This commit is contained in:
@@ -22,10 +22,8 @@ public sealed class AppTests
|
||||
[InlineData(typeof(MainWindowPlacement))]
|
||||
[InlineData(typeof(ILocalizationService))]
|
||||
[InlineData(typeof(IStartupService))]
|
||||
[InlineData(typeof(IUpdateService))]
|
||||
[InlineData(typeof(UpdateOptions))]
|
||||
[InlineData(typeof(Version))]
|
||||
[InlineData(typeof(SettingsViewModel))]
|
||||
[InlineData(typeof(UpdateViewModel))]
|
||||
[InlineData(typeof(MainWindow))]
|
||||
[InlineData(typeof(AppSettings))]
|
||||
public void Everything_the_window_needs_is_declared_in_the_container(Type service)
|
||||
|
||||
@@ -1,19 +1,8 @@
|
||||
using CursorLang.Core.Models;
|
||||
using CursorLang.Settings.Services;
|
||||
using CursorLang.Settings.ViewModels;
|
||||
using CursorLang.Tests.Shared;
|
||||
|
||||
namespace CursorLang.Settings.Tests.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// Parts every test needs but few tests care about.
|
||||
/// </summary>
|
||||
internal static class Fake
|
||||
{
|
||||
internal static UpdateViewModel Updates() =>
|
||||
new(new FakeUpdateService(), new FakeLocalizationService());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A theme that paints nothing and only remembers the windows attached to it.
|
||||
/// </summary>
|
||||
|
||||
@@ -285,11 +285,12 @@ public sealed class SettingsViewModelTests
|
||||
private static SettingsViewModel Create(
|
||||
AppSettings? settings = null,
|
||||
ILocalizationService? localization = null,
|
||||
IStartupService? startup = null) =>
|
||||
IStartupService? startup = null,
|
||||
Version? version = null) =>
|
||||
new(settings ?? new AppSettings(),
|
||||
localization ?? new FakeLocalizationService(),
|
||||
startup ?? new FakeStartupService(),
|
||||
Fake.Updates());
|
||||
version ?? new Version(1, 0, 0, 0));
|
||||
|
||||
// The setting travels to Windows without being awaited: the window must not freeze
|
||||
private static async Task WaitForStartupRequests(FakeStartupService startup, int count)
|
||||
|
||||
@@ -1,230 +0,0 @@
|
||||
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
|
||||
{
|
||||
// The section has something to say at every moment, the one before the first
|
||||
// answer included: the status line is never an empty spot in the window
|
||||
[Fact]
|
||||
public void Before_the_first_check_the_section_says_so()
|
||||
{
|
||||
using UpdateViewModel viewModel = Create(new FakeUpdateService());
|
||||
|
||||
Assert.Equal(UpdateStatus.Idle, viewModel.Status);
|
||||
Assert.Equal("en:UpdateNotChecked", viewModel.StatusText);
|
||||
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.Equal("en:UpdateFailed", viewModel.StatusText);
|
||||
}
|
||||
|
||||
// The window is opened by hand, and the answer is what the section is there
|
||||
// for: a dead network is part of the answer rather than a reason to say nothing
|
||||
[Fact]
|
||||
public async Task A_check_when_the_window_opens_says_when_it_did_not_work_out()
|
||||
{
|
||||
var updates = new FakeUpdateService { Failure = new HttpRequestException("no network") };
|
||||
using UpdateViewModel viewModel = Create(updates);
|
||||
|
||||
await viewModel.StartAsync();
|
||||
|
||||
Assert.Equal(UpdateStatus.Failed, viewModel.Status);
|
||||
Assert.Equal("en:UpdateFailed", viewModel.StatusText);
|
||||
}
|
||||
|
||||
// Nothing is remembered between openings: an answer from yesterday is worth
|
||||
// less than today's, and the request costs nothing at this rate
|
||||
[Fact]
|
||||
public async Task Every_opening_of_the_window_asks_anew()
|
||||
{
|
||||
var updates = new FakeUpdateService();
|
||||
using UpdateViewModel viewModel = Create(updates);
|
||||
|
||||
await viewModel.StartAsync();
|
||||
Assert.Equal(1, updates.CheckCalls);
|
||||
|
||||
await viewModel.StartAsync();
|
||||
Assert.Equal(2, updates.CheckCalls);
|
||||
}
|
||||
|
||||
[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,
|
||||
ILocalizationService? localization = null) =>
|
||||
new(updates, localization ?? new FakeLocalizationService());
|
||||
}
|
||||
@@ -404,39 +404,20 @@ public sealed class MainWindowTests
|
||||
});
|
||||
}
|
||||
|
||||
// The version is nowhere else in the window, so the title has to carry it
|
||||
[Fact]
|
||||
public void The_updates_are_checked_by_the_button_in_the_window()
|
||||
public void The_title_of_the_window_shows_the_version()
|
||||
{
|
||||
Sta.Run(() =>
|
||||
{
|
||||
var updates = new FakeUpdateService();
|
||||
using UpdateViewModel section = CreateUpdates(updates);
|
||||
using SettingsViewModel viewModel = CreateViewModel(updates: section);
|
||||
var localization = new FakeLocalizationService();
|
||||
localization.Strings["SettingsTitle"] = "CursorLang {0} — Settings";
|
||||
|
||||
Open(viewModel, window =>
|
||||
{
|
||||
Button check = Assert.Single(FindButtonsBoundTo(window, "Updates.CheckCommand"));
|
||||
using SettingsViewModel viewModel = CreateViewModel(
|
||||
localization: localization,
|
||||
version: new Version(1, 2, 3, 0));
|
||||
|
||||
Assert.True(check.IsVisible);
|
||||
check.Command.Execute(null);
|
||||
|
||||
Assert.Equal(1, updates.CheckCalls);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// An app installed from the Store is updated by the Store
|
||||
[Fact]
|
||||
public void An_app_that_updates_itself_elsewhere_shows_no_updates_section()
|
||||
{
|
||||
Sta.Run(() =>
|
||||
{
|
||||
using UpdateViewModel section = CreateUpdates(new FakeUpdateService { IsSupported = false });
|
||||
using SettingsViewModel viewModel = CreateViewModel(updates: section);
|
||||
|
||||
Open(viewModel, window =>
|
||||
Assert.All(FindButtonsBoundTo(window, "Updates.CheckCommand"), button =>
|
||||
Assert.False(button.IsVisible)));
|
||||
Open(viewModel, window => Assert.Equal("CursorLang 1.2.3 — Settings", window.Title));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -486,11 +467,11 @@ public sealed class MainWindowTests
|
||||
AppSettings? settings = null,
|
||||
ILocalizationService? localization = null,
|
||||
IStartupService? startup = null,
|
||||
UpdateViewModel? updates = null) =>
|
||||
Version? version = null) =>
|
||||
new(settings ?? new AppSettings(),
|
||||
localization ?? new FakeLocalizationService(),
|
||||
startup ?? new FakeStartupService(),
|
||||
updates ?? Fake.Updates());
|
||||
version ?? new Version(1, 0, 0, 0));
|
||||
|
||||
private static void Open(SettingsViewModel viewModel, Action<MainWindow> check) =>
|
||||
Open(viewModel, new FakeThemeService(), new MainWindowPlacement(), check);
|
||||
@@ -525,9 +506,6 @@ public sealed class MainWindowTests
|
||||
}
|
||||
}
|
||||
|
||||
private static UpdateViewModel CreateUpdates(IUpdateService updates) =>
|
||||
new(updates, new FakeLocalizationService());
|
||||
|
||||
private static IEnumerable<Button> FindButtonsBoundTo(DependencyObject root, string path) =>
|
||||
FindAll<Button>(root).Where(button =>
|
||||
BindingOperations.GetBinding(button, ButtonBase.CommandProperty)?.Path.Path == path);
|
||||
|
||||
Reference in New Issue
Block a user