diff --git a/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ExportTest.cs b/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ExportTest.cs
index bbb02b5a..ea6aefe5 100644
--- a/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ExportTest.cs
+++ b/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ExportTest.cs
@@ -35,7 +35,7 @@ public async Task DownloadCuesheet_GeneratesCuesheetFile_WhenCuesheetIsValid()
await detailView.EditTrackAsync("Track Artist 1", "Track Title 1");
await bar.OpenExportDialogAsync("Cuesheet");
var downloadTask = TestPage.WaitForDownloadAsync();
- await TestPage.GetByRole(AriaRole.Button, new() { Name = "Download-Cuesheet.cue" }).ClickAsync();
+ await TestPage.GetByRole(AriaRole.Button, new() { Name = "Download" }).ClickAsync();
var download = await downloadTask;
using var stream = await download.CreateReadStreamAsync();
using var reader = new StreamReader(stream);
diff --git a/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/ExportTest.cs b/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/ExportTest.cs
index eb85f017..859ae80d 100644
--- a/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/ExportTest.cs
+++ b/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/ExportTest.cs
@@ -37,7 +37,7 @@ public async Task DownloadCuesheet_GeneratesCuesheetFile_WhenCuesheetIsValid()
await detailView.EditTrackAsync("Track Artist 1", "Track Title 1");
await bar.OpenExportDialogAsync("Cuesheet");
var downloadTask = TestPage.WaitForDownloadAsync();
- await TestPage.GetByRole(AriaRole.Button, new() { Name = "Download-Cuesheet.cue" }).ClickAsync();
+ await TestPage.GetByRole(AriaRole.Button, new() { Name = "Download" }).ClickAsync();
var download = await downloadTask;
using var stream = await download.CreateReadStreamAsync();
using var reader = new StreamReader(stream);
diff --git a/AudioCuesheetEditor.Tests/Services/IO/TextImportServiceTests.cs b/AudioCuesheetEditor.Tests/Services/IO/TextImportServiceTests.cs
index 8cc1b33c..df44aa1b 100644
--- a/AudioCuesheetEditor.Tests/Services/IO/TextImportServiceTests.cs
+++ b/AudioCuesheetEditor.Tests/Services/IO/TextImportServiceTests.cs
@@ -13,13 +13,13 @@
//You should have received a copy of the GNU General Public License
//along with Foobar. If not, see
//.
-using AudioCuesheetEditor.Data.Options;
using AudioCuesheetEditor.Model.AudioCuesheet;
using AudioCuesheetEditor.Model.AudioCuesheet.Import;
using AudioCuesheetEditor.Model.IO.Import;
using AudioCuesheetEditor.Model.Options;
using AudioCuesheetEditor.Model.Utility;
using AudioCuesheetEditor.Services.IO;
+using AudioCuesheetEditor.Services.Options;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
diff --git a/AudioCuesheetEditor.Tests/Services/Options/LocalStorageOptionsProviderTests.cs b/AudioCuesheetEditor.Tests/Services/Options/LocalStorageOptionsProviderTests.cs
new file mode 100644
index 00000000..d23853d7
--- /dev/null
+++ b/AudioCuesheetEditor.Tests/Services/Options/LocalStorageOptionsProviderTests.cs
@@ -0,0 +1,104 @@
+//This file is part of AudioCuesheetEditor.
+
+//AudioCuesheetEditor is free software: you can redistribute it and/or modify
+//it under the terms of the GNU General Public License as published by
+//the Free Software Foundation, either version 3 of the License, or
+//(at your option) any later version.
+
+//AudioCuesheetEditor is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+
+//You should have received a copy of the GNU General Public License
+//along with Foobar. If not, see
+//.
+using AudioCuesheetEditor.Model.AudioCuesheet;
+using AudioCuesheetEditor.Model.Entity;
+using AudioCuesheetEditor.Model.Options;
+using AudioCuesheetEditor.Services.IO;
+using AudioCuesheetEditor.Services.Options;
+using AudioCuesheetEditor.Services.UI;
+using Microsoft.Extensions.Localization;
+using Microsoft.JSInterop;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AudioCuesheetEditor.Tests.Services.Options
+{
+ [TestClass()]
+ public class LocalStorageOptionsProviderTests
+ {
+ private readonly LocalStorageOptionsProvider _service;
+ private readonly Mock _jsRuntime = new();
+ private readonly Mock> _localizer = new();
+
+ public LocalStorageOptionsProviderTests()
+ {
+ _service = new(_jsRuntime.Object, _localizer.Object);
+ }
+
+ [TestMethod]
+ public async Task SaveOptionsAsync_NonValidatableOptions_ReturnsSuccessAsync()
+ {
+ // Arrange
+ var options = new ExportOptions();
+ Boolean eventRaised = false;
+ _service.OptionSaved += delegate
+ {
+ eventRaised = true;
+ };
+ // Act
+ var result = await _service.SaveOptionsAsync(options);
+ // Assert
+ Assert.IsTrue(result.IsSuccess);
+ Assert.IsTrue(eventRaised);
+ }
+
+ [TestMethod]
+ public async Task SaveOptionsAsync_ValidOptions_ReturnsSuccessAsync()
+ {
+ // Arrange
+ var options = new DownloadOptions()
+ {
+ ProjectFilename = "Test.ace",
+ CuesheetFilename = "Test.cue"
+ };
+ Boolean eventRaised = false;
+ _service.OptionSaved += delegate
+ {
+ eventRaised = true;
+ };
+ // Act
+ var result = await _service.SaveOptionsAsync(options);
+ // Assert
+ Assert.IsTrue(result.IsSuccess);
+ Assert.IsTrue(eventRaised);
+ }
+
+ [TestMethod]
+ public async Task SaveOptionsAsync_InvalidOptions_ReturnsFailureAsync()
+ {
+ // Arrange
+ var options = new DownloadOptions()
+ {
+ CuesheetFilename = null,
+ ProjectFilename = null
+ };
+ Boolean eventRaised = false;
+ _service.OptionSaved += delegate
+ {
+ eventRaised = true;
+ };
+ // Act
+ var result = await _service.SaveOptionsAsync(options);
+ // Assert
+ Assert.IsFalse(result.IsSuccess);
+ Assert.IsFalse(eventRaised);
+ }
+ }
+}
diff --git a/AudioCuesheetEditor.Tests/Services/UI/ApplicationOptionsTimeSpanParserTests.cs b/AudioCuesheetEditor.Tests/Services/UI/ApplicationOptionsTimeSpanParserTests.cs
index a8205c0f..6412aa39 100644
--- a/AudioCuesheetEditor.Tests/Services/UI/ApplicationOptionsTimeSpanParserTests.cs
+++ b/AudioCuesheetEditor.Tests/Services/UI/ApplicationOptionsTimeSpanParserTests.cs
@@ -13,10 +13,10 @@
//You should have received a copy of the GNU General Public License
//along with Foobar. If not, see
//.
-using AudioCuesheetEditor.Data.Options;
using AudioCuesheetEditor.Model.AudioCuesheet;
using AudioCuesheetEditor.Model.Options;
using AudioCuesheetEditor.Model.Utility;
+using AudioCuesheetEditor.Services.Options;
using AudioCuesheetEditor.Services.UI;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
diff --git a/AudioCuesheetEditor.Tests/Services/UI/SessionStateContainerTests.cs b/AudioCuesheetEditor.Tests/Services/UI/SessionStateContainerTests.cs
index 91151f28..93a7028a 100644
--- a/AudioCuesheetEditor.Tests/Services/UI/SessionStateContainerTests.cs
+++ b/AudioCuesheetEditor.Tests/Services/UI/SessionStateContainerTests.cs
@@ -13,10 +13,10 @@
//You should have received a copy of the GNU General Public License
//along with Foobar. If not, see
//.
-using AudioCuesheetEditor.Data.Options;
using AudioCuesheetEditor.Model.AudioCuesheet;
using AudioCuesheetEditor.Model.IO.Import;
using AudioCuesheetEditor.Model.Options;
+using AudioCuesheetEditor.Services.Options;
using AudioCuesheetEditor.Services.UI;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
diff --git a/AudioCuesheetEditor/Program.cs b/AudioCuesheetEditor/Program.cs
index 0f2f478f..52932a92 100644
--- a/AudioCuesheetEditor/Program.cs
+++ b/AudioCuesheetEditor/Program.cs
@@ -14,13 +14,13 @@
//along with Foobar. If not, see
//.
using AudioCuesheetEditor;
-using AudioCuesheetEditor.Data.Options;
-using AudioCuesheetEditor.Data.Services;
using AudioCuesheetEditor.Extensions;
using AudioCuesheetEditor.Model.Options;
using AudioCuesheetEditor.Services.Audio;
using AudioCuesheetEditor.Services.AudioCuesheet;
using AudioCuesheetEditor.Services.IO;
+using AudioCuesheetEditor.Services.Options;
+using AudioCuesheetEditor.Services.Provider;
using AudioCuesheetEditor.Services.UI;
using AudioCuesheetEditor.Services.Validation;
using BlazorDownloadFile;
diff --git a/AudioCuesheetEditor/Services/IO/TextImportService.cs b/AudioCuesheetEditor/Services/IO/TextImportService.cs
index e196ca0f..39bca6d0 100644
--- a/AudioCuesheetEditor/Services/IO/TextImportService.cs
+++ b/AudioCuesheetEditor/Services/IO/TextImportService.cs
@@ -14,13 +14,13 @@
//along with Foobar. If not, see
//.
-using AudioCuesheetEditor.Data.Options;
using AudioCuesheetEditor.Model.AudioCuesheet;
using AudioCuesheetEditor.Model.AudioCuesheet.Import;
using AudioCuesheetEditor.Model.IO.Audio;
using AudioCuesheetEditor.Model.IO.Import;
using AudioCuesheetEditor.Model.Options;
using AudioCuesheetEditor.Model.Utility;
+using AudioCuesheetEditor.Services.Options;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
diff --git a/AudioCuesheetEditor/Data/Options/ILocalStorageOptionsProvider.cs b/AudioCuesheetEditor/Services/Options/ILocalStorageOptionsProvider.cs
similarity index 82%
rename from AudioCuesheetEditor/Data/Options/ILocalStorageOptionsProvider.cs
rename to AudioCuesheetEditor/Services/Options/ILocalStorageOptionsProvider.cs
index e96fee9d..db629e4b 100644
--- a/AudioCuesheetEditor/Data/Options/ILocalStorageOptionsProvider.cs
+++ b/AudioCuesheetEditor/Services/Options/ILocalStorageOptionsProvider.cs
@@ -16,14 +16,14 @@
using AudioCuesheetEditor.Model.Options;
using System.Linq.Expressions;
-namespace AudioCuesheetEditor.Data.Options
+namespace AudioCuesheetEditor.Services.Options
{
public interface ILocalStorageOptionsProvider
{
event EventHandler? OptionSaved;
Task GetOptionsAsync() where T : IOptions;
- Task SaveOptionsAsync(IOptions options);
- Task SaveOptionsValueAsync(Expression> propertyExpression, object? value) where T : class, IOptions, new();
+ Task SaveOptionsAsync(IOptions options);
+ Task SaveOptionsValueAsync(Expression> propertyExpression, object? value) where T : class, IOptions, new();
Task SaveNestedOptionValueAsync(Expression> nestedPropertyExpression, Expression> valuePropertyExpression, TValue value) where T : class, IOptions, new();
}
}
diff --git a/AudioCuesheetEditor/Data/Options/LocalStorageOptionsProvider.cs b/AudioCuesheetEditor/Services/Options/LocalStorageOptionsProvider.cs
similarity index 80%
rename from AudioCuesheetEditor/Data/Options/LocalStorageOptionsProvider.cs
rename to AudioCuesheetEditor/Services/Options/LocalStorageOptionsProvider.cs
index 10a70da7..aa46c044 100644
--- a/AudioCuesheetEditor/Data/Options/LocalStorageOptionsProvider.cs
+++ b/AudioCuesheetEditor/Services/Options/LocalStorageOptionsProvider.cs
@@ -15,20 +15,21 @@
//.
using AudioCuesheetEditor.Model.Entity;
using AudioCuesheetEditor.Model.Options;
+using Microsoft.Extensions.Localization;
using Microsoft.JSInterop;
using System.Linq.Expressions;
using System.Reflection;
using System.Text.Json;
-namespace AudioCuesheetEditor.Data.Options
+namespace AudioCuesheetEditor.Services.Options
{
- public class LocalStorageOptionsProvider(IJSRuntime jsRuntime): ILocalStorageOptionsProvider
+ public class LocalStorageOptionsProvider(IJSRuntime jsRuntime, IStringLocalizer localizer) : ILocalStorageOptionsProvider
{
public event EventHandler? OptionSaved;
private readonly IJSRuntime _jsRuntime = jsRuntime;
-
- private readonly JsonSerializerOptions SerializerOptions = new()
+ private readonly IStringLocalizer _localizer = localizer;
+ private readonly JsonSerializerOptions _serializerOptions = new()
{
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
};
@@ -61,22 +62,21 @@ public async Task GetOptionsAsync() where T : IOptions
}
}
- public async Task SaveOptionsAsync(IOptions options)
+ public async Task SaveOptionsAsync(IOptions options)
{
- bool saveOptions = true;
if (options is IValidateable validateable)
{
- saveOptions = validateable.Validate().Status != ValidationStatus.Error;
- }
- if (saveOptions)
- {
- var optionsJson = JsonSerializer.Serialize