File: Forms\InputRadioTest.cs
Web Access
Project: src\src\Components\Web\test\Microsoft.AspNetCore.Components.Web.Tests.csproj (Microsoft.AspNetCore.Components.Web.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Linq.Expressions;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Test.Helpers;
 
namespace Microsoft.AspNetCore.Components.Forms;
 
public class InputRadioTest
{
    [Fact]
    public async Task ThrowsOnFirstRenderIfInputRadioHasNoGroup()
    {
        var model = new TestModel();
        var rootComponent = new TestInputRadioHostComponent<TestEnum>
        {
            EditContext = new EditContext(model),
            InnerContent = RadioButtonsWithoutGroup(null)
        };
 
        var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => RenderAndGetTestInputComponentAsync(rootComponent));
        Assert.Contains($"must have an ancestor", ex.Message);
    }
 
    [Fact]
    public async Task GroupGeneratesNameGuidWhenInvalidNameSupplied()
    {
        var model = new TestModel();
        var rootComponent = new TestInputRadioHostComponent<TestEnum>
        {
            EditContext = new EditContext(model)
            {
                ShouldUseFieldIdentifiers = false,
            },
            InnerContent = RadioButtonsWithGroup(null, () => model.TestEnum)
        };
 
        var inputRadioComponents = await RenderAndGetTestInputComponentAsync(rootComponent);
 
        Assert.All(inputRadioComponents, inputRadio => Assert.True(Guid.TryParseExact(inputRadio.GroupName, "N", out _)));
    }
 
    [Fact]
    public async Task RadioInputContextExistsWhenValidNameSupplied()
    {
        var groupName = "group";
        var model = new TestModel();
        var rootComponent = new TestInputRadioHostComponent<TestEnum>
        {
            EditContext = new EditContext(model),
            InnerContent = RadioButtonsWithGroup(groupName, () => model.TestEnum)
        };
 
        var inputRadioComponents = await RenderAndGetTestInputComponentAsync(rootComponent);
 
        Assert.All(inputRadioComponents, inputRadio => Assert.Equal(groupName, inputRadio.GroupName));
    }
 
    [Fact]
    public async Task InputElementIsAssignedSuccessfully()
    {
        var model = new TestModel();
        var rootComponent = new TestInputRadioHostComponent<TestEnum>
        {
            EditContext = new EditContext(model),
            InnerContent = RadioButtonsWithGroup(null, () => model.TestEnum)
        };
 
        var inputRadioComponents = await RenderAndGetTestInputComponentAsync(rootComponent);
 
        Assert.All(inputRadioComponents, inputRadio => Assert.NotNull(inputRadio.Element));
    }
 
    private static RenderFragment RadioButtonsWithoutGroup(string name) => (builder) =>
    {
        foreach (var selectedValue in (TestEnum[])Enum.GetValues(typeof(TestEnum)))
        {
            builder.OpenComponent<TestInputRadio>(0);
            builder.AddComponentParameter(1, "Name", name);
            builder.AddComponentParameter(2, "Value", selectedValue);
            builder.CloseComponent();
        }
    };
 
    private static RenderFragment RadioButtonsWithGroup(string name, Expression<Func<TestEnum>> valueExpression) => (builder) =>
    {
        builder.OpenComponent<InputRadioGroup<TestEnum>>(0);
        builder.AddComponentParameter(1, "Name", name);
        builder.AddComponentParameter(2, "ValueExpression", valueExpression);
        builder.AddComponentParameter(2, "ChildContent", new RenderFragment((childBuilder) =>
        {
            foreach (var value in (TestEnum[])Enum.GetValues(typeof(TestEnum)))
            {
                childBuilder.OpenComponent<TestInputRadio>(0);
                childBuilder.AddComponentParameter(1, "Value", value);
                childBuilder.CloseComponent();
            }
        }));
 
        builder.CloseComponent();
    };
 
    private static IEnumerable<TestInputRadio> FindInputRadioComponents(CapturedBatch batch)
        => batch.ReferenceFrames
                .Where(f => f.FrameType == RenderTreeFrameType.Component)
                .Select(f => f.Component)
                .OfType<TestInputRadio>();
 
    private static async Task<IEnumerable<TestInputRadio>> RenderAndGetTestInputComponentAsync(TestInputRadioHostComponent<TestEnum> rootComponent)
    {
        var testRenderer = new TestRenderer();
        var componentId = testRenderer.AssignRootComponentId(rootComponent);
        await testRenderer.RenderRootComponentAsync(componentId);
        return FindInputRadioComponents(testRenderer.Batches.Single());
    }
 
    private enum TestEnum
    {
        One,
        Two,
        Three
    }
 
    private class TestModel
    {
        public TestEnum TestEnum { get; set; }
    }
 
    private class TestInputRadio : InputRadio<TestEnum>
    {
        public string GroupName => Context.GroupName;
    }
 
    private class TestInputRadioHostComponent<TValue> : AutoRenderComponent
    {
        public EditContext EditContext { get; set; }
 
        public RenderFragment InnerContent { get; set; }
 
        protected override void BuildRenderTree(RenderTreeBuilder builder)
        {
            builder.OpenComponent<CascadingValue<EditContext>>(0);
            builder.AddComponentParameter(1, "Value", EditContext);
            builder.AddComponentParameter(2, "ChildContent", InnerContent);
            builder.CloseComponent();
        }
    }
}