< Summary

Information
Class: Pomodoro.Web.Services.SettingsValidationResult
Assembly: Pomodoro.Web
File(s): /home/runner/work/Pomodoro/Pomodoro/src/Pomodoro.Web/Services/SettingsPresenterService.cs
Line coverage
100%
Covered lines: 2
Uncovered lines: 0
Coverable lines: 2
Total lines: 332
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_IsValid()100%11100%
get_Errors()100%11100%

File(s)

/home/runner/work/Pomodoro/Pomodoro/src/Pomodoro.Web/Services/SettingsPresenterService.cs

#LineLine coverage
 1using Microsoft.Extensions.Logging;
 2using Microsoft.JSInterop;
 3using Pomodoro.Web.Models;
 4
 5namespace Pomodoro.Web.Services;
 6
 7/// <summary>
 8/// Service for settings page presentation logic
 9/// </summary>
 10public class SettingsPresenterService
 11{
 12    private readonly ILogger<SettingsPresenterService> _logger;
 13
 14    public SettingsPresenterService(ILogger<SettingsPresenterService> logger)
 15    {
 16        _logger = logger;
 17    }
 18
 19    /// <summary>
 20    /// Checks if given settings match default values
 21    /// </summary>
 22    /// <param name="settings">Settings to check</param>
 23    /// <returns>True if settings match defaults, false otherwise</returns>
 24    public virtual bool IsAtDefaults(TimerSettings settings)
 25    {
 26        var defaults = new TimerSettings();
 27        return settings.PomodoroMinutes == defaults.PomodoroMinutes
 28            && settings.ShortBreakMinutes == defaults.ShortBreakMinutes
 29            && settings.LongBreakMinutes == defaults.LongBreakMinutes
 30            && settings.SoundEnabled == defaults.SoundEnabled
 31            && settings.NotificationsEnabled == defaults.NotificationsEnabled
 32            && settings.AutoStartEnabled == defaults.AutoStartEnabled
 33            && settings.AutoStartDelaySeconds == defaults.AutoStartDelaySeconds;
 34    }
 35
 36    /// <summary>
 37    /// Builds a success message for import operation
 38    /// </summary>
 39    /// <param name="totalImported">Total number of records imported</param>
 40    /// <param name="totalSkipped">Total number of records skipped</param>
 41    /// <returns>Formatted success message</returns>
 42    public virtual string BuildImportSuccessMessage(int totalImported, int totalSkipped)
 43    {
 44        var messageParts = new List<string>();
 45
 46        if (totalImported > 0)
 47        {
 48            messageParts.Add($"imported {totalImported} records");
 49        }
 50
 51        if (totalSkipped > 0)
 52        {
 53            messageParts.Add($"skipped {totalSkipped} duplicates");
 54        }
 55
 56        return messageParts.Count > 0
 57            ? $"Import complete: {string.Join(", ", messageParts)}."
 58            : "Import complete: no new records to import.";
 59    }
 60
 61    /// <summary>
 62    /// Validates settings values
 63    /// </summary>
 64    /// <param name="settings">Settings to validate</param>
 65    /// <returns>Validation result with any errors</returns>
 66    public SettingsValidationResult ValidateSettings(TimerSettings settings)
 67    {
 68        var errors = new List<string>();
 69
 70        ValidatePomodoroDuration(settings.PomodoroMinutes, errors);
 71        ValidateShortBreakDuration(settings.ShortBreakMinutes, errors);
 72        ValidateLongBreakDuration(settings.LongBreakMinutes, errors);
 73        ValidateAutoStartDelay(settings.AutoStartDelaySeconds, errors);
 74
 75        return new SettingsValidationResult
 76        {
 77            IsValid = errors.Count == 0,
 78            Errors = errors
 79        };
 80    }
 81
 82    /// <summary>
 83    /// Validates pomodoro duration
 84    /// </summary>
 85    /// <param name="pomodoroMinutes">Pomodoro duration in minutes</param>
 86    /// <param name="errors">Error list to add to</param>
 87    private void ValidatePomodoroDuration(int pomodoroMinutes, List<string> errors)
 88    {
 89        if (pomodoroMinutes < Constants.Timer.MinPomodoroMinutes || pomodoroMinutes > Constants.Timer.MaxPomodoroMinutes
 90        {
 91            errors.Add($"Pomodoro duration must be between {Constants.Timer.MinPomodoroMinutes} and {Constants.Timer.Max
 92        }
 93    }
 94
 95    /// <summary>
 96    /// Validates short break duration
 97    /// </summary>
 98    /// <param name="shortBreakMinutes">Short break duration in minutes</param>
 99    /// <param name="errors">Error list to add to</param>
 100    private void ValidateShortBreakDuration(int shortBreakMinutes, List<string> errors)
 101    {
 102        if (shortBreakMinutes < Constants.Timer.MinBreakMinutes || shortBreakMinutes > Constants.Timer.MaxBreakMinutes)
 103        {
 104            errors.Add($"Short break duration must be between {Constants.Timer.MinBreakMinutes} and {Constants.Timer.Max
 105        }
 106    }
 107
 108    /// <summary>
 109    /// Validates long break duration
 110    /// </summary>
 111    /// <param name="longBreakMinutes">Long break duration in minutes</param>
 112    /// <param name="errors">Error list to add to</param>
 113    private void ValidateLongBreakDuration(int longBreakMinutes, List<string> errors)
 114    {
 115        if (longBreakMinutes < 1 || longBreakMinutes > 60)
 116        {
 117            errors.Add("Long break duration must be between 1 and 60 minutes");
 118        }
 119    }
 120
 121    /// <summary>
 122    /// Validates auto-start delay
 123    /// </summary>
 124    /// <param name="autoStartDelaySeconds">Auto-start delay in seconds</param>
 125    /// <param name="errors">Error list to add to</param>
 126    private void ValidateAutoStartDelay(int autoStartDelaySeconds, List<string> errors)
 127    {
 128        if (autoStartDelaySeconds < Constants.Timer.MinAutoStartDelaySeconds || autoStartDelaySeconds > Constants.Timer.
 129        {
 130            errors.Add($"Auto-start delay must be between {Constants.Timer.MinAutoStartDelaySeconds} and {Constants.Time
 131        }
 132    }
 133
 134    /// <summary>
 135    /// Determines if settings have changes
 136    /// </summary>
 137    /// <param name="current">Current settings</param>
 138    /// <param name="original">Original settings</param>
 139    /// <returns>True if settings have changes</returns>
 140    public bool HasChanges(TimerSettings current, TimerSettings original)
 141    {
 142        return !current.Equals(original);
 143    }
 144
 145    /// <summary>
 146    /// Creates a toast message for settings save
 147    /// </summary>
 148    /// <param name="success">Whether save was successful</param>
 149    /// <param name="customMessage">Optional custom message</param>
 150    /// <returns>Toast message</returns>
 151    public string CreateToastMessage(bool success, string? customMessage = null)
 152    {
 153        if (!string.IsNullOrEmpty(customMessage))
 154        {
 155            return customMessage!;
 156        }
 157
 158        return success ? "Settings saved successfully!" : "Failed to save settings";
 159    }
 160
 161    /// <summary>
 162    /// Determines if auto-start delay should be shown
 163    /// </summary>
 164    /// <param name="autoStartEnabled">Whether auto-start is enabled</param>
 165    /// <returns>True if auto-start delay should be shown</returns>
 166    public bool ShouldShowAutoStartDelay(bool autoStartEnabled)
 167    {
 168        return autoStartEnabled;
 169    }
 170
 171    /// <summary>
 172    /// Validates import file size
 173    /// </summary>
 174    /// <param name="fileSize">File size in bytes</param>
 175    /// <returns>Validation result</returns>
 176    public FileValidationResult ValidateImportFileSize(long fileSize)
 177    {
 178        if (fileSize == 0)
 179        {
 180            return new FileValidationResult
 181            {
 182                IsValid = false,
 183                ErrorMessage = "File is empty"
 184            };
 185        }
 186
 187        if (fileSize > Constants.Validation.MaxImportFileSizeBytes)
 188        {
 189            return new FileValidationResult
 190            {
 191                IsValid = false,
 192                ErrorMessage = $"File too large. Maximum size is {Constants.Validation.MaxImportFileSizeBytes / (1024 * 
 193            };
 194        }
 195
 196        return new FileValidationResult
 197        {
 198            IsValid = true,
 199            ErrorMessage = null
 200        };
 201    }
 202
 203    /// <summary>
 204    /// Creates export filename with current date
 205    /// </summary>
 206    /// <returns>Export filename</returns>
 207    public string CreateExportFilename()
 208    {
 209        return $"pomodoro-backup-{DateTime.Today:yyyy-MM-dd}.json";
 210    }
 211
 212    /// <summary>
 213    /// Downloads file using JavaScript interop
 214    /// </summary>
 215    /// <param name="jsInteropService">JS interop service instance</param>
 216    /// <param name="filename">Filename to download</param>
 217    /// <param name="content">File content</param>
 218    /// <param name="mimeType">MIME type</param>
 219    public virtual async Task DownloadFileAsync(IJSInteropService jsInteropService, string filename, string content, str
 220    {
 221        try
 222        {
 223            var bytes = System.Text.Encoding.UTF8.GetBytes(content);
 224            var base64 = Convert.ToBase64String(bytes);
 225
 226            await jsInteropService.InvokeVoidAsync("fileInterop.downloadFile", filename, base64, mimeType);
 227        }
 228        catch (Exception ex)
 229        {
 230            _logger.LogError(ex, "Failed to download file {Filename}", filename);
 231            throw;
 232        }
 233    }
 234
 235    /// <summary>
 236    /// Determines if clear data confirmation should be shown
 237    /// </summary>
 238    /// <param name="isClearing">Current clearing state</param>
 239    /// <returns>True if confirmation should be shown</returns>
 240    public bool ShouldShowClearConfirmation(bool isClearing)
 241    {
 242        return !isClearing;
 243    }
 244
 245    /// <summary>
 246    /// Creates toast hide action with delay
 247    /// </summary>
 248    /// <param name="showToast">Action to show toast</param>
 249    /// <param name="hideToast">Action to hide toast</param>
 250    /// <param name="message">Toast message</param>
 251    /// <returns>Action that hides toast after delay</returns>
 252    public Action CreateDelayedToastHideAction(Action<bool> showToast, Action hideToast, string message)
 253    {
 254        return () =>
 255        {
 256            showToast(true);
 257            SafeTaskRunner.RunAndForget(async () =>
 258            {
 259                await Task.Delay(Constants.UI.ToastDurationMs);
 260                hideToast();
 261            }, _logger, Constants.SafeTaskOperations.ToastHide);
 262        };
 263    }
 264
 265    /// <summary>
 266    /// Determines if import button should be disabled
 267    /// </summary>
 268    /// <param name="isImporting">Current importing state</param>
 269    /// <returns>True if button should be disabled</returns>
 270    public bool ShouldDisableImportButton(bool isImporting)
 271    {
 272        return isImporting;
 273    }
 274
 275    /// <summary>
 276    /// Determines if export button should be disabled
 277    /// </summary>
 278    /// <param name="isExporting">Current exporting state</param>
 279    /// <returns>True if button should be disabled</returns>
 280    public bool ShouldDisableExportButton(bool isExporting)
 281    {
 282        return isExporting;
 283    }
 284
 285    /// <summary>
 286    /// Determines if clear button should be disabled
 287    /// </summary>
 288    /// <param name="isClearing">Current clearing state</param>
 289    /// <returns>True if button should be disabled</returns>
 290    public bool ShouldDisableClearButton(bool isClearing)
 291    {
 292        return isClearing;
 293    }
 294
 295    /// <summary>
 296    /// Determines if save button should be disabled
 297    /// </summary>
 298    /// <param name="hasChanges">Whether settings have changes</param>
 299    /// <returns>True if button should be disabled</returns>
 300    public bool ShouldDisableSaveButton(bool hasChanges)
 301    {
 302        return !hasChanges;
 303    }
 304
 305    /// <summary>
 306    /// Determines if reset button should be disabled
 307    /// </summary>
 308    /// <param name="isAtDefaults">Whether settings are at defaults</param>
 309    /// <returns>True if button should be disabled</returns>
 310    public bool ShouldDisableResetButton(bool isAtDefaults)
 311    {
 312        return isAtDefaults;
 313    }
 314}
 315
 316/// <summary>
 317/// Settings validation result
 318/// </summary>
 319public class SettingsValidationResult
 320{
 14321    public bool IsValid { get; set; }
 21322    public List<string> Errors { get; set; } = new();
 323}
 324
 325/// <summary>
 326/// File validation result
 327/// </summary>
 328public class FileValidationResult
 329{
 330    public bool IsValid { get; set; }
 331    public string? ErrorMessage { get; set; }
 332}

Methods/Properties

get_IsValid()
get_Errors()