Replaced unreliable sleeps with WebDriverWait

This commit is contained in:
2025-11-10 00:29:36 +01:00
parent 53645a4b7e
commit fb15d825d0
6 changed files with 35 additions and 16 deletions

View File

@@ -7,6 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2" /> <PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="DotNetSeleniumExtras.WaitHelpers" Version="3.11.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.10" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="Selenium.Support" Version="4.38.0" /> <PackageReference Include="Selenium.Support" Version="4.38.0" />

View File

@@ -2,6 +2,8 @@ using System.Diagnostics;
using System.Net; using System.Net;
using OpenQA.Selenium; using OpenQA.Selenium;
using OpenQA.Selenium.Chrome; using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using SeleniumExtras.WaitHelpers;
namespace Berufsschule_HAM.E2ETests.Helper; namespace Berufsschule_HAM.E2ETests.Helper;
@@ -9,6 +11,7 @@ public static class AppHelper
{ {
public const string ServerUrl = "http://localhost:5275"; public const string ServerUrl = "http://localhost:5275";
public static Uri ServerUri = new(ServerUrl); public static Uri ServerUri = new(ServerUrl);
public static int DefaultTimeout = 5;
public static async Task<Process> StartApp(string appUrl) public static async Task<Process> StartApp(string appUrl)
{ {
var startInfo = new ProcessStartInfo var startInfo = new ProcessStartInfo
@@ -123,4 +126,19 @@ public static class AppHelper
throw; throw;
} }
} }
public static void AwaitVisible(ChromeDriver driver, By by)
{
Timeout(driver).Until(ExpectedConditions.ElementIsVisible(by));
}
public static WebDriverWait Timeout(ChromeDriver driver, int timeout)
{
return new(driver, TimeSpan.FromSeconds(timeout));
}
public static WebDriverWait Timeout(ChromeDriver driver)
{
return new(driver, TimeSpan.FromSeconds(DefaultTimeout));
}
} }

View File

@@ -3,6 +3,7 @@ using System.Net;
using OpenQA.Selenium; using OpenQA.Selenium;
using OpenQA.Selenium.Chrome; using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI; using OpenQA.Selenium.Support.UI;
using SeleniumExtras.WaitHelpers;
namespace Berufsschule_HAM.E2ETests.Helper; namespace Berufsschule_HAM.E2ETests.Helper;
@@ -13,7 +14,7 @@ public static class GroupsHelper
NavigateToGroupsPage(driver); NavigateToGroupsPage(driver);
IWebElement createGroupButton = driver.FindElement(By.CssSelector("button[data-bs-target=\"#createGroupModal\"]")); IWebElement createGroupButton = driver.FindElement(By.CssSelector("button[data-bs-target=\"#createGroupModal\"]"));
createGroupButton.Click(); createGroupButton.Click();
Thread.Sleep(500); AppHelper.AwaitVisible(driver, By.Id("cn"));
driver.FindElement(By.Id("cn")).SendKeys(cn); driver.FindElement(By.Id("cn")).SendKeys(cn);
driver.FindElement(By.Id("displayname")).SendKeys(displayName); driver.FindElement(By.Id("displayname")).SendKeys(displayName);
if (canInventorize) driver.FindElement(By.Id("canInventorize")).Click(); if (canInventorize) driver.FindElement(By.Id("canInventorize")).Click();
@@ -32,7 +33,7 @@ public static class GroupsHelper
NavigateToGroupsPage(driver); NavigateToGroupsPage(driver);
IWebElement updateGroupButton = driver.FindElement(By.CssSelector($"button[data-group-id=\"{cn}\"].btn-update")); IWebElement updateGroupButton = driver.FindElement(By.CssSelector($"button[data-group-id=\"{cn}\"].btn-update"));
AppHelper.ScrollIntoViewAndClick(driver, updateGroupButton); AppHelper.ScrollIntoViewAndClick(driver, updateGroupButton);
Thread.Sleep(500); AppHelper.AwaitVisible(driver, By.CssSelector("input#groupId"));
var cnInput = driver.FindElement(By.CssSelector("input#groupId")); var cnInput = driver.FindElement(By.CssSelector("input#groupId"));
cnInput.Click(); cnInput.Click();
cnInput.Clear(); cnInput.Clear();
@@ -69,7 +70,7 @@ public static class GroupsHelper
NavigateToGroupsPage(driver); NavigateToGroupsPage(driver);
IWebElement deleteButton = driver.FindElement(By.CssSelector($"button[data-group-id=\"{cn}\"].btn-delete")); IWebElement deleteButton = driver.FindElement(By.CssSelector($"button[data-group-id=\"{cn}\"].btn-delete"));
AppHelper.ScrollIntoViewAndClick(driver, deleteButton); AppHelper.ScrollIntoViewAndClick(driver, deleteButton);
Thread.Sleep(1000); AppHelper.AwaitVisible(driver, By.Id("deleteModal"));
driver.FindElement(By.Id("deleteModal")); driver.FindElement(By.Id("deleteModal"));
IWebElement deleteConfirmButton = driver.FindElement(By.CssSelector("#deleteModal .btn-danger")); IWebElement deleteConfirmButton = driver.FindElement(By.CssSelector("#deleteModal .btn-danger"));
deleteConfirmButton.Click(); deleteConfirmButton.Click();

View File

@@ -13,7 +13,7 @@ public static class LocationsHelper
NavigateToLocationsPage(driver); NavigateToLocationsPage(driver);
IWebElement createLocationButton = driver.FindElement(By.CssSelector("button[data-bs-target=\"#createModal\"]")); IWebElement createLocationButton = driver.FindElement(By.CssSelector("button[data-bs-target=\"#createModal\"]"));
createLocationButton.Click(); createLocationButton.Click();
Thread.Sleep(500); AppHelper.AwaitVisible(driver, By.Id("createLocationName"));
driver.FindElement(By.Id("createLocationName")).SendKeys(name); driver.FindElement(By.Id("createLocationName")).SendKeys(name);
driver.FindElement(By.Id("createRoomNumber")).SendKeys(room); driver.FindElement(By.Id("createRoomNumber")).SendKeys(room);
driver.FindElement(By.Id("createSeat")).SendKeys(seat); driver.FindElement(By.Id("createSeat")).SendKeys(seat);
@@ -28,7 +28,7 @@ public static class LocationsHelper
string selector = $"button.btn-warning[data-location-name=\"{name}\"][data-room-number=\"{room}\"][data-seat=\"{seat}\"]"; string selector = $"button.btn-warning[data-location-name=\"{name}\"][data-room-number=\"{room}\"][data-seat=\"{seat}\"]";
IWebElement updateLocationButton = driver.FindElement(By.CssSelector(selector)); IWebElement updateLocationButton = driver.FindElement(By.CssSelector(selector));
AppHelper.ScrollIntoViewAndClick(driver, updateLocationButton); AppHelper.ScrollIntoViewAndClick(driver, updateLocationButton);
Thread.Sleep(500); AppHelper.AwaitVisible(driver, By.Id("editLocationName"));
List<string[]> ids = [["editLocationName", newName], ["editRoomNumber", newRoom], ["editSeat", newSeat]]; List<string[]> ids = [["editLocationName", newName], ["editRoomNumber", newRoom], ["editSeat", newSeat]];
ids.ForEach(id => ids.ForEach(id =>
{ {
@@ -48,7 +48,7 @@ public static class LocationsHelper
NavigateToLocationsPage(driver); NavigateToLocationsPage(driver);
IWebElement deleteButton = driver.FindElement(By.CssSelector($"button.btn-danger[data-location-id=\"{cn}\"]")); IWebElement deleteButton = driver.FindElement(By.CssSelector($"button.btn-danger[data-location-id=\"{cn}\"]"));
AppHelper.ScrollIntoViewAndClick(driver, deleteButton); AppHelper.ScrollIntoViewAndClick(driver, deleteButton);
Thread.Sleep(1000); AppHelper.AwaitVisible(driver, By.Id("deleteModal"));
driver.FindElement(By.Id("deleteModal")); driver.FindElement(By.Id("deleteModal"));
IWebElement deleteConfirmButton = driver.FindElement(By.Id("deleteForm")); IWebElement deleteConfirmButton = driver.FindElement(By.Id("deleteForm"));
deleteConfirmButton.Click(); deleteConfirmButton.Click();

View File

@@ -1,6 +1,7 @@
using Berufsschule_HAM.E2ETests.Helper; using Berufsschule_HAM.E2ETests.Helper;
using OpenQA.Selenium; using OpenQA.Selenium;
using OpenQA.Selenium.Chrome; using OpenQA.Selenium.Chrome;
using SeleniumExtras.WaitHelpers;
using System.Diagnostics; using System.Diagnostics;
namespace Berufsschule_HAM.E2ETests; namespace Berufsschule_HAM.E2ETests;
@@ -46,7 +47,7 @@ public class HomePageTests : IDisposable
IWebElement logout = _driver.FindElement(By.CssSelector("a[href=\"/Home/Logout\"]")); IWebElement logout = _driver.FindElement(By.CssSelector("a[href=\"/Home/Logout\"]"));
Assert.True(logout.Displayed); Assert.True(logout.Displayed);
logout.Click(); logout.Click();
Thread.Sleep(250); AppHelper.Timeout(_driver).Until(ExpectedConditions.UrlContains("/Home/Login"));
Assert.Contains("/Home/Login", _driver.Url); Assert.Contains("/Home/Login", _driver.Url);
} }

View File

@@ -1,6 +1,7 @@
using Berufsschule_HAM.E2ETests.Helper; using Berufsschule_HAM.E2ETests.Helper;
using OpenQA.Selenium; using OpenQA.Selenium;
using OpenQA.Selenium.Chrome; using OpenQA.Selenium.Chrome;
using SeleniumExtras.WaitHelpers;
using System.Diagnostics; using System.Diagnostics;
namespace Berufsschule_HAM.E2ETests; namespace Berufsschule_HAM.E2ETests;
@@ -41,11 +42,10 @@ public class InventoryPageTests : IDisposable
assetIdInputField.SendKeys("1"); assetIdInputField.SendKeys("1");
IWebElement scanBarcode = _driver.FindElement(By.Id("enterAssetIdManuallyButton")); IWebElement scanBarcode = _driver.FindElement(By.Id("enterAssetIdManuallyButton"));
scanBarcode.Click(); scanBarcode.Click();
Thread.Sleep(500); AppHelper.AwaitVisible(_driver, By.Id("viewAssetModal"));
IWebElement viewModal = _driver.FindElement(By.Id("viewAssetModal")); IWebElement viewModal = _driver.FindElement(By.Id("viewAssetModal"));
Assert.True(viewModal.Displayed); Assert.True(viewModal.Displayed);
Assert.True(_driver.FindElement(By.CssSelector("svg[class=\"form-control\"]")).Displayed); Assert.True(_driver.FindElement(By.CssSelector("svg[class=\"form-control\"]")).Displayed);
Thread.Sleep(500);
Assert.NotEmpty(_driver.FindElement(By.Id("detailName")).GetAttribute("value") ?? ""); Assert.NotEmpty(_driver.FindElement(By.Id("detailName")).GetAttribute("value") ?? "");
Assert.NotEmpty(_driver.FindElement(By.Id("detailLocation")).GetAttribute("value") ?? ""); Assert.NotEmpty(_driver.FindElement(By.Id("detailLocation")).GetAttribute("value") ?? "");
@@ -71,12 +71,11 @@ public class InventoryPageTests : IDisposable
assetIdInputField.SendKeys("1"); assetIdInputField.SendKeys("1");
IWebElement scanBarcode = _driver.FindElement(By.Id("enterAssetIdManuallyButton")); IWebElement scanBarcode = _driver.FindElement(By.Id("enterAssetIdManuallyButton"));
scanBarcode.Click(); scanBarcode.Click();
Thread.Sleep(500); AppHelper.AwaitVisible(_driver, By.Id("viewAssetModal"));
IWebElement viewModal = _driver.FindElement(By.Id("viewAssetModal")); IWebElement viewModal = _driver.FindElement(By.Id("viewAssetModal"));
Assert.True(viewModal.Displayed); Assert.True(viewModal.Displayed);
IWebElement okButton = _driver.FindElement(By.CssSelector("#viewAssetModal button.btn.btn-primary[data-bs-dismiss=\"modal\"]")); IWebElement okButton = _driver.FindElement(By.CssSelector("#viewAssetModal button.btn.btn-primary[data-bs-dismiss=\"modal\"]"));
okButton.Click(); okButton.Click();
Thread.Sleep(500);
Assert.True(AppHelper.TryRetryFindSuccessToast(_driver)); Assert.True(AppHelper.TryRetryFindSuccessToast(_driver));
} }
@@ -89,15 +88,14 @@ public class InventoryPageTests : IDisposable
assetIdInputField.SendKeys("1"); assetIdInputField.SendKeys("1");
IWebElement scanBarcode = _driver.FindElement(By.Id("enterAssetIdManuallyButton")); IWebElement scanBarcode = _driver.FindElement(By.Id("enterAssetIdManuallyButton"));
scanBarcode.Click(); scanBarcode.Click();
Thread.Sleep(500); AppHelper.AwaitVisible(_driver, By.Id("viewAssetModal"));
IWebElement viewModal = _driver.FindElement(By.Id("viewAssetModal")); IWebElement viewModal = _driver.FindElement(By.Id("viewAssetModal"));
Assert.True(viewModal.Displayed); Assert.True(viewModal.Displayed);
IWebElement updateButton = _driver.FindElement(By.CssSelector("#viewAssetModal button.btn.btn-warning[data-bs-dismiss=\"modal\"]")); IWebElement updateButton = _driver.FindElement(By.CssSelector("#viewAssetModal button.btn.btn-warning[data-bs-dismiss=\"modal\"]"));
updateButton.Click(); updateButton.Click();
Thread.Sleep(1000); AppHelper.AwaitVisible(_driver, By.CssSelector("#updateAssetModal button.btn.btn-warning[type=\"submit\"]"));
IWebElement okButton = _driver.FindElement(By.CssSelector("#updateAssetModal button.btn.btn-warning[type=\"submit\"]")); IWebElement okButton = _driver.FindElement(By.CssSelector("#updateAssetModal button.btn.btn-warning[type=\"submit\"]"));
okButton.Click(); okButton.Click();
Thread.Sleep(500);
Assert.True(AppHelper.TryRetryFindSuccessToast(_driver)); Assert.True(AppHelper.TryRetryFindSuccessToast(_driver));
} }
@@ -108,7 +106,7 @@ public class InventoryPageTests : IDisposable
_driver.Navigate().GoToUrl(new Uri(serverUri, "/Home/Inventory")); _driver.Navigate().GoToUrl(new Uri(serverUri, "/Home/Inventory"));
IWebElement link = _driver.FindElement(By.CssSelector("a[href=\"/Home/Assets?CreateModal=true\"]")); IWebElement link = _driver.FindElement(By.CssSelector("a[href=\"/Home/Assets?CreateModal=true\"]"));
link.Click(); link.Click();
Thread.Sleep(500); AppHelper.Timeout(_driver).Until(ExpectedConditions.UrlContains("/Home/Assets"));
Assert.Contains("/Home/Assets", _driver.Url); Assert.Contains("/Home/Assets", _driver.Url);
} }
@@ -119,7 +117,7 @@ public class InventoryPageTests : IDisposable
_driver.Navigate().GoToUrl(new Uri(serverUri, "/Home/Inventory")); _driver.Navigate().GoToUrl(new Uri(serverUri, "/Home/Inventory"));
IWebElement openPrintModalButton = _driver.FindElement(By.Id("openPrintModal")); IWebElement openPrintModalButton = _driver.FindElement(By.Id("openPrintModal"));
openPrintModalButton.Click(); openPrintModalButton.Click();
Thread.Sleep(500); AppHelper.AwaitVisible(_driver, By.Id("printModal"));
IWebElement printModal = _driver.FindElement(By.Id("printModal")); IWebElement printModal = _driver.FindElement(By.Id("printModal"));
Assert.True(printModal.Displayed); Assert.True(printModal.Displayed);
} }