using System.ComponentModel; using CursorLang.Core.Models; using CursorLang.Core.Services; namespace CursorLang.Tests.Shared; /// /// The keyboard layout, with the test in charge of it. /// public sealed class FakeKeyboardLayoutService : IKeyboardLayoutService { public int StartCalls { get; private set; } public int StopCalls { get; private set; } public int SwitchCalls { get; private set; } public KeyboardLayout CurrentLayout { get; set; } = KeyboardLayout.FromLocaleId(0x0409); public KeyboardLayout Current => CurrentLayout; public event EventHandler? LayoutChanged; public void Start() => StartCalls++; public void Stop() => StopCalls++; public void SwitchToNext() => SwitchCalls++; public void RaiseLayoutChanged(KeyboardLayout layout, LayoutChangeReason reason) => LayoutChanged?.Invoke(this, new LayoutChangedEventArgs(layout, reason)); public bool HasSubscribers => LayoutChanged is not null; } /// /// A tooltip that pops up nowhere and merely remembers what it was asked for. /// public sealed class FakeLayoutPopupService : ILayoutPopupService { public List Shown { get; } = []; public List ShownUntilHidden { get; } = []; public 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++; } /// /// The Caps Lock interception without intercepting anything: the test supplies the presses. /// public sealed class FakeCapsLockHotkeyService : ICapsLockHotkeyService { public event EventHandler? Tapped; public event EventHandler? HoldStarted; public event EventHandler? HoldEnded; public bool IsRunning { get; private set; } public int StartCalls { get; private set; } public int StopCalls { get; private set; } public void Start() { StartCalls++; IsRunning = true; } public void Stop() { StopCalls++; IsRunning = false; } public void RaiseTapped() => Tapped?.Invoke(this, EventArgs.Empty); public void RaiseHoldStarted() => HoldStarted?.Invoke(this, EventArgs.Empty); public void RaiseHoldEnded() => HoldEnded?.Invoke(this, EventArgs.Empty); public bool HasSubscribers => Tapped is not null || HoldStarted is not null || HoldEnded is not null; } /// /// Startup whose state the test assigns. /// public sealed class FakeStartupService : IStartupService { public StartupState State { get; set; } = StartupState.Disabled; public StartupState? AnswerOnEnable { get; set; } public List Requests { get; } = []; public int GetStateCalls { get; private set; } public Task GetStateAsync() { GetStateCalls++; return Task.FromResult(State); } public Task SetEnabledAsync(bool enabled) { Requests.Add(enabled); State = enabled ? AnswerOnEnable ?? StartupState.Enabled : StartupState.Disabled; return Task.FromResult(State); } } /// /// Releases the test writes itself, with no repository behind them. /// public sealed class FakeUpdateService : IUpdateService { public ReleaseInfo? Release { get; set; } public Exception? Failure { get; set; } public TaskCompletionSource? DownloadGate { get; set; } public string PackagePath { get; set; } = string.Empty; public int CheckCalls { get; private set; } public List Installed { get; } = []; public bool IsSupported { get; set; } = true; public Version CurrentVersion { get; set; } = new(1, 0, 0, 0); public Task CheckAsync(CancellationToken cancellationToken) { CheckCalls++; return Failure is null ? Task.FromResult(Release) : Task.FromException(Failure); } public async Task DownloadAsync( ReleaseInfo release, IProgress? 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); } /// /// A release list the test fills in, with no repository behind it. /// public sealed class FakeReleaseFeed : IReleaseFeed { public ReleaseInfo? Release { get; set; } public Exception? Failure { get; set; } public List Authorized { get; } = []; public Task GetLatestAsync(CancellationToken cancellationToken) => Failure is null ? Task.FromResult(Release) : Task.FromException(Failure); public void Authorize(HttpRequestMessage request) => Authorized.Add(request); } /// /// Interface strings without resources: the key comes back as is, tagged with the language. /// public sealed class FakeLocalizationService : ILocalizationService { private string _currentLanguage = "en"; public event PropertyChangedEventHandler? PropertyChanged; public List RequestedKeys { get; } = []; public string this[string key] { get { RequestedKeys.Add(key); return $"{_currentLanguage}:{key}"; } } public IReadOnlyList 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(LocalizationService.IndexerName)); } } } /// /// A tooltip window that shows nothing. /// public sealed class FakeLayoutPopupWindow : ILayoutPopupWindow { public int ShowCalls { get; private set; } public int HideCalls { get; private set; } public int CloseCalls { get; private set; } public List Calls { get; } = []; public string? ShownText { get; private set; } public void ShowPopup(string shortName) { ShowCalls++; ShownText = shortName; Calls.Add("show"); } public void Hide() { HideCalls++; Calls.Add("hide"); } public void Close() { CloseCalls++; Calls.Add("close"); } }