From 7d02f9c8759a9be11abf666631dc3d48e2dce627 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 18 Oct 2026 16:36:43 +0000 Subject: [PATCH] test(admin-panel): wait for the results of interactions in the logging configuration page tests The page loads its configuration file asynchronously. When a test interacts with it right after the content appeared, the page may still finish its initialization, so the event is handled after Click or Change returned. The tests then found the elements of the previous render: "No elements were found that matches the selector 'textarea'" or an UnknownEventHandlerIdException. This happened in about one of ten runs, mostly in the first run after a build (e.g. on CI). The tests now wait for the result of each interaction. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01XowA25b3pgLghrNZSZRwTi --- .../Pages/LoggingConfigurationPageTests.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/MUnique.OpenMU.Web.Tests/Pages/LoggingConfigurationPageTests.cs b/tests/MUnique.OpenMU.Web.Tests/Pages/LoggingConfigurationPageTests.cs index f328b64340..247d39e1b9 100644 --- a/tests/MUnique.OpenMU.Web.Tests/Pages/LoggingConfigurationPageTests.cs +++ b/tests/MUnique.OpenMU.Web.Tests/Pages/LoggingConfigurationPageTests.cs @@ -20,6 +20,11 @@ namespace MUnique.OpenMU.Web.Tests.Pages; /// /// Tests for the page. /// +/// +/// The page loads the configuration file asynchronously. When the tests interact with it right after +/// the content appeared, the page may still finish its initialization, and the event is handled after +/// Click or Change returned. So the tests wait for the result of each interaction. +/// [TestFixture] [NonParallelizable] public class LoggingConfigurationPageTests @@ -75,9 +80,13 @@ public void SavesChangedLevels() Assert.That(cut.Find("#defaultLogLevel").GetAttribute("value"), Is.EqualTo("Information")); cut.Find("#defaultLogLevel").Change("Debug"); + cut.WaitForState(() => cut.Find("#defaultLogLevel").GetAttribute("value") == "Debug"); cut.FindAll("button").First(b => b.TextContent.Contains(Resources.AddNew)).Click(); + cut.WaitForState(() => cut.FindAll("tbody input").Count == 2); cut.FindAll("tbody input")[1].Change("MUnique.OpenMU.GameLogic"); + cut.WaitForState(() => cut.FindAll("tbody input")[1].GetAttribute("value") == "MUnique.OpenMU.GameLogic"); cut.FindAll("tbody select")[1].Change("Verbose"); + cut.WaitForState(() => cut.FindAll("tbody select")[1].GetAttribute("value") == "Verbose"); cut.FindAll("button").First(b => b.TextContent.Contains(Resources.Save)).Click(); cut.WaitForAssertion(() => this._toastService.Verify(t => t.ShowSuccess(Resources.LoggingConfigurationSaved, null))); @@ -98,9 +107,10 @@ public void DoesNotSaveOverrideWithoutSource() cut.WaitForState(() => cut.FindAll("tbody tr").Count == 1); cut.FindAll("button").First(b => b.TextContent.Contains(Resources.AddNew)).Click(); + cut.WaitForState(() => cut.FindAll("tbody tr").Count == 2); cut.FindAll("button").First(b => b.TextContent.Contains(Resources.Save)).Click(); - this._toastService.Verify(t => t.ShowError(Resources.LogLevelOverridesInvalid, null)); + cut.WaitForAssertion(() => this._toastService.Verify(t => t.ShowError(Resources.LogLevelOverridesInvalid, null))); Assert.That(File.ReadAllText(this._filePath), Is.EqualTo(content)); } @@ -115,7 +125,8 @@ public void DoesNotSaveInvalidJson() cut.WaitForState(() => cut.FindAll("button.nav-link").Count == 2); cut.FindAll("button.nav-link")[1].Click(); - cut.Find("textarea").Change("{ \"MinimumLevel\": "); + cut.WaitForElement("textarea").Change("{ \"MinimumLevel\": "); + cut.WaitForState(() => cut.Find("textarea").GetAttribute("value") == "{ \"MinimumLevel\": "); cut.FindAll("button").First(b => b.TextContent.Contains(Resources.Save)).Click(); cut.WaitForAssertion(() => this._toastService.Verify(t => t.ShowError(It.Is(m => m.StartsWith("The JSON is invalid")), null)));