224 lines
7.4 KiB
C#
224 lines
7.4 KiB
C#
using System.ComponentModel.DataAnnotations;
|
|
using System.Linq.Expressions;
|
|
using Microsoft.AspNetCore.Components;
|
|
using Microsoft.AspNetCore.Components.Forms;
|
|
using Microsoft.AspNetCore.Components.Rendering;
|
|
using Bunit;
|
|
using SufiChain.SufiBlazor.Components.Forms;
|
|
using Xunit;
|
|
|
|
namespace SufiChain.SufiBlazor.Tests.Components.Forms;
|
|
|
|
public class SbFormTests : BunitContext
|
|
{
|
|
private IRenderedComponent<SbForm> RenderForm(
|
|
Action<ComponentParameterCollectionBuilder<SbForm>>? configure = null)
|
|
{
|
|
return Render<SbForm>(p =>
|
|
{
|
|
p.Add(x => x.Model, new TestFormModel());
|
|
p.AddChildContent("<span>Form content</span>");
|
|
configure?.Invoke(p);
|
|
});
|
|
}
|
|
|
|
[Fact]
|
|
public void RendersFormStructure()
|
|
{
|
|
// Arrange & Act
|
|
var cut = RenderForm();
|
|
|
|
// Assert
|
|
var form = cut.Find("form.sb-form");
|
|
Assert.NotNull(form);
|
|
Assert.NotNull(cut.Find(".sb-form__content"));
|
|
}
|
|
|
|
[Fact]
|
|
public void RendersChildContent()
|
|
{
|
|
// Arrange & Act
|
|
var cut = RenderForm();
|
|
|
|
// Assert
|
|
var content = cut.Find(".sb-form__content");
|
|
Assert.NotNull(content);
|
|
Assert.Contains("Form content", content.InnerHtml);
|
|
}
|
|
|
|
[Fact]
|
|
public void RendersFooterContentWhenProvided()
|
|
{
|
|
// Arrange & Act
|
|
var cut = Render<SbForm>(p => p
|
|
.Add(x => x.Model, new TestFormModel())
|
|
.AddChildContent("<span>Body</span>")
|
|
.Add(x => x.FooterContent, (RenderFragment)(b => b.AddMarkupContent(0, "<button type=\"submit\">Save</button>"))));
|
|
|
|
// Assert
|
|
var footer = cut.Find(".sb-form__footer");
|
|
Assert.NotNull(footer);
|
|
Assert.NotNull(cut.Find("button[type='submit']"));
|
|
}
|
|
|
|
[Fact]
|
|
public void DoesNotRenderFooterWhenFooterContentNull()
|
|
{
|
|
// Arrange & Act
|
|
var cut = RenderForm();
|
|
|
|
// Assert
|
|
Assert.Empty(cut.FindAll(".sb-form__footer"));
|
|
}
|
|
|
|
[Fact]
|
|
public void RendersDataAnnotationsValidatorWhenEnableDataAnnotationsValidationTrue()
|
|
{
|
|
// Arrange & Act
|
|
var cut = RenderForm(p => p.Add(x => x.EnableDataAnnotationsValidation, true));
|
|
|
|
// Assert - DataAnnotationsValidator renders no visible output but is in the component tree
|
|
var validator = cut.FindComponent<DataAnnotationsValidator>();
|
|
Assert.NotNull(validator);
|
|
}
|
|
|
|
[Fact]
|
|
public void DoesNotRenderDataAnnotationsValidatorWhenDisabled()
|
|
{
|
|
// Arrange & Act
|
|
var cut = RenderForm(p => p.Add(x => x.EnableDataAnnotationsValidation, false));
|
|
|
|
// Assert
|
|
Assert.Throws<Bunit.Rendering.ComponentNotFoundException>(() => cut.FindComponent<DataAnnotationsValidator>());
|
|
}
|
|
|
|
[Fact]
|
|
public void AppliesClassParameter()
|
|
{
|
|
// Arrange & Act
|
|
var cut = RenderForm(p => p.Add(x => x.Class, "my-form"));
|
|
|
|
// Assert
|
|
var form = cut.Find("form");
|
|
Assert.Contains("sb-form", form.ClassList);
|
|
Assert.Contains("my-form", form.ClassList);
|
|
}
|
|
|
|
[Fact]
|
|
public void AppliesAdditionalAttributes()
|
|
{
|
|
// Arrange & Act
|
|
var cut = RenderForm(p => p.Add(x => x.AdditionalAttributes, new Dictionary<string, object>
|
|
{
|
|
{ "data-testid", "main-form" },
|
|
{ "aria-label", "Test form" }
|
|
}));
|
|
|
|
// Assert
|
|
var form = cut.Find("form");
|
|
Assert.Equal("main-form", form.GetAttribute("data-testid"));
|
|
Assert.Equal("Test form", form.GetAttribute("aria-label"));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokesOnValidSubmitWhenFormValid()
|
|
{
|
|
// Arrange
|
|
EditContext? receivedContext = null;
|
|
var model = new TestFormModel { Name = "Valid Name" };
|
|
var cut = Render<SbForm>(p => p
|
|
.Add(x => x.Model, model)
|
|
.Add(x => x.OnValidSubmit, EventCallback.Factory.Create<EditContext>(this, ctx => receivedContext = ctx))
|
|
.Add(x => x.OnInvalidSubmit, EventCallback.Factory.Create<EditContext>(this, _ => { }))
|
|
.AddChildContent(FormFieldsWithSubmit(model)));
|
|
|
|
// Act
|
|
var submitBtn = cut.Find("button[type='submit']");
|
|
await cut.InvokeAsync(() => submitBtn!.Click());
|
|
|
|
// Assert
|
|
Assert.NotNull(receivedContext);
|
|
Assert.True(receivedContext!.GetValidationMessages().Count() == 0);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokesOnInvalidSubmitWhenFormInvalid()
|
|
{
|
|
// Arrange - empty Name triggers [Required]
|
|
EditContext? receivedContext = null;
|
|
var model = new TestFormModel { Name = "" };
|
|
var cut = Render<SbForm>(p => p
|
|
.Add(x => x.Model, model)
|
|
.Add(x => x.OnValidSubmit, EventCallback.Factory.Create<EditContext>(this, _ => { }))
|
|
.Add(x => x.OnInvalidSubmit, EventCallback.Factory.Create<EditContext>(this, ctx => receivedContext = ctx))
|
|
.AddChildContent(FormFieldsWithSubmit(model)));
|
|
|
|
// Act
|
|
var submitBtn = cut.Find("button[type='submit']");
|
|
await cut.InvokeAsync(() => submitBtn!.Click());
|
|
|
|
// Assert
|
|
Assert.NotNull(receivedContext);
|
|
Assert.True(receivedContext!.GetValidationMessages().Count() > 0);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ShowsValidationSummaryAfterInvalidSubmit()
|
|
{
|
|
// Arrange
|
|
var model = new TestFormModel { Name = "" };
|
|
var cut = Render<SbForm>(p => p
|
|
.Add(x => x.Model, model)
|
|
.Add(x => x.OnInvalidSubmit, EventCallback.Factory.Create<EditContext>(this, _ => { }))
|
|
.AddChildContent(FormFieldsWithSubmit(model)));
|
|
|
|
// Assert - not shown before submit
|
|
Assert.Empty(cut.FindAll(".sb-form__validation-summary"));
|
|
|
|
// Act
|
|
var submitBtn = cut.Find("button[type='submit']");
|
|
await cut.InvokeAsync(() => submitBtn!.Click());
|
|
|
|
// Assert - validation summary shown after invalid submit
|
|
var summary = cut.Find(".sb-form__validation-summary");
|
|
Assert.NotNull(summary);
|
|
Assert.NotNull(cut.FindComponent<ValidationSummary>());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DoesNotShowValidationSummaryWhenShowValidationSummaryFalse()
|
|
{
|
|
// Arrange
|
|
var model = new TestFormModel { Name = "" };
|
|
var cut = Render<SbForm>(p => p
|
|
.Add(x => x.Model, model)
|
|
.Add(x => x.ShowValidationSummary, false)
|
|
.Add(x => x.OnInvalidSubmit, EventCallback.Factory.Create<EditContext>(this, _ => { }))
|
|
.AddChildContent(FormFieldsWithSubmit(model)));
|
|
|
|
// Act
|
|
var submitBtn = cut.Find("button[type='submit']");
|
|
await cut.InvokeAsync(() => submitBtn!.Click());
|
|
|
|
// Assert
|
|
Assert.Empty(cut.FindAll(".sb-form__validation-summary"));
|
|
}
|
|
|
|
private RenderFragment FormFieldsWithSubmit(TestFormModel model) => builder =>
|
|
{
|
|
builder.OpenComponent<InputText>(0);
|
|
builder.AddAttribute(1, "Value", model.Name);
|
|
builder.AddAttribute(2, "ValueChanged", EventCallback.Factory.Create<string>(this, v => model.Name = v ?? ""));
|
|
builder.AddAttribute(3, "ValueExpression", (Expression<Func<string>>)(() => model.Name));
|
|
builder.AddAttribute(4, "Id", "name");
|
|
builder.CloseComponent();
|
|
builder.AddMarkupContent(5, "<button type=\"submit\">Submit</button>");
|
|
};
|
|
}
|
|
|
|
internal class TestFormModel
|
|
{
|
|
[Required(ErrorMessage = "Name is required")]
|
|
public string Name { get; set; } = "";
|
|
}
|