Files
cursor-lang/CursorLang.Settings.Tests/ViewModels/SettingsViewModelTests.cs
T
alex 55ac8e6556 lightweight variant (#1)
Reviewed-on: #1
Co-authored-by: Aleksandr Neychev <alexnejchev73@gmail.com>
2026-08-12 13:37:30 +00:00

292 lines
10 KiB
C#

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);
}
}