LANCommander/LANCommander.Server.UI.Tests/Tests/AdminNavigationTests.cs
Pat Hartl 9b5195f1d6 Use web-first Playwright assertions in UI tests
Replace non-waiting Assert.True(await locator.IsVisibleAsync()) checks with
auto-retrying Assertions.Expect(locator).ToBeVisibleAsync()/ToBeHiddenAsync()
to remove flakiness against Blazor's async rendering. Table-row/field page
objects (Metadata/Roles/Users/Profile) now expose ILocator helpers instead of
Task<bool>, so deletion checks wait for the element to disappear.

Also: rename ScreenshotHelper.CaptureIfFailedAsync to CaptureAsync (xUnit v2
cannot expose the test outcome to DisposeAsync, so it always captures the final
page state), drop the unused TestConstants.ServerPort/BaseUrl now that ports
bind dynamically, and fix IClassFixture doc comments to ICollectionFixture.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-23 17:51:34 -05:00

157 lines
5.7 KiB
C#

using LANCommander.Server.UI.Tests.Pages;
using Microsoft.Playwright;
using Xunit.Abstractions;
namespace LANCommander.Server.UI.Tests.Tests;
/// <summary>
/// Tests for navigating around key parts of the admin application.
/// These tests verify that the main admin pages are accessible and render correctly
/// after logging in as an administrator.
/// Uses the shared "Server" collection fixture so the server starts once for the whole collection.
/// </summary>
[Collection("Server")]
public class AdminNavigationTests : IAsyncLifetime
{
private readonly ConfiguredServerFixture _fixture;
private readonly ITestOutputHelper _output;
private IBrowserContext _context = null!;
private IPage _page = null!;
public AdminNavigationTests(ConfiguredServerFixture fixture, ITestOutputHelper output)
{
_fixture = fixture;
_output = output;
}
public async Task InitializeAsync()
{
(_context, _page) = await _fixture.CreateLoggedInPageAsync();
}
public async Task DisposeAsync()
{
await ScreenshotHelper.CaptureAsync(_page, _output);
if (_page != null) await _page.CloseAsync();
if (_context != null) await _context.DisposeAsync();
}
[Fact]
public async Task Dashboard_ShowsOverviewWithCharts()
{
var dashboard = new AdminDashboardPage(_page);
Assert.True(await dashboard.IsDisplayedAsync());
// Dashboard should show playtime charts
await Assertions.Expect(_page.GetByText("Top 10 Total Playtime (By Player)")).ToBeVisibleAsync();
await Assertions.Expect(_page.GetByText("Top 10 Total Playtime (By Game)")).ToBeVisibleAsync();
await Assertions.Expect(_page.GetByText("Top Average Session Length (By Game)")).ToBeVisibleAsync();
}
[Fact]
public async Task Navigation_SidebarShowsExpectedMenuItems()
{
var dashboard = new AdminDashboardPage(_page);
var menuItems = await dashboard.GetMainMenuItemsAsync();
Assert.Contains(menuItems, m => m.Contains("Dashboards"));
Assert.Contains(menuItems, m => m.Contains("Games"));
Assert.Contains(menuItems, m => m.Contains("Redistributables"));
Assert.Contains(menuItems, m => m.Contains("Tools"));
Assert.Contains(menuItems, m => m.Contains("Servers"));
Assert.Contains(menuItems, m => m.Contains("Issues"));
Assert.Contains(menuItems, m => m.Contains("Files"));
Assert.Contains(menuItems, m => m.Contains("Settings"));
}
[Fact]
public async Task GamesPage_ShowsEmptyTable()
{
var dashboard = new AdminDashboardPage(_page);
await dashboard.NavigateToGamesAsync();
Assert.Contains("/Games", _page.Url);
await Assertions.Expect(_page.GetByText("Games").First).ToBeVisibleAsync();
await Assertions.Expect(_page.GetByRole(AriaRole.Button, new() { Name = "Add Game" })).ToBeVisibleAsync();
await Assertions.Expect(_page.GetByRole(AriaRole.Button, new() { Name = "Import" })).ToBeVisibleAsync();
// Empty table should show "No data"
await Assertions.Expect(_page.GetByText("No data")).ToBeVisibleAsync();
}
[Fact]
public async Task RedistributablesPage_IsAccessible()
{
var dashboard = new AdminDashboardPage(_page);
await dashboard.NavigateToRedistributablesAsync();
Assert.Contains("/Redistributables", _page.Url);
await Assertions.Expect(_page.GetByText("Redistributables").First).ToBeVisibleAsync();
}
[Fact]
public async Task ToolsPage_IsAccessible()
{
var dashboard = new AdminDashboardPage(_page);
await dashboard.NavigateToToolsAsync();
Assert.Contains("/Tools", _page.Url);
await Assertions.Expect(_page.GetByText("Tools").First).ToBeVisibleAsync();
}
[Fact]
public async Task ServersPage_IsAccessible()
{
var dashboard = new AdminDashboardPage(_page);
await dashboard.NavigateToServersAsync();
Assert.Contains("/Servers", _page.Url);
await Assertions.Expect(_page.GetByText("Servers").First).ToBeVisibleAsync();
}
[Fact]
public async Task IssuesPage_IsAccessible()
{
var dashboard = new AdminDashboardPage(_page);
await dashboard.NavigateToIssuesAsync();
Assert.Contains("/Issues", _page.Url);
}
[Fact]
public async Task FilesPage_IsAccessible()
{
var dashboard = new AdminDashboardPage(_page);
await dashboard.NavigateToFilesAsync();
Assert.Contains("/Files", _page.Url);
}
[Fact]
public async Task SettingsGeneralPage_IsAccessible()
{
var dashboard = new AdminDashboardPage(_page);
await dashboard.NavigateToSettingsGeneralAsync();
Assert.Contains("/Settings/General", _page.Url);
// Verify settings-specific content is visible (Database Provider is unique to General settings)
await Assertions.Expect(_page.GetByText("Database Provider")).ToBeVisibleAsync();
}
[Fact]
public async Task SettingsMenu_ShowsAllExpectedSubItems()
{
// Open the Settings submenu
await _page.GetByRole(AriaRole.Button, new() { Name = "Settings" }).ClickAsync();
// Verify key settings sub-items are visible
var expectedSettings = new[] {
"General", "Users", "Roles", "Authentication",
"Archives", "Media", "Logs", "Updates"
};
foreach (var setting in expectedSettings)
{
await Assertions.Expect(_page.GetByRole(AriaRole.Link, new() { Name = setting, Exact = true })).ToBeVisibleAsync();
}
}
}