mirror of
https://gitee.com/LongbowEnterprise/BootstrapBlazor.git
synced 2024-12-05 13:39:39 +08:00
!2480 test(#I4W9WY): add unit test for ValidateForm
* test: 增加异步提交单元测试 * test: 增加资源文件 * test: 增加资源文件单元测试 * test: 增加本地化单元测试 * refactor: 移除冗余代码 * refactor: 移除冗余代码 * refactor: 重构代码 * test: 增加代码覆盖率 * test: 增加辅助代码 * test: 增加 Upload 组件单元测试 * test: 增加属性是类单元测试 * test: 增加 Metadata 单元测试 * refactor: 重构代码 * test: 增加代码覆盖率 * test: 增加 SetError 单元测试 * test: 增加 ShowLabel 单元测试 * test: 增加 必填项 单元测试 * test: 增加校验所有字段单元测试 * test: 增加 OnFieldChanged 单元测试 * test: 增加 Validate 单元测试 * refactor: 增加 NotNullWhen 标签 * refactor: 重构逻辑增加空判断 * refactor: 使用 TryAdd 方法 * test: 增加 Validator 单元测试 * refactor: 移除冗余代码
This commit is contained in:
parent
12a50f5139
commit
4db1f69fed
@ -16,31 +16,27 @@ public class BootstrapBlazorDataAnnotationsValidator : ComponentBase
|
|||||||
/// 获得/设置 当前编辑数据上下文
|
/// 获得/设置 当前编辑数据上下文
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
|
[NotNull]
|
||||||
private EditContext? CurrentEditContext { get; set; }
|
private EditContext? CurrentEditContext { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获得/设置 当前编辑窗体上下文
|
/// 获得/设置 当前编辑窗体上下文
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
public ValidateForm? EditForm { get; set; }
|
private ValidateForm? ValidateForm { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始化方法
|
/// 初始化方法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
if (EditForm == null)
|
if (ValidateForm == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"{nameof(BootstrapBlazorDataAnnotationsValidator)} requires a cascading " +
|
throw new InvalidOperationException($"{nameof(Components.BootstrapBlazorDataAnnotationsValidator)} requires a cascading " +
|
||||||
$"parameter of type {nameof(ValidateForm)}. For example, you can use {nameof(BootstrapBlazorDataAnnotationsValidator)} " +
|
$"parameter of type {nameof(Components.ValidateForm)}. For example, you can use {nameof(Components.BootstrapBlazorDataAnnotationsValidator)} " +
|
||||||
$"inside an {nameof(ValidateForm)}.");
|
$"inside an {nameof(Components.ValidateForm)}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CurrentEditContext == null)
|
CurrentEditContext.AddEditContextDataAnnotationsValidation(ValidateForm);
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"{nameof(BootstrapBlazorDataAnnotationsValidator)} requires a cascading parameter of type {nameof(EditContext)}. For example, you can use {nameof(BootstrapBlazorDataAnnotationsValidator)} inside an EditForm.");
|
|
||||||
}
|
|
||||||
|
|
||||||
CurrentEditContext.AddEditContextDataAnnotationsValidation(EditForm);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ public partial class ValidateForm : IAsyncDisposable
|
|||||||
/// <param name="value"></param>
|
/// <param name="value"></param>
|
||||||
internal void AddValidator((string FieldName, Type ModelType) key, (FieldIdentifier FieldIdentifier, IValidateComponent IValidateComponent) value)
|
internal void AddValidator((string FieldName, Type ModelType) key, (FieldIdentifier FieldIdentifier, IValidateComponent IValidateComponent) value)
|
||||||
{
|
{
|
||||||
ValidatorCache.AddOrUpdate(key, k => value, (k, v) => v = value);
|
ValidatorCache.TryAdd(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -125,15 +125,18 @@ public partial class ValidateForm : IAsyncDisposable
|
|||||||
private void InternalSetError(MemberExpression exp, string errorMessage)
|
private void InternalSetError(MemberExpression exp, string errorMessage)
|
||||||
{
|
{
|
||||||
var fieldName = exp.Member.Name;
|
var fieldName = exp.Member.Name;
|
||||||
var modelType = exp.Expression?.Type;
|
if (exp.Expression != null)
|
||||||
var validator = ValidatorCache.FirstOrDefault(c => c.Key.ModelType == modelType && c.Key.FieldName == fieldName).Value.ValidateComponent;
|
|
||||||
if (validator != null)
|
|
||||||
{
|
{
|
||||||
var results = new List<ValidationResult>
|
var modelType = exp.Expression.Type;
|
||||||
|
var validator = ValidatorCache.FirstOrDefault(c => c.Key.ModelType == modelType && c.Key.FieldName == fieldName).Value.ValidateComponent;
|
||||||
|
if (validator != null)
|
||||||
{
|
{
|
||||||
new ValidationResult(errorMessage, new string[] { fieldName })
|
var results = new List<ValidationResult>
|
||||||
};
|
{
|
||||||
validator.ToggleMessage(results, true);
|
new ValidationResult(errorMessage, new string[] { fieldName })
|
||||||
|
};
|
||||||
|
validator.ToggleMessage(results, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,17 +147,13 @@ public partial class ValidateForm : IAsyncDisposable
|
|||||||
/// <param name="errorMessage">错误描述信息,可为空,为空时查找资源文件</param>
|
/// <param name="errorMessage">错误描述信息,可为空,为空时查找资源文件</param>
|
||||||
public void SetError(string propertyName, string errorMessage)
|
public void SetError(string propertyName, string errorMessage)
|
||||||
{
|
{
|
||||||
if (TryGetModelField(propertyName, out var modelType, out var fieldName)
|
if (TryGetModelField(propertyName, out var modelType, out var fieldName) && TryGetValidator(modelType, fieldName, out var validator))
|
||||||
&& TryGetValidator(modelType, fieldName, out var validator))
|
|
||||||
{
|
{
|
||||||
if (validator != null)
|
var results = new List<ValidationResult>
|
||||||
{
|
{
|
||||||
var results = new List<ValidationResult>
|
new ValidationResult(errorMessage, new string[] { fieldName })
|
||||||
{
|
};
|
||||||
new ValidationResult(errorMessage, new string[] { fieldName })
|
validator.ToggleMessage(results, true);
|
||||||
};
|
|
||||||
validator.ToggleMessage(results, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +179,7 @@ public partial class ValidateForm : IAsyncDisposable
|
|||||||
return propNames.IsEmpty;
|
return propNames.IsEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetValidator(Type modelType, string fieldName, [MaybeNull] out IValidateComponent validator)
|
private bool TryGetValidator(Type modelType, string fieldName, [NotNullWhen(true)] out IValidateComponent validator)
|
||||||
{
|
{
|
||||||
validator = ValidatorCache.FirstOrDefault(c => c.Key.ModelType == modelType && c.Key.FieldName == fieldName).Value.ValidateComponent;
|
validator = ValidatorCache.FirstOrDefault(c => c.Key.ModelType == modelType && c.Key.FieldName == fieldName).Value.ValidateComponent;
|
||||||
return validator != null;
|
return validator != null;
|
||||||
@ -333,11 +332,7 @@ public partial class ValidateForm : IAsyncDisposable
|
|||||||
{
|
{
|
||||||
rule.ErrorMessage = result.ErrorMessage;
|
rule.ErrorMessage = result.ErrorMessage;
|
||||||
}
|
}
|
||||||
|
results.Add(new ValidationResult(rule.ErrorMessage, new string[] { memberName }));
|
||||||
var errorMessage = string.IsNullOrEmpty(rule.ErrorMessageResourceName)
|
|
||||||
? rule.FormatErrorMessage(displayName ?? memberName)
|
|
||||||
: rule.ErrorMessage;
|
|
||||||
results.Add(new ValidationResult(errorMessage, new string[] { memberName }));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -350,8 +345,7 @@ public partial class ValidateForm : IAsyncDisposable
|
|||||||
private void ValidateProperty(ValidationContext context, List<ValidationResult> results)
|
private void ValidateProperty(ValidationContext context, List<ValidationResult> results)
|
||||||
{
|
{
|
||||||
// 获得所有可写属性
|
// 获得所有可写属性
|
||||||
var properties = context.ObjectType.GetRuntimeProperties()
|
var properties = context.ObjectType.GetRuntimeProperties().Where(p => IsPublic(p) && p.CanWrite && !p.GetIndexParameters().Any());
|
||||||
.Where(p => IsPublic(p) && p.CanWrite && !p.GetIndexParameters().Any());
|
|
||||||
foreach (var pi in properties)
|
foreach (var pi in properties)
|
||||||
{
|
{
|
||||||
// 设置其关联属性字段
|
// 设置其关联属性字段
|
||||||
@ -400,8 +394,8 @@ public partial class ValidateForm : IAsyncDisposable
|
|||||||
// 处理多个上传文件
|
// 处理多个上传文件
|
||||||
uploader.UploadFiles.ForEach(file =>
|
uploader.UploadFiles.ForEach(file =>
|
||||||
{
|
{
|
||||||
// 优先检查 File 流,如果没有检查 FileName
|
// 优先检查 File 流,不需要检查 FileName
|
||||||
ValidateDataAnnotations((object?)file.File ?? file.FileName, context, messages, pi, file.ValidateId);
|
ValidateDataAnnotations(file.File, context, messages, pi, file.ValidateId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -421,7 +415,7 @@ public partial class ValidateForm : IAsyncDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Button> AsyncSubmitButtons { get; set; } = new List<Button>();
|
private List<Button> AsyncSubmitButtons { get; } = new List<Button>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 注册提交按钮
|
/// 注册提交按钮
|
||||||
|
@ -18,11 +18,11 @@ internal static class IStringLocalizerExtensions
|
|||||||
/// <param name="key"></param>
|
/// <param name="key"></param>
|
||||||
/// <param name="text"></param>
|
/// <param name="text"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static bool TryGetLocalizerString(this IStringLocalizer? localizer, string key, [MaybeNullWhen(false)] out string? text)
|
public static bool TryGetLocalizerString(this IStringLocalizer localizer, string key, [MaybeNullWhen(false)] out string? text)
|
||||||
{
|
{
|
||||||
var ret = false;
|
var ret = false;
|
||||||
text = null;
|
text = null;
|
||||||
var l = localizer?[key];
|
var l = localizer[key];
|
||||||
if (l != null)
|
if (l != null)
|
||||||
{
|
{
|
||||||
ret = !l.ResourceNotFound;
|
ret = !l.ResourceNotFound;
|
||||||
|
384
test/UnitTest/Components/ValidateFormTest.cs
Normal file
384
test/UnitTest/Components/ValidateFormTest.cs
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
// Website: https://www.blazor.zone or https://argozhang.github.io/
|
||||||
|
|
||||||
|
using BootstrapBlazor.Shared;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace UnitTest.Components;
|
||||||
|
|
||||||
|
public class ValidateFormTest : BootstrapBlazorTestBase
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void BootstrapBlazorDataAnnotationsValidator_Error()
|
||||||
|
{
|
||||||
|
Assert.ThrowsAny<InvalidOperationException>(() => Context.RenderComponent<BootstrapBlazorDataAnnotationsValidator>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_Ok()
|
||||||
|
{
|
||||||
|
var valid = false;
|
||||||
|
var invalid = false;
|
||||||
|
var foo = new Foo();
|
||||||
|
var cut = Context.RenderComponent<ValidateForm>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Model, foo);
|
||||||
|
pb.Add(a => a.OnValidSubmit, context =>
|
||||||
|
{
|
||||||
|
valid = true;
|
||||||
|
return Task.CompletedTask;
|
||||||
|
});
|
||||||
|
pb.Add(a => a.OnInvalidSubmit, context =>
|
||||||
|
{
|
||||||
|
invalid = true;
|
||||||
|
return Task.CompletedTask;
|
||||||
|
});
|
||||||
|
pb.AddChildContent<BootstrapInput<string>>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Value, foo.Name);
|
||||||
|
pb.Add(a => a.ValueChanged, EventCallback.Factory.Create<string>(this, v => foo.Name = v));
|
||||||
|
pb.Add(a => a.ValueExpression, foo.GenerateValueExpression());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var form = cut.Find("form");
|
||||||
|
cut.InvokeAsync(() => form.Submit());
|
||||||
|
Assert.True(invalid);
|
||||||
|
|
||||||
|
cut.InvokeAsync(() => cut.Find("input").Change("Test"));
|
||||||
|
cut.InvokeAsync(() => form.Submit());
|
||||||
|
Assert.True(valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OnFieldValueChanged_Ok()
|
||||||
|
{
|
||||||
|
var changed = false;
|
||||||
|
var foo = new Foo();
|
||||||
|
var cut = Context.RenderComponent<ValidateForm>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Model, foo);
|
||||||
|
pb.Add(a => a.OnFieldValueChanged, (fieldName, v) =>
|
||||||
|
{
|
||||||
|
changed = true;
|
||||||
|
});
|
||||||
|
pb.AddChildContent<BootstrapInput<string>>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Value, foo.Name);
|
||||||
|
pb.Add(a => a.ValueChanged, EventCallback.Factory.Create<string>(this, v => foo.Name = v));
|
||||||
|
pb.Add(a => a.ValueExpression, foo.GenerateValueExpression());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var form = cut.Find("form");
|
||||||
|
cut.InvokeAsync(() => cut.Find("input").Change("Test"));
|
||||||
|
cut.InvokeAsync(() => form.Submit());
|
||||||
|
Assert.True(changed);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ValidateAllProperties_Ok()
|
||||||
|
{
|
||||||
|
var foo = new Foo();
|
||||||
|
var invalid = false;
|
||||||
|
var cut = Context.RenderComponent<ValidateForm>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Model, foo);
|
||||||
|
pb.Add(a => a.ValidateAllProperties, true);
|
||||||
|
pb.Add(a => a.OnInvalidSubmit, context =>
|
||||||
|
{
|
||||||
|
invalid = true;
|
||||||
|
return Task.CompletedTask;
|
||||||
|
});
|
||||||
|
pb.AddChildContent<BootstrapInput<string>>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Value, foo.Name);
|
||||||
|
pb.Add(a => a.ValueChanged, EventCallback.Factory.Create<string>(this, v => foo.Name = v));
|
||||||
|
pb.Add(a => a.ValueExpression, foo.GenerateValueExpression());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var form = cut.Find("form");
|
||||||
|
cut.InvokeAsync(() => form.Submit());
|
||||||
|
Assert.True(invalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ShowRequiredMark_Ok()
|
||||||
|
{
|
||||||
|
var foo = new Foo();
|
||||||
|
var cut = Context.RenderComponent<ValidateForm>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Model, foo);
|
||||||
|
pb.Add(a => a.ShowRequiredMark, true);
|
||||||
|
pb.AddChildContent<BootstrapInput<string>>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Value, foo.Name);
|
||||||
|
pb.Add(a => a.ValueChanged, EventCallback.Factory.Create<string>(this, v => foo.Name = v));
|
||||||
|
pb.Add(a => a.ValueExpression, foo.GenerateValueExpression());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
cut.Contains("required=\"true\"");
|
||||||
|
|
||||||
|
cut.SetParametersAndRender(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.ShowRequiredMark, false);
|
||||||
|
});
|
||||||
|
cut.DoesNotContain("required=\"true\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ShowLabel_Ok()
|
||||||
|
{
|
||||||
|
var foo = new Foo();
|
||||||
|
var cut = Context.RenderComponent<ValidateForm>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Model, foo);
|
||||||
|
pb.Add(a => a.ShowLabel, true);
|
||||||
|
pb.AddChildContent<BootstrapInput<string>>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Value, foo.Name);
|
||||||
|
pb.Add(a => a.ValueChanged, EventCallback.Factory.Create<string>(this, v => foo.Name = v));
|
||||||
|
pb.Add(a => a.ValueExpression, foo.GenerateValueExpression());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
cut.Contains("label");
|
||||||
|
|
||||||
|
cut.SetParametersAndRender(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.ShowLabel, false);
|
||||||
|
});
|
||||||
|
cut.DoesNotContain("label");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetError_Ok()
|
||||||
|
{
|
||||||
|
var foo = new Foo();
|
||||||
|
var dummy = new Dummy();
|
||||||
|
var cut = Context.RenderComponent<ValidateForm>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Model, foo);
|
||||||
|
pb.AddChildContent<BootstrapInput<string>>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Value, foo.Name);
|
||||||
|
pb.Add(a => a.ValueExpression, foo.GenerateValueExpression());
|
||||||
|
});
|
||||||
|
pb.AddChildContent<DateTimePicker<DateTime?>>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Value, dummy.Value);
|
||||||
|
pb.Add(a => a.ValueExpression, Utility.GenerateValueExpression(dummy, "Value", typeof(DateTime?)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
cut.Instance.SetError("Name", "Test_SetError");
|
||||||
|
cut.Instance.SetError("Test.Name", "Test_SetError");
|
||||||
|
cut.Instance.SetError<Foo>(f => f.Name, "Name_SetError");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetError_UnaryExpression()
|
||||||
|
{
|
||||||
|
var foo = new Foo();
|
||||||
|
var dummy = new Dummy();
|
||||||
|
var cut = Context.RenderComponent<ValidateForm>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Model, dummy);
|
||||||
|
pb.AddChildContent<DateTimePicker<DateTime?>>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Value, foo.DateTime);
|
||||||
|
pb.Add(a => a.ValueExpression, Utility.GenerateValueExpression(foo, "DateTime", typeof(DateTime?)));
|
||||||
|
});
|
||||||
|
pb.AddChildContent<DateTimePicker<DateTime?>>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Value, dummy.Value);
|
||||||
|
pb.Add(a => a.ValueExpression, Utility.GenerateValueExpression(dummy, "Value", typeof(DateTime?)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
cut.Instance.SetError<Dummy>(f => f.Value, "Name_SetError");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MetadataTypeAttribute_Ok()
|
||||||
|
{
|
||||||
|
var foo = new Dummy();
|
||||||
|
var cut = Context.RenderComponent<ValidateForm>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Model, foo);
|
||||||
|
pb.AddChildContent<DateTimePicker<DateTime?>>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Value, foo.Value);
|
||||||
|
pb.Add(a => a.ValueExpression, Utility.GenerateValueExpression(foo, "Value", typeof(DateTime?)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var form = cut.Find("form");
|
||||||
|
cut.InvokeAsync(() => form.Submit());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_Class_Ok()
|
||||||
|
{
|
||||||
|
var foo = new Dummy();
|
||||||
|
var cut = Context.RenderComponent<ValidateForm>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Model, foo);
|
||||||
|
pb.Add(a => a.ValidateAllProperties, true);
|
||||||
|
pb.AddChildContent<BootstrapInput<string>>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Value, foo.Foo.Name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var form = cut.Find("form");
|
||||||
|
cut.InvokeAsync(() => form.Submit());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_UploadFile_Ok()
|
||||||
|
{
|
||||||
|
var foo = new Dummy() { File = "text.txt" };
|
||||||
|
var cut = Context.RenderComponent<ValidateForm>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Model, foo);
|
||||||
|
pb.AddChildContent<ButtonUpload<string>>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Value, foo.File);
|
||||||
|
pb.Add(a => a.ValueExpression, Utility.GenerateValueExpression(foo, "File", typeof(string)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var form = cut.Find("form");
|
||||||
|
cut.InvokeAsync(() => form.Submit());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_Localizer_Ok()
|
||||||
|
{
|
||||||
|
var foo = new MockFoo();
|
||||||
|
var cut = Context.RenderComponent<ValidateForm>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Model, foo);
|
||||||
|
pb.AddChildContent<BootstrapInput<string>>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Value, foo.Name);
|
||||||
|
pb.Add(a => a.ValueExpression, Utility.GenerateValueExpression(foo, "Name", typeof(string)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var form = cut.Find("form");
|
||||||
|
cut.InvokeAsync(() => form.Submit());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_Attribute_Ok()
|
||||||
|
{
|
||||||
|
var foo = new MockFoo();
|
||||||
|
var cut = Context.RenderComponent<ValidateForm>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Model, foo);
|
||||||
|
pb.AddChildContent<BootstrapInput<string>>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Value, foo.Rule);
|
||||||
|
pb.Add(a => a.ValueExpression, Utility.GenerateValueExpression(foo, "Rule", typeof(string)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var form = cut.Find("form");
|
||||||
|
cut.InvokeAsync(() => form.Submit());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_MemberName_Ok()
|
||||||
|
{
|
||||||
|
var foo = new MockFoo();
|
||||||
|
var cut = Context.RenderComponent<ValidateForm>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Model, foo);
|
||||||
|
pb.AddChildContent<BootstrapInput<string>>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Value, foo.Member);
|
||||||
|
pb.Add(a => a.ValueExpression, Utility.GenerateValueExpression(foo, "Member", typeof(string)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var form = cut.Find("form");
|
||||||
|
cut.InvokeAsync(() => form.Submit());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_Address_Ok()
|
||||||
|
{
|
||||||
|
var foo = new MockFoo();
|
||||||
|
var cut = Context.RenderComponent<ValidateForm>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Model, foo);
|
||||||
|
pb.AddChildContent<BootstrapInput<string>>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Value, foo.Address);
|
||||||
|
pb.Add(a => a.ValueExpression, Utility.GenerateValueExpression(foo, "Address", typeof(string)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var form = cut.Find("form");
|
||||||
|
cut.InvokeAsync(() => form.Submit());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ValidateFormButton_Ok()
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<bool>();
|
||||||
|
var valid = false;
|
||||||
|
var foo = new Foo() { Name = "Test" };
|
||||||
|
var cut = Context.RenderComponent<ValidateForm>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(v => v.Model, foo);
|
||||||
|
pb.Add(v => v.OnValidSubmit, context =>
|
||||||
|
{
|
||||||
|
valid = true;
|
||||||
|
tcs.SetResult(true);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
});
|
||||||
|
pb.AddChildContent<BootstrapInput<string>>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(a => a.Value, foo.Name);
|
||||||
|
pb.Add(a => a.ValueExpression, foo.GenerateValueExpression());
|
||||||
|
});
|
||||||
|
pb.AddChildContent<Button>(pb =>
|
||||||
|
{
|
||||||
|
pb.Add(b => b.IsAsync, true);
|
||||||
|
pb.Add(b => b.ButtonType, ButtonType.Submit);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await cut.InvokeAsync(() => cut.Find("form").Submit());
|
||||||
|
await tcs.Task;
|
||||||
|
Assert.True(valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MetadataType(typeof(DummyMetadata))]
|
||||||
|
private class Dummy
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public DateTime? Value { get; set; }
|
||||||
|
|
||||||
|
public Foo Foo { get; set; } = new Foo();
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string? File { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DummyMetadata
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public DateTime? Value { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockFoo
|
||||||
|
{
|
||||||
|
[Required(ErrorMessage = "{0} is Required")]
|
||||||
|
public string? Name { get; set; }
|
||||||
|
|
||||||
|
[EmailAddress(ErrorMessage = "{0} must fill")]
|
||||||
|
[Display(Name = "Address")]
|
||||||
|
public string? Address { get; set; } = "test";
|
||||||
|
|
||||||
|
[Required()]
|
||||||
|
public string? Rule { get; set; }
|
||||||
|
|
||||||
|
[EmailAddress()]
|
||||||
|
public string? Member { get; set; } = "test";
|
||||||
|
}
|
||||||
|
}
|
@ -46,9 +46,10 @@ public class BootstrapBlazorTestHost : IDisposable
|
|||||||
|
|
||||||
protected virtual void ConfigureServices(IServiceCollection services)
|
protected virtual void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddBootstrapBlazor(localizationAction: options =>
|
services.AddBootstrapBlazor();
|
||||||
|
services.ConfigureJsonLocalizationOptions(op =>
|
||||||
{
|
{
|
||||||
options.AdditionalJsonAssemblies = new[] { typeof(Alert).Assembly };
|
op.AdditionalJsonAssemblies = new[] { typeof(Alert).Assembly, GetType().Assembly };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,5 +25,13 @@
|
|||||||
"PlaceHolder": "请选择 ...",
|
"PlaceHolder": "请选择 ...",
|
||||||
"Primary": "小学",
|
"Primary": "小学",
|
||||||
"Middel": "中学"
|
"Middel": "中学"
|
||||||
|
},
|
||||||
|
"UnitTest.Components.ValidateFormTest.MockFoo": {
|
||||||
|
"{0} is Required": "Name is Required",
|
||||||
|
"Member.EmailAddress": "Member is Required",
|
||||||
|
"Email": "Age msg"
|
||||||
|
},
|
||||||
|
"System.ComponentModel.DataAnnotations.RequiredAttribute": {
|
||||||
|
"ErrorMessage": "Rule is Required"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user