From e86cd5c9c8e84f93b506ede39a7efab2e80ea551 Mon Sep 17 00:00:00 2001 From: 6d486f49 <76097bcc@gmail.com> Date: Tue, 19 May 2026 13:36:29 -0400 Subject: [PATCH] Stub Gen Selenium Tests --- AOW4.SeleniumTests/AOW4.SeleniumTests.csproj | 19 +++++ AOW4.SeleniumTests/Driver/DriverFactory.cs | 27 +++++++ AOW4.SeleniumTests/Pages/CounterPage.cs | 15 ++++ AOW4.SeleniumTests/Pages/HomePage.cs | 15 ++++ AOW4.SeleniumTests/Pages/NavMenuPage.cs | 27 +++++++ AOW4.SeleniumTests/Pages/WeatherPage.cs | 15 ++++ AOW4.SeleniumTests/README.md | 20 ++++++ AOW4.SeleniumTests/Tests/BaseTest.cs | 36 ++++++++++ AOW4.SeleniumTests/Tests/BrokenLinksTest.cs | 74 ++++++++++++++++++++ AOW4.SeleniumTests/Tests/NavigationTests.cs | 25 +++++++ AOW4.slnx | 1 + 11 files changed, 274 insertions(+) create mode 100644 AOW4.SeleniumTests/AOW4.SeleniumTests.csproj create mode 100644 AOW4.SeleniumTests/Driver/DriverFactory.cs create mode 100644 AOW4.SeleniumTests/Pages/CounterPage.cs create mode 100644 AOW4.SeleniumTests/Pages/HomePage.cs create mode 100644 AOW4.SeleniumTests/Pages/NavMenuPage.cs create mode 100644 AOW4.SeleniumTests/Pages/WeatherPage.cs create mode 100644 AOW4.SeleniumTests/README.md create mode 100644 AOW4.SeleniumTests/Tests/BaseTest.cs create mode 100644 AOW4.SeleniumTests/Tests/BrokenLinksTest.cs create mode 100644 AOW4.SeleniumTests/Tests/NavigationTests.cs diff --git a/AOW4.SeleniumTests/AOW4.SeleniumTests.csproj b/AOW4.SeleniumTests/AOW4.SeleniumTests.csproj new file mode 100644 index 0000000..2ab7cb8 --- /dev/null +++ b/AOW4.SeleniumTests/AOW4.SeleniumTests.csproj @@ -0,0 +1,19 @@ + + + + net10.0 + false + enable + enable + + + + + + + + + + + + diff --git a/AOW4.SeleniumTests/Driver/DriverFactory.cs b/AOW4.SeleniumTests/Driver/DriverFactory.cs new file mode 100644 index 0000000..aa2643a --- /dev/null +++ b/AOW4.SeleniumTests/Driver/DriverFactory.cs @@ -0,0 +1,27 @@ +using System; +using OpenQA.Selenium; +using OpenQA.Selenium.Chrome; + +namespace AOW4.SeleniumTests.Driver; + +public static class DriverFactory +{ + public static IWebDriver CreateChromeDriver() + { + var options = new ChromeOptions(); + var headless = Environment.GetEnvironmentVariable("HEADLESS"); + if (!string.IsNullOrEmpty(headless) && (headless == "1" || headless.Equals("true", StringComparison.OrdinalIgnoreCase))) + { + options.AddArgument("--headless=new"); + } + + options.AddArgument("--disable-gpu"); + options.AddArgument("--no-sandbox"); + options.AddArgument("--disable-dev-shm-usage"); + + var service = ChromeDriverService.CreateDefaultService(); + service.HideCommandPromptWindow = true; + + return new ChromeDriver(service, options); + } +} diff --git a/AOW4.SeleniumTests/Pages/CounterPage.cs b/AOW4.SeleniumTests/Pages/CounterPage.cs new file mode 100644 index 0000000..877e249 --- /dev/null +++ b/AOW4.SeleniumTests/Pages/CounterPage.cs @@ -0,0 +1,15 @@ +using OpenQA.Selenium; + +namespace AOW4.SeleniumTests.Pages; + +public class CounterPage +{ + private readonly IWebDriver _driver; + public CounterPage(IWebDriver driver) => _driver = driver; + + public bool IsAt() + { + var url = _driver.Url ?? string.Empty; + return url.Contains("counter", System.StringComparison.OrdinalIgnoreCase) || _driver.PageSource.Contains("Counter"); + } +} diff --git a/AOW4.SeleniumTests/Pages/HomePage.cs b/AOW4.SeleniumTests/Pages/HomePage.cs new file mode 100644 index 0000000..8f571d2 --- /dev/null +++ b/AOW4.SeleniumTests/Pages/HomePage.cs @@ -0,0 +1,15 @@ +using OpenQA.Selenium; + +namespace AOW4.SeleniumTests.Pages; + +public class HomePage +{ + private readonly IWebDriver _driver; + public HomePage(IWebDriver driver) => _driver = driver; + + public bool IsAt() + { + var url = _driver.Url ?? string.Empty; + return url.EndsWith("/") || url.Contains("/index", System.StringComparison.OrdinalIgnoreCase) || _driver.PageSource.Contains("Home"); + } +} diff --git a/AOW4.SeleniumTests/Pages/NavMenuPage.cs b/AOW4.SeleniumTests/Pages/NavMenuPage.cs new file mode 100644 index 0000000..9b3a599 --- /dev/null +++ b/AOW4.SeleniumTests/Pages/NavMenuPage.cs @@ -0,0 +1,27 @@ +using System.Linq; +using OpenQA.Selenium; + +namespace AOW4.SeleniumTests.Pages; + +public class NavMenuPage +{ + private readonly IWebDriver _driver; + + public NavMenuPage(IWebDriver driver) + { + _driver = driver; + } + + public void ClickLinkByText(string linkText) + { + var link = _driver.FindElements(By.CssSelector("a[href]")) + .FirstOrDefault(e => !string.IsNullOrWhiteSpace(e.Text) && e.Text.Trim().Equals(linkText, System.StringComparison.OrdinalIgnoreCase)); + + if (link == null) + { + throw new NoSuchElementException($"Link with text '{linkText}' not found in the page."); + } + + link.Click(); + } +} diff --git a/AOW4.SeleniumTests/Pages/WeatherPage.cs b/AOW4.SeleniumTests/Pages/WeatherPage.cs new file mode 100644 index 0000000..e482da8 --- /dev/null +++ b/AOW4.SeleniumTests/Pages/WeatherPage.cs @@ -0,0 +1,15 @@ +using OpenQA.Selenium; + +namespace AOW4.SeleniumTests.Pages; + +public class WeatherPage +{ + private readonly IWebDriver _driver; + public WeatherPage(IWebDriver driver) => _driver = driver; + + public bool IsAt() + { + var url = _driver.Url ?? string.Empty; + return url.Contains("weather", System.StringComparison.OrdinalIgnoreCase) || _driver.PageSource.Contains("Weather"); + } +} diff --git a/AOW4.SeleniumTests/README.md b/AOW4.SeleniumTests/README.md new file mode 100644 index 0000000..9ac5807 --- /dev/null +++ b/AOW4.SeleniumTests/README.md @@ -0,0 +1,20 @@ +# AOW4 Selenium Tests + +Requirements: +- .NET 10 SDK +- Google Chrome installed (compatible with ChromeDriver package) +- The AOW4 web app running locally (by default at `http://localhost:5000`) or set `BASE_URL` env var. + +Run tests: + +```powershell +# optional: run headless +$env:HEADLESS = "1" +# optional: point to running app +$env:BASE_URL = "http://localhost:5000" +dotnet test AOW4.SeleniumTests\AOW4.SeleniumTests.csproj +``` + +Notes: +- Navigation tests use the UI nav links — ensure the app is running before executing tests. +- Broken links scanner sends HTTP HEAD requests and falls back to GET if needed. diff --git a/AOW4.SeleniumTests/Tests/BaseTest.cs b/AOW4.SeleniumTests/Tests/BaseTest.cs new file mode 100644 index 0000000..5b9db07 --- /dev/null +++ b/AOW4.SeleniumTests/Tests/BaseTest.cs @@ -0,0 +1,36 @@ +using NUnit.Framework; +using OpenQA.Selenium; +using AOW4.SeleniumTests.Driver; + +namespace AOW4.SeleniumTests.Tests; + +[TestFixture] +public abstract class BaseTest +{ + protected IWebDriver Driver = null!; + protected string BaseUrl => System.Environment.GetEnvironmentVariable("BASE_URL") ?? "http://localhost:5000"; + + [OneTimeSetUp] + public void GlobalSetup() + { + Driver = DriverFactory.CreateChromeDriver(); + Driver.Manage().Window.Maximize(); + } + + [OneTimeTearDown] + public void GlobalTeardown() + { + try + { + Driver.Quit(); + } + catch + { + } + } + + protected void GoHome() + { + Driver.Navigate().GoToUrl(BaseUrl); + } +} diff --git a/AOW4.SeleniumTests/Tests/BrokenLinksTest.cs b/AOW4.SeleniumTests/Tests/BrokenLinksTest.cs new file mode 100644 index 0000000..87d57d3 --- /dev/null +++ b/AOW4.SeleniumTests/Tests/BrokenLinksTest.cs @@ -0,0 +1,74 @@ +using NUnit.Framework; +using System.Net.Http; +using System.Collections.Generic; +using System.Linq; +using OpenQA.Selenium; + +namespace AOW4.SeleniumTests.Tests; + +[TestFixture] +public class BrokenLinksTest : BaseTest +{ + [Test] + public void ScanAndReportBrokenLinks() + { + GoHome(); + + var anchors = Driver.FindElements(By.CssSelector("a[href]")) + .Select(a => a.GetAttribute("href") ?? string.Empty) + .Where(h => !string.IsNullOrWhiteSpace(h)) + .Distinct() + .ToList(); + + var failures = new List(); + using var client = new HttpClient(); + + foreach (var raw in anchors) + { + if (raw.StartsWith("javascript:", System.StringComparison.OrdinalIgnoreCase)) + continue; + if (raw.StartsWith("mailto:", System.StringComparison.OrdinalIgnoreCase)) + continue; + + System.Uri uri; + try + { + uri = new System.Uri(raw, System.UriKind.RelativeOrAbsolute); + if (!uri.IsAbsoluteUri) + { + uri = new System.Uri(new System.Uri(BaseUrl), raw); + } + } + catch + { + failures.Add($"Invalid URI: {raw}"); + continue; + } + + try + { + using var req = new HttpRequestMessage(HttpMethod.Head, uri); + var resp = client.Send(req); + if (!resp.IsSuccessStatusCode) + { + // try GET as fallback + using var greq = new HttpRequestMessage(HttpMethod.Get, uri); + var gresp = client.Send(greq); + if (!gresp.IsSuccessStatusCode) + { + failures.Add($"{(int)gresp.StatusCode} {uri}"); + } + } + } + catch (System.Exception ex) + { + failures.Add($"Error checking {uri}: {ex.Message}"); + } + } + + if (failures.Any()) + { + Assert.Fail("Broken links found:\n" + string.Join("\n", failures)); + } + } +} diff --git a/AOW4.SeleniumTests/Tests/NavigationTests.cs b/AOW4.SeleniumTests/Tests/NavigationTests.cs new file mode 100644 index 0000000..851eed1 --- /dev/null +++ b/AOW4.SeleniumTests/Tests/NavigationTests.cs @@ -0,0 +1,25 @@ +using NUnit.Framework; +using AOW4.SeleniumTests.Pages; + +namespace AOW4.SeleniumTests.Tests; + +[TestFixture] +public class NavigationTests : BaseTest +{ + [TestCase("Home", "/")] + [TestCase("Counter", "/counter")] + [TestCase("Weather", "/weather")] + public void ClickNavLink_NavigatesToPage(string linkText, string expectedPath) + { + GoHome(); + + var nav = new NavMenuPage(Driver); + nav.ClickLinkByText(linkText); + + // small wait for navigation + System.Threading.Thread.Sleep(700); + + Assert.IsTrue(Driver.Url.Contains(expectedPath, System.StringComparison.OrdinalIgnoreCase) || Driver.PageSource.Contains(linkText), + $"Expected to be on route containing '{expectedPath}' after clicking '{linkText}', but was '{Driver.Url}'"); + } +} diff --git a/AOW4.slnx b/AOW4.slnx index be47bad..6361106 100644 --- a/AOW4.slnx +++ b/AOW4.slnx @@ -1,4 +1,5 @@ +