sorcha-ui

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Sorcha UI Development Skill

Sorcha UI开发技能

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.
针对Sorcha.UI的测试驱动UI开发。每一处页面变更都配有可在Docker环境(
docker-compose up -d
)中运行的Playwright端到端(E2E)测试。测试会自动验证控制台错误、网络故障、MudBlazor CSS健康状态,并在失败时截取屏幕截图。

Prerequisites

前提条件

Docker must be running with all services:
bash
docker-compose up -d
Docker必须运行所有服务:
bash
docker-compose up -d

Login: admin@sorcha.local / Dev_Pass_2025!

Login: admin@sorcha.local / Dev_Pass_2025!

undefined
undefined

Workflow: Building a Page

工作流:构建页面

For every page you build or modify, follow these steps in order:
对于每个你构建或修改的页面,请按以下顺序执行步骤:

Step 1: Create the Page Object

步骤1:创建页面对象

Create
tests/Sorcha.UI.E2E.Tests/PageObjects/{PageName}Page.cs
:
csharp
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();
}
创建
tests/Sorcha.UI.E2E.Tests/PageObjects/{PageName}Page.cs
:
csharp
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)

步骤2:编写Playwright测试(测试优先)

Create
tests/Sorcha.UI.E2E.Tests/Docker/{Feature}Tests.cs
:
csharp
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));
        }
    }
}
创建
tests/Sorcha.UI.E2E.Tests/Docker/{Feature}Tests.cs
:
csharp
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

步骤3:构建Blazor页面

Edit
src/Apps/Sorcha.UI/Sorcha.UI.Web.Client/Pages/{Page}.razor
:
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>
编辑
src/Apps/Sorcha.UI/Sorcha.UI.Web.Client/Pages/{Page}.razor
:
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

步骤4:运行测试

bash
undefined
bash
undefined

Run tests for the feature you just built

运行你刚构建的功能对应的测试

dotnet test tests/Sorcha.UI.E2E.Tests --filter "Category=Wallets"
dotnet test tests/Sorcha.UI.E2E.Tests --filter "Category=Wallets"

Run all smoke tests (fast CI gate)

运行所有冒烟测试(快速CI验证门)

dotnet test tests/Sorcha.UI.E2E.Tests --filter "Category=Smoke"
dotnet test tests/Sorcha.UI.E2E.Tests --filter "Category=Smoke"

Run all Docker tests

运行所有Docker测试

dotnet test tests/Sorcha.UI.E2E.Tests --filter "Category=Docker"
undefined
dotnet test tests/Sorcha.UI.E2E.Tests --filter "Category=Docker"
undefined

Step 5: Check Artifacts on Failure

步骤5:失败时检查工件

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.
测试失败时,屏幕截图会保存到
tests/Sorcha.UI.E2E.Tests/bin/Debug/net10.0/screenshots/
目录。控制台错误和网络故障会在测试输出中报告。

Key Concepts

核心概念

ConceptUsageLocation
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 ObjectsEncapsulate 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 testsAdded to Razor components
CategoriesFilter tests by feature or concern
[Category("Wallets")]
on test classes
概念用途位置
DockerTestBase
未授权测试,自动捕获错误
Infrastructure/DockerTestBase.cs
AuthenticatedDockerTestBase
登录一次,在测试间复用认证状态
Infrastructure/AuthenticatedDockerTestBase.cs
TestConstants
URL、凭据、路由、超时时间
Infrastructure/TestConstants.cs
Page Objects封装每个页面的选择器和操作
PageObjects/*.cs
MudBlazorHelpers
共享的MudBlazor定位器和布局验证
PageObjects/Shared/MudBlazorHelpers.cs
data-testid
用于构建高稳定性测试的主要选择器策略添加到Razor组件中
Categories按功能或关注点过滤测试测试类上的
[Category(\"Wallets\")]

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)
PageTest (Playwright NUnit)
  └── DockerTestBase(控制台错误、网络故障、屏幕截图)
        ├── ComponentHealthTests、LoginTests(未授权)
        └── AuthenticatedDockerTestBase(登录一次、复用状态、布局健康检查)
              ├── DashboardTests
              ├── NavigationTests
              ├── WalletTests
              └── ...(每个功能区域对应一个)

Test Categories

测试分类

CategoryScopeUse
Smoke
All pages load, no JS errorsCI gate
Docker
All tests targeting DockerFull Docker suite
Auth
Login, logout, redirectsAuthentication features
Authenticated
All tests needing loginPost-login features
Dashboard
Dashboard pageDashboard development
Navigation
Drawer, app bar, routingLayout changes
Components
CSS, MudBlazor, responsiveStyle/component changes
Wallets
Wallet pagesWallet features
Blueprints
Blueprint pagesBlueprint features
Registers
Register pagesRegister features
Schemas
Schema librarySchema features
Admin
Administration pagesAdmin features
分类范围用途
Smoke
所有页面可加载,无JS错误CI验证门
Docker
所有针对Docker的测试完整Docker测试套件
Auth
登录、登出、重定向认证功能
Authenticated
所有需要登录的测试登录后功能
Dashboard
仪表板页面仪表板开发
Navigation
抽屉、应用栏、路由布局变更
Components
CSS、MudBlazor、响应式样式/组件变更
Wallets
钱包页面钱包功能
Blueprints
蓝图页面蓝图功能
Registers
注册页面注册功能
Schemas
模式库模式功能
Admin
管理页面管理功能

File Locations

文件位置

WhatWhere
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
内容位置
Blazor页面
src/Apps/Sorcha.UI/Sorcha.UI.Web.Client/Pages/
共享组件
src/Apps/Sorcha.UI/Sorcha.UI.Core/
布局
src/Apps/Sorcha.UI/Sorcha.UI.Web.Client/Components/Layout/
测试基础设施
tests/Sorcha.UI.E2E.Tests/Infrastructure/
页面对象
tests/Sorcha.UI.E2E.Tests/PageObjects/
Docker测试
tests/Sorcha.UI.E2E.Tests/Docker/
MudBlazor助手
tests/Sorcha.UI.E2E.Tests/PageObjects/Shared/MudBlazorHelpers.cs

See Also

另请参阅

  • patterns - Page implementation and test patterns
  • workflows - Development workflow and checklist
  • patterns - 页面实现和测试模式
  • workflows - 开发工作流和检查清单

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
  • 查看blazor技能了解Blazor WASM组件架构
  • 查看playwright技能了解Playwright API参考
  • 查看frontend-design技能了解MudBlazor样式
  • 查看minimal-apis技能了解页面调用的后端API端点
  • 查看jwt技能了解认证令牌处理

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"
使用Context7获取最新的Playwright .NET和MudBlazor文档。
库ID:
  • /websites/playwright_dev_dotnet
    (Playwright .NET)
  • /websites/mudblazor
    (MudBlazor组件库)
推荐查询:
  • "Locators selectors data-testid"
  • "NUnit test fixtures parallel"
  • "MudBlazor card table dialog"
  • "MudBlazor form validation"
  • "Browser storage state authentication"