From aeecd8890817974089d46b91c23a9056ce3f033c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 19 Jan 2026 16:04:47 +0000 Subject: [PATCH 1/3] Initial plan From 58a87412e4b20fd81002fff27c357c0d0a66de3d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 19 Jan 2026 16:10:13 +0000 Subject: [PATCH 2/3] Add default timeout feature to quiz header with apply to all questions button Co-authored-by: fortunkam <1851394+fortunkam@users.noreply.github.com> --- .../Client/Shared/Edit/RootComponent.razor | 34 +++++++++ .../JsonSerializationTests.cs | 69 +++++++++++++++++++ .../TestData/v1SchemaQuestionSet.json | 3 +- .../TestData/v2SchemaQuestionSet.json | 3 +- QuizExperiment.Models/QuestionSet.cs | 3 + 5 files changed, 110 insertions(+), 2 deletions(-) diff --git a/QuizExperiment.Admin/Client/Shared/Edit/RootComponent.razor b/QuizExperiment.Admin/Client/Shared/Edit/RootComponent.razor index 45b52f6..c2308b2 100644 --- a/QuizExperiment.Admin/Client/Shared/Edit/RootComponent.razor +++ b/QuizExperiment.Admin/Client/Shared/Edit/RootComponent.razor @@ -48,6 +48,22 @@ + + if (!AreAllQuestionsValid) { @@ -304,4 +320,22 @@ await Save(); NavigationManager.NavigateTo($"/manage?path={_questionSet.FolderPath}"); } + + private void ApplyDefaultTimeoutToAllQuestions() + { + if (_questionSet?.DefaultTimeout.HasValue == true && _questionSet.DefaultTimeout.Value > 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..9961c59 100644 --- a/QuizExperiment.Models.Test/JsonSerializationTests.cs +++ b/QuizExperiment.Models.Test/JsonSerializationTests.cs @@ -85,5 +85,74 @@ public void SerializingMultipleQuestionTypes() $"A question is missing the 'questionType' property: {question}"); } } + + [Fact] + public void DeserializeQuestionSetWithDefaultTimeout() + { + // Arrange + var options = new JsonSerializerOptions(); + options.Converters.Add(new PolymorphicQuestionConverter()); + options.Converters.Add(new PolymorphicQuestionListConverter()); + options.PropertyNamingPolicy = null; + + // 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 = new JsonSerializerOptions(); + options.Converters.Add(new PolymorphicQuestionConverter()); + options.Converters.Add(new PolymorphicQuestionListConverter()); + options.PropertyNamingPolicy = null; + + // 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; } } } From e0dd09b5cd7dbd2d5d968befada7c2d5b2c6c181 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 19 Jan 2026 16:12:06 +0000 Subject: [PATCH 3/3] Address code review feedback: add min validation, simplify condition, extract helper method Co-authored-by: fortunkam <1851394+fortunkam@users.noreply.github.com> --- .../Client/Shared/Edit/RootComponent.razor | 4 +-- .../JsonSerializationTests.cs | 29 +++++++++---------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/QuizExperiment.Admin/Client/Shared/Edit/RootComponent.razor b/QuizExperiment.Admin/Client/Shared/Edit/RootComponent.razor index c2308b2..9bf3d23 100644 --- a/QuizExperiment.Admin/Client/Shared/Edit/RootComponent.razor +++ b/QuizExperiment.Admin/Client/Shared/Edit/RootComponent.razor @@ -56,7 +56,7 @@ Default Timeout (seconds)
- +
@@ -323,7 +323,7 @@ private void ApplyDefaultTimeoutToAllQuestions() { - if (_questionSet?.DefaultTimeout.HasValue == true && _questionSet.DefaultTimeout.Value > 0) + if (_questionSet?.DefaultTimeout > 0) { foreach (var question in _questions) { diff --git a/QuizExperiment.Models.Test/JsonSerializationTests.cs b/QuizExperiment.Models.Test/JsonSerializationTests.cs index 9961c59..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 @@ -90,10 +93,7 @@ public void SerializingMultipleQuestionTypes() public void DeserializeQuestionSetWithDefaultTimeout() { // Arrange - 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 @@ -125,10 +125,7 @@ public void DeserializeQuestionSetWithDefaultTimeout() public void DeserializeQuestionSetWithoutDefaultTimeout() { // Arrange - 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