using Bunit; using Microsoft.AspNetCore.Components; using Microsoft.Extensions.DependencyInjection; using SilverOS.Welcome.App.Components; using SilverOS.Welcome.App.Components.Steps; using Xunit; public class AccountStepTests : TestContext { // Helper: register WizardState and render AccountStep with an OnValidityChanged capture. private (IRenderedComponent cut, Func lastValidity) RenderStep(WizardState? state = null) { var wizardState = state ?? new WizardState(); Services.AddSingleton(wizardState); bool? captured = null; var cut = RenderComponent(p => p.Add(s => s.OnValidityChanged, EventCallback.Factory.Create(this, v => captured = v))); return (cut, () => captured); } [Fact] public void OnValidityChanged_fires_false_on_initial_mount_with_empty_fields() { var (_, lastValidity) = RenderStep(); Assert.NotNull(lastValidity()); Assert.False(lastValidity(), "Step should be invalid on first mount (empty fields)."); } [Fact] public void OnValidityChanged_fires_true_after_all_valid_inputs_are_entered() { var (cut, lastValidity) = RenderStep(); // Simulate user filling in all four fields. cut.Find("#username").Input("alice"); cut.Find("#password").Input("Secret1!"); cut.Find("#adminpassword").Input("Admin1!"); cut.Find("#bitlockerpin").Input("123456"); Assert.True(lastValidity(), "Step should be valid after all fields are correctly filled."); } [Fact] public void OnValidityChanged_fires_false_when_a_field_is_cleared_after_being_valid() { var (cut, lastValidity) = RenderStep(); cut.Find("#username").Input("alice"); cut.Find("#password").Input("Secret1!"); cut.Find("#adminpassword").Input("Admin1!"); cut.Find("#bitlockerpin").Input("123456"); Assert.True(lastValidity()); // sanity // Clear a required field — must revert to invalid. cut.Find("#username").Input(""); Assert.False(lastValidity(), "Step should become invalid again when a required field is cleared."); } [Fact] public void OnValidityChanged_fires_false_when_pin_is_non_numeric_or_too_short() { var (cut, lastValidity) = RenderStep(); cut.Find("#username").Input("alice"); cut.Find("#password").Input("Secret1!"); cut.Find("#adminpassword").Input("Admin1!"); // Too short — 5 digits. cut.Find("#bitlockerpin").Input("12345"); Assert.False(lastValidity(), "PIN with only 5 digits must be invalid."); // Non-numeric. cut.Find("#bitlockerpin").Input("abc123"); Assert.False(lastValidity(), "Non-numeric PIN must be invalid."); // Exactly 6 digits — valid. cut.Find("#bitlockerpin").Input("123456"); Assert.True(lastValidity(), "Exactly 6 numeric digits is valid."); } [Fact] public void OnValidityChanged_fires_true_on_mount_when_wizard_state_already_populated() { var prefilledState = new WizardState { Username = "alice", Password = "Secret1!", AdminPassword = "Admin1!", BitLockerPin = "123456" }; var (_, lastValidity) = RenderStep(prefilledState); Assert.True(lastValidity(), "Step should fire valid=true on mount when WizardState already has valid values (Back→Forward re-mount)."); } }