LANCommander/LANCommander.Server.UI.Tests/Pages/FirstTimeSetupPage.cs
Aaron Powell 94cfd6fe18 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-03-08 08:25:44 +11:00

94 lines
3.5 KiB
C#

using Microsoft.Playwright;
namespace LANCommander.Server.UI.Tests.Pages;
/// <summary>
/// Page object for the First Time Setup wizard at /FirstTimeSetup.
/// </summary>
public class FirstTimeSetupPage
{
private readonly IPage _page;
public FirstTimeSetupPage(IPage page)
{
_page = page;
}
public async Task NavigateAsync()
{
await _page.GotoAsync("/FirstTimeSetup");
// Wait for Blazor to render the page
await _page.WaitForSelectorAsync("text=First Time Setup", new() { Timeout = 10000 });
}
public async Task<bool> IsDisplayedAsync()
{
return await _page.GetByText("First Time Setup").IsVisibleAsync();
}
// Step 1: Database
public async Task SelectDatabaseProviderAsync(string provider)
{
await _page.GetByRole(AriaRole.Combobox).ClickAsync();
await _page.GetByRole(AriaRole.Option, new() { Name = provider }).ClickAsync();
}
public async Task ClickConnectAsync()
{
await _page.GetByRole(AriaRole.Button, new() { Name = "Connect" }).ClickAsync();
}
public async Task CompleteDatabaseStepAsync(string provider = "SQLite")
{
await SelectDatabaseProviderAsync(provider);
await ClickConnectAsync();
// Wait for navigation to paths step
await _page.WaitForURLAsync("**/FirstTimeSetup/Paths", new() { Timeout = 30000 });
}
// Step 2: Paths
public async Task CompletePathsStepAsync()
{
await _page.GetByRole(AriaRole.Button, new() { Name = "Next" }).ClickAsync();
await _page.WaitForURLAsync("**/FirstTimeSetup/Metadata", new() { Timeout = 10000 });
}
// Step 3: Metadata
public async Task CompleteMetadataStepAsync()
{
await _page.GetByRole(AriaRole.Button, new() { Name = "Save" }).ClickAsync();
await _page.WaitForURLAsync("**/FirstTimeSetup/Administrator", new() { Timeout = 10000 });
}
// Step 4: Administrator
public async Task CreateAdministratorAsync(string username, string password)
{
// AntDesign doesn't use standard label/for associations, so use role-based selection
// Username is the first textbox on the Administrator step
await _page.WaitForSelectorAsync("text=To get started", new() { Timeout = 10000 });
await _page.GetByRole(AriaRole.Textbox).First.FillAsync(username);
await _page.Locator("input[name='context.Password']").FillAsync(password);
await _page.Locator("input[name='context.PasswordConfirm']").FillAsync(password);
await _page.GetByRole(AriaRole.Button, new() { Name = "Create" }).ClickAsync();
}
/// <summary>
/// Completes the entire first-time setup wizard from start to finish.
/// </summary>
public async Task CompleteFullSetupAsync(
string adminUsername = "admin",
string adminPassword = "Password1234!",
string databaseProvider = "SQLite")
{
await CompleteDatabaseStepAsync(databaseProvider);
await CompletePathsStepAsync();
await CompleteMetadataStepAsync();
await CreateAdministratorAsync(adminUsername, adminPassword);
// Wait for the success message
await _page.WaitForSelectorAsync("text=Setup completed", new() { Timeout = 15000 });
// Wait for redirect to login page (may already have happened)
await _page.WaitForSelectorAsync("text=User Name", new() { Timeout = 15000 });
}
}