sorcha-ui
Sorcha UI Development Skill
Test-driven UI development for Sorcha.UI. Every page change is paired with Playwright E2E tests that run against the Docker environment (docker-compose up -d). Tests automatically validate console errors, network failures, MudBlazor CSS health, and take screenshots on failure.
Prerequisites
Docker must be running with all services:
docker-compose up -d
# UI at http://localhost:5400 | API Gateway at http://localhost:80
# Login: admin@sorcha.local / Dev_Pass_2025!
Workflow: Building a Page
For every page you build or modify, follow these steps in order:
Step 1: Create the Page Object
Create tests/Sorcha.UI.E2E.Tests/PageObjects/{PageName}Page.cs:
using Microsoft.Playwright;
using Sorcha.UI.E2E.Tests.Infrastructure;
using Sorcha.UI.E2E.Tests.PageObjects.Shared;
namespace Sorcha.UI.E2E.Tests.PageObjects;
public class WalletListPage
{
private readonly IPage _page;
public WalletListPage(IPage page) => _page = page;
// Use data-testid as primary selector strategy
public ILocator WalletCards => MudBlazorHelpers.TestIdPrefix(_page, "wallet-card-");
public ILocator CreateButton => MudBlazorHelpers.TestId(_page, "create-wallet-btn");
public ILocator EmptyState => MudBlazorHelpers.TestId(_page, "wallet-empty-state");
public ILocator SearchInput => _page.Locator("input[placeholder*='Search']");
// Fallback to MudBlazor class selectors
public ILocator Table => MudBlazorHelpers.Table(_page);
public ILocator LoadingSpinner => MudBlazorHelpers.CircularProgress(_page);
public async Task NavigateAsync()
{
await _page.GotoAsync($"{TestConstants.UiWebUrl}{TestConstants.AuthenticatedRoutes.Wallets}");
await _page.WaitForLoadStateAsync(LoadState.NetworkIdle);
await MudBlazorHelpers.WaitForBlazorAsync(_page);
}
public async Task<int> GetWalletCountAsync() => await WalletCards.CountAsync();
public async Task<bool> IsEmptyStateVisibleAsync() =>
await EmptyState.CountAsync() > 0 && await EmptyState.IsVisibleAsync();
}
Step 2: Write the Playwright Tests (Test-First)
Create tests/Sorcha.UI.E2E.Tests/Docker/{Feature}Tests.cs:
using Sorcha.UI.E2E.Tests.Infrastructure;
using Sorcha.UI.E2E.Tests.PageObjects;
namespace Sorcha.UI.E2E.Tests.Docker;
[Parallelizable(ParallelScope.Self)]
[TestFixture]
[Category("Docker")]
[Category("Wallets")]
[Category("Authenticated")]
public class WalletListTests : AuthenticatedDockerTestBase
{
private WalletListPage _walletList = null!;
[SetUp]
public override async Task BaseSetUp()
{
await base.BaseSetUp();
_walletList = new WalletListPage(Page);
}
[Test]
[Retry(2)]
public async Task WalletList_LoadsWithoutErrors()
{
await NavigateAuthenticatedAsync(TestConstants.AuthenticatedRoutes.Wallets);
// Base class automatically checks console errors, network 5xx, CSS health
}
[Test]
public async Task WalletList_ShowsEmptyStateOrWallets()
{
await _walletList.NavigateAsync();
var count = await _walletList.GetWalletCountAsync();
if (count == 0)
{
Assert.That(await _walletList.IsEmptyStateVisibleAsync(), Is.True,
"Empty system should show empty state message");
}
else
{
Assert.That(count, Is.GreaterThan(0));
}
}
}
Step 3: Build the Blazor Page
Edit src/Apps/Sorcha.UI/Sorcha.UI.Web.Client/Pages/{Page}.razor:
@page "/wallets"
@layout MainLayout
@rendermode @(new InteractiveWebAssemblyRenderMode(prerender: false))
@attribute [Authorize]
@inject HttpClient Http
<PageTitle>Wallets - Sorcha</PageTitle>
@if (_isLoading)
{
<MudProgressCircular Indeterminate="true" data-testid="wallet-loading" />
}
else if (_wallets.Count == 0)
{
<MudAlert Severity="Severity.Info" data-testid="wallet-empty-state">
No wallets found. Create your first wallet to get started.
</MudAlert>
}
else
{
@foreach (var wallet in _wallets)
{
<MudCard data-testid="wallet-card-@wallet.Id" Class="mb-3">
<MudCardContent>
<MudText Typo="Typo.h6">@wallet.Name</MudText>
</MudCardContent>
</MudCard>
}
}
<MudButton data-testid="create-wallet-btn" Variant="Variant.Filled"
Color="Color.Primary" Href="wallets/create">
Create Wallet
</MudButton>
Step 4: Run Tests
# Run tests for the feature you just built
dotnet test tests/Sorcha.UI.E2E.Tests --filter "Category=Wallets"
# Run all smoke tests (fast CI gate)
dotnet test tests/Sorcha.UI.E2E.Tests --filter "Category=Smoke"
# Run all Docker tests
dotnet test tests/Sorcha.UI.E2E.Tests --filter "Category=Docker"
Step 5: Check Artifacts on Failure
Screenshots are saved to tests/Sorcha.UI.E2E.Tests/bin/Debug/net10.0/screenshots/ on any test failure. Console errors and network failures are reported in the test output.
Key Concepts
| Concept | Usage | Location |
|---|---|---|
DockerTestBase |
Unauthenticated tests with auto error capture | Infrastructure/DockerTestBase.cs |
AuthenticatedDockerTestBase |
Login once, reuse auth state across tests | Infrastructure/AuthenticatedDockerTestBase.cs |
TestConstants |
URLs, credentials, routes, timeouts | Infrastructure/TestConstants.cs |
| Page Objects | Encapsulate selectors and actions per page | PageObjects/*.cs |
MudBlazorHelpers |
Shared MudBlazor locators and layout validation | PageObjects/Shared/MudBlazorHelpers.cs |
data-testid |
Primary selector strategy for resilient tests | Added to Razor components |
| Categories | Filter tests by feature or concern | [Category("Wallets")] on test classes |
Test Inheritance
PageTest (Playwright NUnit)
└── DockerTestBase (console errors, network failures, screenshots)
├── ComponentHealthTests, LoginTests (unauthenticated)
└── AuthenticatedDockerTestBase (login once, reuse state, layout health)
├── DashboardTests
├── NavigationTests
├── WalletTests
└── ... (one per feature area)
Test Categories
| Category | Scope | Use |
|---|---|---|
Smoke |
All pages load, no JS errors | CI gate |
Docker |
All tests targeting Docker | Full Docker suite |
Auth |
Login, logout, redirects | Authentication features |
Authenticated |
All tests needing login | Post-login features |
Dashboard |
Dashboard page | Dashboard development |
Navigation |
Drawer, app bar, routing | Layout changes |
Components |
CSS, MudBlazor, responsive | Style/component changes |
Wallets |
Wallet pages | Wallet features |
Blueprints |
Blueprint pages | Blueprint features |
Registers |
Register pages | Register features |
Schemas |
Schema library | Schema features |
Admin |
Administration pages | Admin features |
File Locations
| What | Where |
|---|---|
| Blazor pages | src/Apps/Sorcha.UI/Sorcha.UI.Web.Client/Pages/ |
| Shared components | src/Apps/Sorcha.UI/Sorcha.UI.Core/ |
| Layout | src/Apps/Sorcha.UI/Sorcha.UI.Web.Client/Components/Layout/ |
| Test infrastructure | tests/Sorcha.UI.E2E.Tests/Infrastructure/ |
| Page objects | tests/Sorcha.UI.E2E.Tests/PageObjects/ |
| Docker tests | tests/Sorcha.UI.E2E.Tests/Docker/ |
| MudBlazor helpers | tests/Sorcha.UI.E2E.Tests/PageObjects/Shared/MudBlazorHelpers.cs |
See Also
Related Skills
- See the blazor skill for Blazor WASM component architecture
- See the playwright skill for Playwright API reference
- See the frontend-design skill for MudBlazor styling
- See the minimal-apis skill for backend API endpoints the pages call
- See the jwt skill for authentication token handling
Documentation Resources
Fetch latest Playwright .NET and MudBlazor documentation with Context7.
Library IDs:
/websites/playwright_dev_dotnet(Playwright .NET)/websites/mudblazor(MudBlazor component library)
Recommended Queries:
- "Locators selectors data-testid"
- "NUnit test fixtures parallel"
- "MudBlazor card table dialog"
- "MudBlazor form validation"
- "Browser storage state authentication"