diff --git a/AOW4.Client/AOW4.Client.csproj b/AOW4.Client/AOW4.Client.csproj index ab51c0e..91750a3 100644 --- a/AOW4.Client/AOW4.Client.csproj +++ b/AOW4.Client/AOW4.Client.csproj @@ -1,16 +1,16 @@ - - net10.0 - enable - enable - true - Default - true - + + net10.0 + enable + enable + true + Default + true + - - - + + + diff --git a/AOW4.Client/Pages/Counter.razor b/AOW4.Client/Pages/Counter.razor index 6b9e8cb..22e3468 100644 --- a/AOW4.Client/Pages/Counter.razor +++ b/AOW4.Client/Pages/Counter.razor @@ -10,10 +10,11 @@ @code { - private int currentCount = 0; + private int currentCount; private void IncrementCount() { currentCount++; } + } diff --git a/AOW4.Client/Program.cs b/AOW4.Client/Program.cs index 519269f..91db88a 100644 --- a/AOW4.Client/Program.cs +++ b/AOW4.Client/Program.cs @@ -2,4 +2,4 @@ using Microsoft.AspNetCore.Components.WebAssembly.Hosting; var builder = WebAssemblyHostBuilder.CreateDefault(args); -await builder.Build().RunAsync(); +await builder.Build().RunAsync(); \ No newline at end of file diff --git a/AOW4.SeleniumTests/AOW4.SeleniumTests.csproj b/AOW4.SeleniumTests/AOW4.SeleniumTests.csproj index ae70d08..47ceaaf 100644 --- a/AOW4.SeleniumTests/AOW4.SeleniumTests.csproj +++ b/AOW4.SeleniumTests/AOW4.SeleniumTests.csproj @@ -1,18 +1,18 @@ - - net10.0 - false - enable - enable - + + net10.0 + false + enable + enable + - - - - - - - + + + + + + + diff --git a/AOW4.SeleniumTests/Driver/DriverFactory.cs b/AOW4.SeleniumTests/Driver/DriverFactory.cs index aa2643a..fcc922e 100644 --- a/AOW4.SeleniumTests/Driver/DriverFactory.cs +++ b/AOW4.SeleniumTests/Driver/DriverFactory.cs @@ -1,4 +1,3 @@ -using System; using OpenQA.Selenium; using OpenQA.Selenium.Chrome; @@ -10,10 +9,9 @@ public static class DriverFactory { var options = new ChromeOptions(); var headless = Environment.GetEnvironmentVariable("HEADLESS"); - if (!string.IsNullOrEmpty(headless) && (headless == "1" || headless.Equals("true", StringComparison.OrdinalIgnoreCase))) - { + if (!string.IsNullOrEmpty(headless) && + (headless == "1" || headless.Equals("true", StringComparison.OrdinalIgnoreCase))) options.AddArgument("--headless=new"); - } options.AddArgument("--disable-gpu"); options.AddArgument("--no-sandbox"); @@ -24,4 +22,4 @@ public static class DriverFactory return new ChromeDriver(service, options); } -} +} \ No newline at end of file diff --git a/AOW4.SeleniumTests/Pages/CounterPage.cs b/AOW4.SeleniumTests/Pages/CounterPage.cs index 877e249..59159f8 100644 --- a/AOW4.SeleniumTests/Pages/CounterPage.cs +++ b/AOW4.SeleniumTests/Pages/CounterPage.cs @@ -5,11 +5,15 @@ namespace AOW4.SeleniumTests.Pages; public class CounterPage { private readonly IWebDriver _driver; - public CounterPage(IWebDriver driver) => _driver = 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"); + return url.Contains("counter", StringComparison.OrdinalIgnoreCase) || _driver.PageSource.Contains("Counter"); } -} +} \ No newline at end of file diff --git a/AOW4.SeleniumTests/Pages/HomePage.cs b/AOW4.SeleniumTests/Pages/HomePage.cs index 8f571d2..c6617d2 100644 --- a/AOW4.SeleniumTests/Pages/HomePage.cs +++ b/AOW4.SeleniumTests/Pages/HomePage.cs @@ -5,11 +5,16 @@ namespace AOW4.SeleniumTests.Pages; public class HomePage { private readonly IWebDriver _driver; - public HomePage(IWebDriver driver) => _driver = 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"); + return url.EndsWith("/") || url.Contains("/index", StringComparison.OrdinalIgnoreCase) || + _driver.PageSource.Contains("Home"); } -} +} \ No newline at end of file diff --git a/AOW4.SeleniumTests/Pages/NavMenuPage.cs b/AOW4.SeleniumTests/Pages/NavMenuPage.cs index 9b3a599..e27bdf2 100644 --- a/AOW4.SeleniumTests/Pages/NavMenuPage.cs +++ b/AOW4.SeleniumTests/Pages/NavMenuPage.cs @@ -1,4 +1,3 @@ -using System.Linq; using OpenQA.Selenium; namespace AOW4.SeleniumTests.Pages; @@ -15,13 +14,12 @@ public class NavMenuPage 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)); + .FirstOrDefault(e => + !string.IsNullOrWhiteSpace(e.Text) && + e.Text.Trim().Equals(linkText, StringComparison.OrdinalIgnoreCase)); - if (link == null) - { - throw new NoSuchElementException($"Link with text '{linkText}' not found in the page."); - } + if (link == null) throw new NoSuchElementException($"Link with text '{linkText}' not found in the page."); link.Click(); } -} +} \ No newline at end of file diff --git a/AOW4.SeleniumTests/Pages/WeatherPage.cs b/AOW4.SeleniumTests/Pages/WeatherPage.cs deleted file mode 100644 index e482da8..0000000 --- a/AOW4.SeleniumTests/Pages/WeatherPage.cs +++ /dev/null @@ -1,15 +0,0 @@ -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 index 9ac5807..9de8599 100644 --- a/AOW4.SeleniumTests/README.md +++ b/AOW4.SeleniumTests/README.md @@ -1,6 +1,7 @@ # 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. @@ -16,5 +17,6 @@ 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 index 1dd2e86..5f0443d 100644 --- a/AOW4.SeleniumTests/Tests/BaseTest.cs +++ b/AOW4.SeleniumTests/Tests/BaseTest.cs @@ -1,15 +1,12 @@ +using AOW4.SeleniumTests.Driver; 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 => "http://localhost:5212/"; - [OneTimeSetUp] public void GlobalSetup() { @@ -29,8 +26,11 @@ public abstract class BaseTest } } + protected IWebDriver Driver = null!; + protected string BaseUrl => "http://localhost:5212/"; + protected void GoHome() { Driver.Navigate().GoToUrl(BaseUrl); } -} +} \ No newline at end of file diff --git a/AOW4.SeleniumTests/Tests/BrokenLinksTest.cs b/AOW4.SeleniumTests/Tests/BrokenLinksTest.cs index 87d57d3..9db6177 100644 --- a/AOW4.SeleniumTests/Tests/BrokenLinksTest.cs +++ b/AOW4.SeleniumTests/Tests/BrokenLinksTest.cs @@ -1,7 +1,4 @@ using NUnit.Framework; -using System.Net.Http; -using System.Collections.Generic; -using System.Linq; using OpenQA.Selenium; namespace AOW4.SeleniumTests.Tests; @@ -25,19 +22,16 @@ public class BrokenLinksTest : BaseTest foreach (var raw in anchors) { - if (raw.StartsWith("javascript:", System.StringComparison.OrdinalIgnoreCase)) + if (raw.StartsWith("javascript:", StringComparison.OrdinalIgnoreCase)) continue; - if (raw.StartsWith("mailto:", System.StringComparison.OrdinalIgnoreCase)) + if (raw.StartsWith("mailto:", StringComparison.OrdinalIgnoreCase)) continue; - System.Uri uri; + Uri uri; try { - uri = new System.Uri(raw, System.UriKind.RelativeOrAbsolute); - if (!uri.IsAbsoluteUri) - { - uri = new System.Uri(new System.Uri(BaseUrl), raw); - } + uri = new Uri(raw, UriKind.RelativeOrAbsolute); + if (!uri.IsAbsoluteUri) uri = new Uri(new Uri(BaseUrl), raw); } catch { @@ -54,21 +48,15 @@ public class BrokenLinksTest : BaseTest // 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}"); - } + if (!gresp.IsSuccessStatusCode) failures.Add($"{(int)gresp.StatusCode} {uri}"); } } - catch (System.Exception ex) + catch (Exception ex) { failures.Add($"Error checking {uri}: {ex.Message}"); } } - if (failures.Any()) - { - Assert.Fail("Broken links found:\n" + string.Join("\n", failures)); - } + if (failures.Any()) Assert.Fail("Broken links found:\n" + string.Join("\n", failures)); } -} +} \ No newline at end of file diff --git a/AOW4.SeleniumTests/Tests/NavigationTests.cs b/AOW4.SeleniumTests/Tests/NavigationTests.cs index 01d4168..0dfc1e9 100644 --- a/AOW4.SeleniumTests/Tests/NavigationTests.cs +++ b/AOW4.SeleniumTests/Tests/NavigationTests.cs @@ -1,5 +1,4 @@ using NUnit.Framework; -using AOW4.SeleniumTests.Pages; namespace AOW4.SeleniumTests.Tests; @@ -11,7 +10,9 @@ public class NavigationTests : BaseTest { GoHome(); - Assert.IsTrue(Driver.Url.Contains(expectedPath, System.StringComparison.OrdinalIgnoreCase) || Driver.PageSource.Contains(linkText), + Assert.IsTrue( + Driver.Url.Contains(expectedPath, StringComparison.OrdinalIgnoreCase) || + Driver.PageSource.Contains(linkText), $"Expected to be on route containing '{expectedPath}' after clicking '{linkText}', but was '{Driver.Url}'"); } -} +} \ No newline at end of file diff --git a/AOW4.slnx b/AOW4.slnx index 6361106..2892bf2 100644 --- a/AOW4.slnx +++ b/AOW4.slnx @@ -1,5 +1,5 @@ - - - + + + diff --git a/AOW4/AOW4.csproj b/AOW4/AOW4.csproj index a6a657c..90888a0 100644 --- a/AOW4/AOW4.csproj +++ b/AOW4/AOW4.csproj @@ -1,15 +1,15 @@ - - net10.0 - enable - enable - true - + + net10.0 + enable + enable + true + - - - - + + + + diff --git a/AOW4/Components/App.razor b/AOW4/Components/App.razor index f6d3139..3085cd7 100644 --- a/AOW4/Components/App.razor +++ b/AOW4/Components/App.razor @@ -2,21 +2,21 @@ - - - - - - - - - - + + + + + + + + + + - - + + diff --git a/AOW4/Components/Layout/MainLayout.razor.css b/AOW4/Components/Layout/MainLayout.razor.css index 38d1f25..0847203 100644 --- a/AOW4/Components/Layout/MainLayout.razor.css +++ b/AOW4/Components/Layout/MainLayout.razor.css @@ -21,20 +21,20 @@ main { align-items: center; } - .top-row ::deep a, .top-row ::deep .btn-link { - white-space: nowrap; - margin-left: 1.5rem; - text-decoration: none; - } +.top-row ::deep a, .top-row ::deep .btn-link { + white-space: nowrap; + margin-left: 1.5rem; + text-decoration: none; +} - .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { - text-decoration: underline; - } +.top-row ::deep a:hover, .top-row ::deep .btn-link:hover { + text-decoration: underline; +} - .top-row ::deep a:first-child { - overflow: hidden; - text-overflow: ellipsis; - } +.top-row ::deep a:first-child { + overflow: hidden; + text-overflow: ellipsis; +} @media (max-width: 640.98px) { .top-row { @@ -90,9 +90,9 @@ main { z-index: 1000; } - #blazor-error-ui .dismiss { - cursor: pointer; - position: absolute; - right: 0.75rem; - top: 0.5rem; - } +#blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; +} diff --git a/AOW4/Components/Layout/NavMenu.razor b/AOW4/Components/Layout/NavMenu.razor deleted file mode 100644 index b2c4ea8..0000000 --- a/AOW4/Components/Layout/NavMenu.razor +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/AOW4/Components/Layout/NavMenu.razor.css b/AOW4/Components/Layout/NavMenu.razor.css deleted file mode 100644 index a2aeace..0000000 --- a/AOW4/Components/Layout/NavMenu.razor.css +++ /dev/null @@ -1,105 +0,0 @@ -.navbar-toggler { - appearance: none; - cursor: pointer; - width: 3.5rem; - height: 2.5rem; - color: white; - position: absolute; - top: 0.5rem; - right: 1rem; - border: 1px solid rgba(255, 255, 255, 0.1); - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1); -} - -.navbar-toggler:checked { - background-color: rgba(255, 255, 255, 0.5); -} - -.top-row { - min-height: 3.5rem; - background-color: rgba(0,0,0,0.4); -} - -.navbar-brand { - font-size: 1.1rem; -} - -.bi { - display: inline-block; - position: relative; - width: 1.25rem; - height: 1.25rem; - margin-right: 0.75rem; - top: -1px; - background-size: cover; -} - -.bi-house-door-fill-nav-menu { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E"); -} - -.bi-plus-square-fill-nav-menu { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E"); -} - -.bi-list-nested-nav-menu { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E"); -} - -.nav-item { - font-size: 0.9rem; - padding-bottom: 0.5rem; -} - - .nav-item:first-of-type { - padding-top: 1rem; - } - - .nav-item:last-of-type { - padding-bottom: 1rem; - } - - .nav-item ::deep .nav-link { - color: #d7d7d7; - background: none; - border: none; - border-radius: 4px; - height: 3rem; - display: flex; - align-items: center; - line-height: 3rem; - width: 100%; - } - -.nav-item ::deep a.active { - background-color: rgba(255,255,255,0.37); - color: white; -} - -.nav-item ::deep .nav-link:hover { - background-color: rgba(255,255,255,0.1); - color: white; -} - -.nav-scrollable { - display: none; -} - -.navbar-toggler:checked ~ .nav-scrollable { - display: block; -} - -@media (min-width: 641px) { - .navbar-toggler { - display: none; - } - - .nav-scrollable { - /* Never collapse the sidebar for wide screens */ - display: block; - - /* Allow sidebar to scroll for tall menus */ - height: calc(100vh - 3.5rem); - overflow-y: auto; - } -} diff --git a/AOW4/Components/Pages/BuildingCalculator.razor b/AOW4/Components/Pages/BuildingCalculator.razor index 0e8536e..38cdfca 100644 --- a/AOW4/Components/Pages/BuildingCalculator.razor +++ b/AOW4/Components/Pages/BuildingCalculator.razor @@ -10,23 +10,23 @@

Build Order

- - - - - - + + + + + + - @foreach (var entry in Result.BuildOrder) - { - - - - - - - } + @foreach (var entry in Result.BuildOrder) + { + + + + + + + }
Turn RequestedBuildingFinish TurnIndustry Remaining
Turn RequestedBuildingFinish TurnIndustry Remaining
@entry.RequestedTurn@entry.Name@(entry.BuiltFinishTurn == 0 ? "Starting" : entry.BuiltFinishTurn.ToString())@entry.IndustryCostRemaining
@entry.RequestedTurn@entry.Name@(entry.BuiltFinishTurn == 0 ? "Starting" : entry.BuiltFinishTurn.ToString())@entry.IndustryCostRemaining
@@ -35,23 +35,23 @@

Gold Over Time

- - - - - - + + + + + + - @foreach (var snapshot in Result.ResourceHistory) - { - - - - - - - } + @foreach (var snapshot in Result.ResourceHistory) + { + + + + + + + }
TurnStored GoldIncomeUpkeep
TurnStored GoldIncomeUpkeep
@snapshot.Turn@snapshot.Stored.Gold@snapshot.TotalIncome.Gold@snapshot.TotalUpkeep.Gold
@snapshot.Turn@snapshot.Stored.Gold@snapshot.TotalIncome.Gold@snapshot.TotalUpkeep.Gold
@@ -68,12 +68,13 @@ protected override void OnInitialized() { - Result = BuildingPlanCalculator.CreateSampleBuildPlan(60); + Result = BuildingPlanCalculator.CreateSampleBuildPlan(); Json = JsonSerializer.Serialize(Result, new JsonSerializerOptions { WriteIndented = true }); } + }