diff --git a/QuizExperiment.Admin/Client/Shared/Edit/RootComponent.razor b/QuizExperiment.Admin/Client/Shared/Edit/RootComponent.razor index 45b52f6..9bf3d23 100644 --- a/QuizExperiment.Admin/Client/Shared/Edit/RootComponent.razor +++ b/QuizExperiment.Admin/Client/Shared/Edit/RootComponent.razor @@ -48,6 +48,22 @@ + + + + + + Default Timeout (seconds) + + + + + + Apply to All Questions + + + + if (!AreAllQuestionsValid) { @@ -304,4 +320,22 @@ await Save(); NavigationManager.NavigateTo($"/manage?path={_questionSet.FolderPath}"); } + + private void ApplyDefaultTimeoutToAllQuestions() + { + if (_questionSet?.DefaultTimeout > 0) + { + foreach (var question in _questions) + { + question.Timeout = _questionSet.DefaultTimeout.Value; + } + string message = $"Applied timeout of {_questionSet.DefaultTimeout.Value} seconds to all {_questions.Count} questions."; + toastService.ShowSuccess(message); + } + else + { + string message = "Please enter a valid timeout value (greater than 0)."; + toastService.ShowError(message); + } + } } diff --git a/QuizExperiment.Models.Test/JsonSerializationTests.cs b/QuizExperiment.Models.Test/JsonSerializationTests.cs index 8be4650..b1bb088 100644 --- a/QuizExperiment.Models.Test/JsonSerializationTests.cs +++ b/QuizExperiment.Models.Test/JsonSerializationTests.cs @@ -5,6 +5,15 @@ namespace QuizExperiment.Models.Test { public class JsonSerializationTests { + private static JsonSerializerOptions GetJsonSerializerOptions() + { + var options = new JsonSerializerOptions(); + options.Converters.Add(new PolymorphicQuestionConverter()); + options.Converters.Add(new PolymorphicQuestionListConverter()); + options.PropertyNamingPolicy = null; + return options; + } + [Fact] public void DeserializeAndReserialize_V1Schema_PrintsOutput() { @@ -18,10 +27,7 @@ public void DeserializeAndReserialize_V1Schema_PrintsOutput() jsonPath = Path.GetFullPath(jsonPath); } var json = File.ReadAllText(jsonPath); - var options = new JsonSerializerOptions(); - options.Converters.Add(new PolymorphicQuestionConverter()); - options.Converters.Add(new PolymorphicQuestionListConverter()); - options.PropertyNamingPolicy = null; + var options = GetJsonSerializerOptions(); // Act var questionSet = JsonSerializer.Deserialize(json, options); @@ -45,10 +51,7 @@ public void SerializingMultipleQuestionTypes() { // Arrange var baseDir = AppContext.BaseDirectory; - var options = new JsonSerializerOptions(); - options.Converters.Add(new PolymorphicQuestionConverter()); - options.Converters.Add(new PolymorphicQuestionListConverter()); - options.PropertyNamingPolicy = null; + var options = GetJsonSerializerOptions(); // Act var questionSet = new QuestionSet @@ -85,5 +88,68 @@ public void SerializingMultipleQuestionTypes() $"A question is missing the 'questionType' property: {question}"); } } + + [Fact] + public void DeserializeQuestionSetWithDefaultTimeout() + { + // Arrange + var options = GetJsonSerializerOptions(); + + // Act + var questionSet = new QuestionSet + { + Id = "test-id", + Title = "Test Quiz", + DefaultTimeout = 45, + Questions = new List + { + new MultipleChoiceQuestion + { + Title = "Test Question", + Options = new[] { "A", "B", "C", "D" }, + CorrectAnswerIndex = 0, + Timeout = 30 + } + } + }; + var json = JsonSerializer.Serialize(questionSet, options); + var deserializedQuestionSet = JsonSerializer.Deserialize(json, options); + + // Assert + Assert.NotNull(deserializedQuestionSet); + Assert.Equal(45, deserializedQuestionSet.DefaultTimeout); + Assert.Equal("Test Quiz", deserializedQuestionSet.Title); + } + + [Fact] + public void DeserializeQuestionSetWithoutDefaultTimeout() + { + // Arrange + var options = GetJsonSerializerOptions(); + + // Act + var questionSet = new QuestionSet + { + Id = "test-id", + Title = "Test Quiz", + Questions = new List + { + new MultipleChoiceQuestion + { + Title = "Test Question", + Options = new[] { "A", "B", "C", "D" }, + CorrectAnswerIndex = 0, + Timeout = 30 + } + } + }; + var json = JsonSerializer.Serialize(questionSet, options); + var deserializedQuestionSet = JsonSerializer.Deserialize(json, options); + + // Assert + Assert.NotNull(deserializedQuestionSet); + Assert.Null(deserializedQuestionSet.DefaultTimeout); + Assert.Equal("Test Quiz", deserializedQuestionSet.Title); + } } } \ No newline at end of file diff --git a/QuizExperiment.Models.Test/TestData/v1SchemaQuestionSet.json b/QuizExperiment.Models.Test/TestData/v1SchemaQuestionSet.json index 14de99f..797e6b1 100644 --- a/QuizExperiment.Models.Test/TestData/v1SchemaQuestionSet.json +++ b/QuizExperiment.Models.Test/TestData/v1SchemaQuestionSet.json @@ -29,5 +29,6 @@ "correctAnswerIndex": 2 } ], - "folderPath": null + "folderPath": null, + "defaultTimeout": 30 } \ No newline at end of file diff --git a/QuizExperiment.Models.Test/TestData/v2SchemaQuestionSet.json b/QuizExperiment.Models.Test/TestData/v2SchemaQuestionSet.json index 1a028bf..ba7bfd9 100644 --- a/QuizExperiment.Models.Test/TestData/v2SchemaQuestionSet.json +++ b/QuizExperiment.Models.Test/TestData/v2SchemaQuestionSet.json @@ -24,5 +24,6 @@ "timeout": 15 } ], - "folderPath": null + "folderPath": null, + "defaultTimeout": 25 } \ No newline at end of file diff --git a/QuizExperiment.Models/QuestionSet.cs b/QuizExperiment.Models/QuestionSet.cs index 9ac3546..51fc9dd 100644 --- a/QuizExperiment.Models/QuestionSet.cs +++ b/QuizExperiment.Models/QuestionSet.cs @@ -27,5 +27,8 @@ public class QuestionSet [JsonPropertyName("folderPath")] public string? FolderPath { get; set; } + + [JsonPropertyName("defaultTimeout")] + public int? DefaultTimeout { get; set; } } }