sorcha-ui
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSorcha 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 (). Tests automatically validate console errors, network failures, MudBlazor CSS health, and take screenshots on failure.
docker-compose up -d针对Sorcha.UI的测试驱动UI开发。每一处页面变更都配有可在Docker环境()中运行的Playwright端到端(E2E)测试。测试会自动验证控制台错误、网络故障、MudBlazor CSS健康状态,并在失败时截取屏幕截图。
docker-compose up -dPrerequisites
前提条件
Docker must be running with all services:
bash
docker-compose up -dDocker必须运行所有服务:
bash
docker-compose up -dUI at http://localhost:5400 | API Gateway at http://localhost:80
UI at http://localhost:5400 | API Gateway at http://localhost:80
Login: admin@sorcha.local / Dev_Pass_2025!
Login: admin@sorcha.local / Dev_Pass_2025!
undefinedundefinedWorkflow: 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.cscsharp
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.cscsharp
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.cscsharp
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.cscsharp
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}.razorrazor
@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}.razorrazor
@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
undefinedbash
undefinedRun 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"
undefineddotnet test tests/Sorcha.UI.E2E.Tests --filter "Category=Docker"
undefinedStep 5: Check Artifacts on Failure
步骤5:失败时检查工件
Screenshots are saved to 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/测试失败时,屏幕截图会保存到目录。控制台错误和网络故障会在测试输出中报告。
tests/Sorcha.UI.E2E.Tests/bin/Debug/net10.0/screenshots/Key Concepts
核心概念
| Concept | Usage | Location |
|---|---|---|
| Unauthenticated tests with auto error capture | |
| Login once, reuse auth state across tests | |
| URLs, credentials, routes, timeouts | |
| Page Objects | Encapsulate selectors and actions per page | |
| Shared MudBlazor locators and layout validation | |
| Primary selector strategy for resilient tests | Added to Razor components |
| Categories | Filter tests by feature or concern | |
| 概念 | 用途 | 位置 |
|---|---|---|
| 未授权测试,自动捕获错误 | |
| 登录一次,在测试间复用认证状态 | |
| URL、凭据、路由、超时时间 | |
| Page Objects | 封装每个页面的选择器和操作 | |
| 共享的MudBlazor定位器和布局验证 | |
| 用于构建高稳定性测试的主要选择器策略 | 添加到Razor组件中 |
| Categories | 按功能或关注点过滤测试 | 测试类上的 |
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
测试分类
| Category | Scope | Use |
|---|---|---|
| All pages load, no JS errors | CI gate |
| All tests targeting Docker | Full Docker suite |
| Login, logout, redirects | Authentication features |
| All tests needing login | Post-login features |
| Dashboard page | Dashboard development |
| Drawer, app bar, routing | Layout changes |
| CSS, MudBlazor, responsive | Style/component changes |
| Wallet pages | Wallet features |
| Blueprint pages | Blueprint features |
| Register pages | Register features |
| Schema library | Schema features |
| Administration pages | Admin features |
| 分类 | 范围 | 用途 |
|---|---|---|
| 所有页面可加载,无JS错误 | CI验证门 |
| 所有针对Docker的测试 | 完整Docker测试套件 |
| 登录、登出、重定向 | 认证功能 |
| 所有需要登录的测试 | 登录后功能 |
| 仪表板页面 | 仪表板开发 |
| 抽屉、应用栏、路由 | 布局变更 |
| CSS、MudBlazor、响应式 | 样式/组件变更 |
| 钱包页面 | 钱包功能 |
| 蓝图页面 | 蓝图功能 |
| 注册页面 | 注册功能 |
| 模式库 | 模式功能 |
| 管理页面 | 管理功能 |
File Locations
文件位置
| What | Where |
|---|---|
| Blazor pages | |
| Shared components | |
| Layout | |
| Test infrastructure | |
| Page objects | |
| Docker tests | |
| MudBlazor helpers | |
| 内容 | 位置 |
|---|---|
| Blazor页面 | |
| 共享组件 | |
| 布局 | |
| 测试基础设施 | |
| 页面对象 | |
| Docker测试 | |
| MudBlazor助手 | |
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:
- (Playwright .NET)
/websites/playwright_dev_dotnet - (MudBlazor component library)
/websites/mudblazor
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:
- (Playwright .NET)
/websites/playwright_dev_dotnet - (MudBlazor组件库)
/websites/mudblazor
推荐查询:
- "Locators selectors data-testid"
- "NUnit test fixtures parallel"
- "MudBlazor card table dialog"
- "MudBlazor form validation"
- "Browser storage state authentication"