Spike: Playwright UI automated tests for server application
Add LANCommander.Server.UI.Tests project with 19 Playwright-based UI tests
covering three key scenarios:
- First-time setup flow (4 tests): Fresh server redirect, setup wizard steps,
database provider selection, and complete wizard-to-login flow
- Login flow (5 tests): Unauthenticated redirect, page elements, valid/invalid
credentials, and empty credential handling
- Admin navigation (10 tests): Dashboard, sidebar menu, Games, Redistributables,
Tools, Servers, Issues, Files, Settings pages, and Settings submenu items
Infrastructure includes:
- ServerManager: Manages server process lifecycle with data directory backup/restore
- PlaywrightFixture: Browser lifecycle management (headless Chromium)
- ConfiguredServerFixture: Shared xUnit class fixture for tests needing a
configured server (avoids restarting server per test)
- Page objects: LoginPage, FirstTimeSetupPage, AdminDashboardPage
Key technical decisions:
- Uses IClassFixture<T> pattern to share server instances across test classes
- Disables parallel execution (tests share port 1337)
- Uses element-based waits instead of URL-based waits for Blazor SSR reliability
- Uses role-based selectors for AntDesign components (no standard label/for)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-28 14:16:01 +11:00
|
|
|
using Microsoft.Playwright;
|
|
|
|
|
|
|
|
|
|
namespace LANCommander.Server.UI.Tests.Pages;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Page object for the admin dashboard and navigation.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class AdminDashboardPage
|
|
|
|
|
{
|
|
|
|
|
private readonly IPage _page;
|
|
|
|
|
|
|
|
|
|
public AdminDashboardPage(IPage page)
|
|
|
|
|
{
|
|
|
|
|
_page = page;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<bool> IsDisplayedAsync()
|
|
|
|
|
{
|
|
|
|
|
return await _page.GetByText("Dashboard").First.IsVisibleAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<string> GetPageTitleAsync()
|
|
|
|
|
{
|
|
|
|
|
return await _page.TitleAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Navigation helpers
|
|
|
|
|
public async Task NavigateToGamesAsync()
|
|
|
|
|
{
|
|
|
|
|
await _page.GetByRole(AriaRole.Link, new() { Name = "Games" }).ClickAsync();
|
|
|
|
|
await _page.WaitForURLAsync("**/Games", new() { Timeout = 10000 });
|
|
|
|
|
// Wait for Blazor to render the page content
|
|
|
|
|
await _page.WaitForSelectorAsync("text=Add Game", new() { Timeout = 10000 });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task NavigateToSettingsGeneralAsync()
|
|
|
|
|
{
|
2026-06-24 23:14:42 -05:00
|
|
|
// Navigate directly to the route rather than expanding the Settings SubMenu flyout.
|
|
|
|
|
// The nested submenu expansion depends on the Blazor Server circuit being responsive
|
|
|
|
|
// and is unreliable in CI; a direct navigation still exercises routing + auth.
|
|
|
|
|
await _page.GotoAsync("/Settings/General");
|
Spike: Playwright UI automated tests for server application
Add LANCommander.Server.UI.Tests project with 19 Playwright-based UI tests
covering three key scenarios:
- First-time setup flow (4 tests): Fresh server redirect, setup wizard steps,
database provider selection, and complete wizard-to-login flow
- Login flow (5 tests): Unauthenticated redirect, page elements, valid/invalid
credentials, and empty credential handling
- Admin navigation (10 tests): Dashboard, sidebar menu, Games, Redistributables,
Tools, Servers, Issues, Files, Settings pages, and Settings submenu items
Infrastructure includes:
- ServerManager: Manages server process lifecycle with data directory backup/restore
- PlaywrightFixture: Browser lifecycle management (headless Chromium)
- ConfiguredServerFixture: Shared xUnit class fixture for tests needing a
configured server (avoids restarting server per test)
- Page objects: LoginPage, FirstTimeSetupPage, AdminDashboardPage
Key technical decisions:
- Uses IClassFixture<T> pattern to share server instances across test classes
- Disables parallel execution (tests share port 1337)
- Uses element-based waits instead of URL-based waits for Blazor SSR reliability
- Uses role-based selectors for AntDesign components (no standard label/for)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-28 14:16:01 +11:00
|
|
|
await _page.WaitForURLAsync("**/Settings/General", new() { Timeout = 10000 });
|
|
|
|
|
// Wait for Blazor to render the settings content
|
2026-06-23 22:11:05 -05:00
|
|
|
await _page.WaitForSelectorAsync("text=Use SSL", new() { Timeout = 10000 });
|
Spike: Playwright UI automated tests for server application
Add LANCommander.Server.UI.Tests project with 19 Playwright-based UI tests
covering three key scenarios:
- First-time setup flow (4 tests): Fresh server redirect, setup wizard steps,
database provider selection, and complete wizard-to-login flow
- Login flow (5 tests): Unauthenticated redirect, page elements, valid/invalid
credentials, and empty credential handling
- Admin navigation (10 tests): Dashboard, sidebar menu, Games, Redistributables,
Tools, Servers, Issues, Files, Settings pages, and Settings submenu items
Infrastructure includes:
- ServerManager: Manages server process lifecycle with data directory backup/restore
- PlaywrightFixture: Browser lifecycle management (headless Chromium)
- ConfiguredServerFixture: Shared xUnit class fixture for tests needing a
configured server (avoids restarting server per test)
- Page objects: LoginPage, FirstTimeSetupPage, AdminDashboardPage
Key technical decisions:
- Uses IClassFixture<T> pattern to share server instances across test classes
- Disables parallel execution (tests share port 1337)
- Uses element-based waits instead of URL-based waits for Blazor SSR reliability
- Uses role-based selectors for AntDesign components (no standard label/for)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-28 14:16:01 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task NavigateToRedistributablesAsync()
|
|
|
|
|
{
|
|
|
|
|
await _page.GetByRole(AriaRole.Link, new() { Name = "Redistributables" }).ClickAsync();
|
|
|
|
|
await _page.WaitForURLAsync("**/Redistributables", new() { Timeout = 10000 });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task NavigateToServersAsync()
|
|
|
|
|
{
|
|
|
|
|
await _page.GetByRole(AriaRole.Link, new() { Name = "Servers", Exact = true }).ClickAsync();
|
|
|
|
|
await _page.WaitForURLAsync("**/Servers", new() { Timeout = 10000 });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task NavigateToIssuesAsync()
|
|
|
|
|
{
|
|
|
|
|
await _page.GetByRole(AriaRole.Link, new() { Name = "Issues" }).ClickAsync();
|
|
|
|
|
await _page.WaitForURLAsync("**/Issues", new() { Timeout = 10000 });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task NavigateToFilesAsync()
|
|
|
|
|
{
|
|
|
|
|
await _page.GetByRole(AriaRole.Link, new() { Name = "Files" }).ClickAsync();
|
|
|
|
|
await _page.WaitForURLAsync("**/Files", new() { Timeout = 10000 });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task NavigateToToolsAsync()
|
|
|
|
|
{
|
|
|
|
|
await _page.GetByRole(AriaRole.Link, new() { Name = "Tools", Exact = true }).ClickAsync();
|
|
|
|
|
await _page.WaitForURLAsync("**/Tools", new() { Timeout = 10000 });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the visible menu items from the sidebar navigation.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public async Task<IReadOnlyList<string>> GetMainMenuItemsAsync()
|
|
|
|
|
{
|
|
|
|
|
// Wait for sidebar menu items to render
|
|
|
|
|
await _page.GetByRole(AriaRole.Complementary)
|
|
|
|
|
.Locator("[role='menuitem']")
|
|
|
|
|
.First
|
|
|
|
|
.WaitForAsync(new() { Timeout = 10000 });
|
|
|
|
|
|
|
|
|
|
var menuItems = _page.GetByRole(AriaRole.Complementary).Locator("[role='menuitem']");
|
|
|
|
|
var count = await menuItems.CountAsync();
|
|
|
|
|
var items = new List<string>();
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
var text = await menuItems.Nth(i).TextContentAsync();
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(text))
|
|
|
|
|
items.Add(text.Trim());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return items;
|
|
|
|
|
}
|
|
|
|
|
}
|