added tests to project
This commit is contained in:
@@ -0,0 +1,289 @@
|
||||
using System.ComponentModel;
|
||||
using System.Net.Http;
|
||||
using CursorLang.Models;
|
||||
using CursorLang.Services;
|
||||
using CursorLang.ViewModels;
|
||||
|
||||
namespace CursorLang.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(), new AppSettings(), new UpdateOptions());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The keyboard layout, with the test in charge of it.
|
||||
/// </summary>
|
||||
internal sealed class FakeKeyboardLayoutService : IKeyboardLayoutService
|
||||
{
|
||||
internal int StartCalls { get; private set; }
|
||||
|
||||
internal int StopCalls { get; private set; }
|
||||
|
||||
internal int SwitchCalls { get; private set; }
|
||||
|
||||
internal KeyboardLayout CurrentLayout { get; set; } = KeyboardLayout.FromLocaleId(0x0409);
|
||||
|
||||
public KeyboardLayout Current => CurrentLayout;
|
||||
|
||||
public event EventHandler<LayoutChangedEventArgs>? LayoutChanged;
|
||||
|
||||
public void Start() => StartCalls++;
|
||||
|
||||
public void Stop() => StopCalls++;
|
||||
|
||||
public void SwitchToNext() => SwitchCalls++;
|
||||
|
||||
internal void RaiseLayoutChanged(KeyboardLayout layout, LayoutChangeReason reason) =>
|
||||
LayoutChanged?.Invoke(this, new LayoutChangedEventArgs(layout, reason));
|
||||
|
||||
internal bool HasSubscribers => LayoutChanged is not null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A tooltip that pops up nowhere and merely remembers what it was asked for.
|
||||
/// </summary>
|
||||
internal sealed class FakeLayoutPopupService : ILayoutPopupService
|
||||
{
|
||||
internal List<KeyboardLayout> Shown { get; } = [];
|
||||
|
||||
internal List<KeyboardLayout> ShownUntilHidden { get; } = [];
|
||||
|
||||
internal int HideCalls { get; private set; }
|
||||
|
||||
public void Show(KeyboardLayout layout) => Shown.Add(layout);
|
||||
|
||||
public void ShowUntilHidden(KeyboardLayout layout) => ShownUntilHidden.Add(layout);
|
||||
|
||||
public void Hide() => HideCalls++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Caps Lock interception without intercepting anything: the test supplies the presses.
|
||||
/// </summary>
|
||||
internal sealed class FakeCapsLockHotkeyService : ICapsLockHotkeyService
|
||||
{
|
||||
public event EventHandler? Tapped;
|
||||
|
||||
public event EventHandler? HoldStarted;
|
||||
|
||||
public event EventHandler? HoldEnded;
|
||||
|
||||
public bool IsRunning { get; private set; }
|
||||
|
||||
internal int StartCalls { get; private set; }
|
||||
|
||||
internal int StopCalls { get; private set; }
|
||||
|
||||
public void Start()
|
||||
{
|
||||
StartCalls++;
|
||||
IsRunning = true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
StopCalls++;
|
||||
IsRunning = false;
|
||||
}
|
||||
|
||||
internal void RaiseTapped() => Tapped?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
internal void RaiseHoldStarted() => HoldStarted?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
internal void RaiseHoldEnded() => HoldEnded?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
internal bool HasSubscribers => Tapped is not null || HoldStarted is not null || HoldEnded is not null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Startup whose state the test assigns.
|
||||
/// </summary>
|
||||
internal sealed class FakeStartupService : IStartupService
|
||||
{
|
||||
internal StartupState State { get; set; } = StartupState.Disabled;
|
||||
|
||||
internal StartupState? AnswerOnEnable { get; set; }
|
||||
|
||||
internal List<bool> Requests { get; } = [];
|
||||
|
||||
internal int GetStateCalls { get; private set; }
|
||||
|
||||
public Task<StartupState> GetStateAsync()
|
||||
{
|
||||
GetStateCalls++;
|
||||
return Task.FromResult(State);
|
||||
}
|
||||
|
||||
public Task<StartupState> SetEnabledAsync(bool enabled)
|
||||
{
|
||||
Requests.Add(enabled);
|
||||
|
||||
State = enabled
|
||||
? AnswerOnEnable ?? StartupState.Enabled
|
||||
: StartupState.Disabled;
|
||||
|
||||
return Task.FromResult(State);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the test writes itself, with no repository behind them.
|
||||
/// </summary>
|
||||
internal sealed class FakeUpdateService : IUpdateService
|
||||
{
|
||||
internal ReleaseInfo? Release { get; set; }
|
||||
|
||||
internal Exception? Failure { get; set; }
|
||||
|
||||
internal TaskCompletionSource? DownloadGate { get; set; }
|
||||
|
||||
internal string PackagePath { get; set; } = string.Empty;
|
||||
|
||||
internal int CheckCalls { get; private set; }
|
||||
|
||||
internal List<string> Installed { get; } = [];
|
||||
|
||||
public bool IsSupported { get; set; } = true;
|
||||
|
||||
public Version CurrentVersion { get; set; } = new(1, 0, 0, 0);
|
||||
|
||||
public Task<ReleaseInfo?> CheckAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
CheckCalls++;
|
||||
|
||||
return Failure is null
|
||||
? Task.FromResult(Release)
|
||||
: Task.FromException<ReleaseInfo?>(Failure);
|
||||
}
|
||||
|
||||
public async Task<string> DownloadAsync(
|
||||
ReleaseInfo release,
|
||||
IProgress<double>? progress,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (DownloadGate is not null)
|
||||
{
|
||||
await DownloadGate.Task.WaitAsync(cancellationToken);
|
||||
}
|
||||
|
||||
if (Failure is not null)
|
||||
{
|
||||
throw Failure;
|
||||
}
|
||||
|
||||
progress?.Report(0.5);
|
||||
return PackagePath;
|
||||
}
|
||||
|
||||
public void Install(string packagePath) => Installed.Add(packagePath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A release list the test fills in, with no repository behind it.
|
||||
/// </summary>
|
||||
internal sealed class FakeReleaseFeed : IReleaseFeed
|
||||
{
|
||||
internal ReleaseInfo? Release { get; set; }
|
||||
|
||||
internal Exception? Failure { get; set; }
|
||||
|
||||
internal List<HttpRequestMessage> Authorized { get; } = [];
|
||||
|
||||
public Task<ReleaseInfo?> GetLatestAsync(CancellationToken cancellationToken) =>
|
||||
Failure is null ? Task.FromResult(Release) : Task.FromException<ReleaseInfo?>(Failure);
|
||||
|
||||
public void Authorize(HttpRequestMessage request) => Authorized.Add(request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface strings without resources: the key comes back as is, tagged with the language.
|
||||
/// </summary>
|
||||
internal sealed class FakeLocalizationService : ILocalizationService
|
||||
{
|
||||
private string _currentLanguage = "en";
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
internal List<string> RequestedKeys { get; } = [];
|
||||
|
||||
public string this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
RequestedKeys.Add(key);
|
||||
return $"{_currentLanguage}:{key}";
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<LanguageOption> AvailableLanguages { get; } =
|
||||
[
|
||||
new("en", "English"),
|
||||
new("ru", "Русский"),
|
||||
];
|
||||
|
||||
public string CurrentLanguage
|
||||
{
|
||||
get => _currentLanguage;
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value) || value == _currentLanguage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_currentLanguage = value;
|
||||
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentLanguage)));
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(System.Windows.Data.Binding.IndexerName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A theme that paints nothing and only remembers the windows attached to it.
|
||||
/// </summary>
|
||||
internal sealed class FakeThemeService : IThemeService
|
||||
{
|
||||
public AppTheme CurrentTheme { get; set; } = AppTheme.Light;
|
||||
|
||||
internal List<System.Windows.Window> Registered { get; } = [];
|
||||
|
||||
public void Register(System.Windows.Window window) => Registered.Add(window);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A tooltip window that shows nothing.
|
||||
/// </summary>
|
||||
internal sealed class FakeLayoutPopupWindow : ILayoutPopupWindow
|
||||
{
|
||||
internal int ShowCalls { get; private set; }
|
||||
|
||||
internal int HideCalls { get; private set; }
|
||||
|
||||
internal int CloseCalls { get; private set; }
|
||||
|
||||
internal List<string> Calls { get; } = [];
|
||||
|
||||
public void ShowPopup()
|
||||
{
|
||||
ShowCalls++;
|
||||
Calls.Add("show");
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
HideCalls++;
|
||||
Calls.Add("hide");
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
CloseCalls++;
|
||||
Calls.Add("close");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user