with one click
webapp-testing
// MyBlog guidance for running-browser verification of the Blazor UI after bUnit coverage exists, especially for JS interop, Auth0 redirects, and AppHost smoke checks.
// MyBlog guidance for running-browser verification of the Blazor UI after bUnit coverage exists, especially for JS interop, Auth0 redirects, and AppHost smoke checks.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | webapp-testing |
| description | MyBlog guidance for running-browser verification of the Blazor UI after bUnit coverage exists, especially for JS interop, Auth0 redirects, and AppHost smoke checks. |
Automated UI coverage lives in tests/Unit.Tests with bUnit:
tests/Unit.Tests/Components/Layout/NavMenuTests.cs — Navigation menu auth states, theme toggle, JS interoptests/Unit.Tests/Components/RazorSmokeTests.cs — Component rendering smoke teststests/Unit.Tests/Features/UserManagement/ProfileTests.cs — Profile component claim assertionsBunitContext (base class from bUnit; test-specific helpers in TestAuthorizationService.cs)No Playwright or dedicated browser-test project exists today.
Browser-level checks are useful for runtime-only behavior that bUnit cannot verify:
/Account/Login and /Account/Logoutsrc/Web/wwwroot/js/theme.js persistence (local storage, CSS class toggling across page reloads)bUnit first, browser second
All new UI features must have bUnit test coverage in tests/Unit.Tests FIRST.
Use browser automation ONLY when:
Example: NavMenuTests.cs covers auth states (anonymous, admin, author) via
IAuthorizationService mocking in bUnit. No browser needed for those flows.
bUnit test structure in MyBlog
BunitContext (bUnit's TestContext aliased via global using Bunit in GlobalUsings.cs)TestAuthorizationService helper (in tests/Unit.Tests/Testing/) mocks auth for testingRenderForUser(ClaimsPrincipal) and CreatePrincipal() to set up authenticated render contextscut.Markup.Should() to verify rendered HTML outputReal example from NavMenuTests.cs:
public class NavMenuTests : BunitContext
{
public NavMenuTests()
{
Services.AddAuthorizationCore();
Services.AddSingleton<IAuthorizationService, TestAuthorizationService>();
}
[Fact]
public void UnauthenticatedUser_SeesLoginAndNoProtectedLinks()
{
// Arrange (none)
// Act
var cut = RenderForUser(CreatePrincipal(authenticated: false));
// Assert
cut.Markup.Should().Contain("Login");
cut.Markup.Should().NotContain("Logout");
cut.Markup.Should().NotContain("Manage Users");
}
[Fact]
public void AuthenticatedAdmin_UsesDisplayNameAsProfileLabel_AndShowsAdminLinks()
{
// Arrange (none)
// Act
var cut = RenderForUser(CreatePrincipal(name: "Admin User", roles: ["Admin"]));
// Assert
cut.Markup.Should().Contain("Admin User");
cut.Markup.Should().Contain("Manage Users");
cut.Markup.Should().Contain("New Post");
}
}
Note: RenderForUser and CreatePrincipal are helper methods defined in the test class itself (not in a separate base class). They wrap bUnit's RenderComponent<T>() with auth context setup.
Run the app the same way contributors do
src/AppHost for infrastructure-dependent checks (MongoDB, Redis).src/Web only for Web-only, infrastructure-agnostic features.Treat browser artifacts as debugging aids
Escalate repeated runtime gaps into real backlog work
Theme color and brightness persistence — Verify theme.js behavior:
Auth redirects and session state — Verify end-to-end auth flows:
/profile or homeSmoke check authenticated vs. anonymous navigation — After auth/claim changes:
Confirm full AppHost wiring — Before deploying major infrastructure changes:
Scenario: NavMenu theme toggle button is not updating the CSS class in production.
Steps:
NavMenu_TogglesThemeClass_WhenButtonClicked()Next steps:
theme.js actually being loaded?bUnit Component Test (tests/Unit.Tests/Components/Layout/NavMenuTests.cs):
public class NavMenuTests : BunitContext
{
public NavMenuTests()
{
Services.AddAuthorizationCore();
Services.AddSingleton<IAuthorizationService, TestAuthorizationService>();
}
[Fact]
public void UnauthenticatedUser_SeesLoginAndNoProtectedLinks()
{
// Arrange (none)
// Act
var cut = RenderForUser(CreatePrincipal(authenticated: false));
// Assert
cut.Markup.Should().Contain("Login");
cut.Markup.Should().NotContain("Logout");
cut.Markup.Should().NotContain("Manage Users");
cut.Markup.Should().NotContain("New Post");
}
[Fact]
public void AuthenticatedAdmin_UsesDisplayNameAsProfileLabel_AndShowsAdminLinks()
{
// Arrange (none)
// Act
var cut = RenderForUser(CreatePrincipal(name: "Admin User", roles: ["Admin"]));
// Assert
cut.Markup.Should().Contain("Admin User");
cut.Markup.Should().Contain("Manage Users");
cut.Markup.Should().Contain("New Post");
cut.Markup.Should().Contain("Logout");
}
}
Pattern notes:
BunitContext and set up auth via Services.AddSingleton<IAuthorizationService, TestAuthorizationService>()RenderForUser() and CreatePrincipal() are helper methods that the test class defines (or inherits)cut.Markup.Should() to verify rendered HTML output directlyRejected: Automatic Playwright or Node.js installation. MyBlog has no
browser-test project. If that changes in future, adopt Playwright via a
dedicated tests/Web.Tests.E2E project with its own tooling.
Rejected: Treating browser automation as a replacement for bUnit in
tests/Unit.Tests. bUnit is faster, more reliable, and doesn't require a
running server.
Rejected: Adding committed Playwright specs, package.json, or CI
browser-test jobs today. Future work only if the team approves a dedicated
browser-test lane (outside current scope).
Rejected: Carrying over generic form-flow or responsive-design checklists. MyBlog should add those only when a concrete feature needs them.
Rejected: Using the commented Aspire sample in
tests/Integration.Tests/IntegrationTest1.cs as canonical coverage. (TODO:
Delete or replace with real AppHost smoke test.)