mirror of
https://gitee.com/LongbowEnterprise/BootstrapBlazor.git
synced 2024-12-05 05:29:47 +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>
|
||||
[CascadingParameter]
|
||||
[NotNull]
|
||||
private EditContext? CurrentEditContext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 当前编辑窗体上下文
|
||||
/// </summary>
|
||||
[CascadingParameter]
|
||||
public ValidateForm? EditForm { get; set; }
|
||||
private ValidateForm? ValidateForm { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 初始化方法
|
||||
/// </summary>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
if (EditForm == null)
|
||||
if (ValidateForm == null)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(BootstrapBlazorDataAnnotationsValidator)} requires a cascading " +
|
||||
$"parameter of type {nameof(ValidateForm)}. For example, you can use {nameof(BootstrapBlazorDataAnnotationsValidator)} " +
|
||||
$"inside an {nameof(ValidateForm)}.");
|
||||
throw new InvalidOperationException($"{nameof(Components.BootstrapBlazorDataAnnotationsValidator)} requires a cascading " +
|
||||
$"parameter of type {nameof(Components.ValidateForm)}. For example, you can use {nameof(Components.BootstrapBlazorDataAnnotationsValidator)} " +
|
||||
$"inside an {nameof(Components.ValidateForm)}.");
|
||||
}
|
||||
|
||||
if (CurrentEditContext == null)
|
||||
{
|
||||
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);
|
||||
CurrentEditContext.AddEditContextDataAnnotationsValidation(ValidateForm);
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ public partial class ValidateForm : IAsyncDisposable
|
||||
/// <param name="value"></param>
|
||||
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>
|
||||
@ -125,15 +125,18 @@ public partial class ValidateForm : IAsyncDisposable
|
||||
private void InternalSetError(MemberExpression exp, string errorMessage)
|
||||
{
|
||||
var fieldName = exp.Member.Name;
|
||||
var modelType = exp.Expression?.Type;
|
||||
var validator = ValidatorCache.FirstOrDefault(c => c.Key.ModelType == modelType && c.Key.FieldName == fieldName).Value.ValidateComponent;
|
||||
if (validator != null)
|
||||
if (exp.Expression != 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 })
|
||||
};
|
||||
validator.ToggleMessage(results, true);
|
||||
var results = new List<ValidationResult>
|
||||
{
|
||||
new ValidationResult(errorMessage, new string[] { fieldName })
|
||||
};
|
||||
validator.ToggleMessage(results, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,17 +147,13 @@ public partial class ValidateForm : IAsyncDisposable
|
||||
/// <param name="errorMessage">错误描述信息,可为空,为空时查找资源文件</param>
|
||||
public void SetError(string propertyName, string errorMessage)
|
||||
{
|
||||
if (TryGetModelField(propertyName, out var modelType, out var fieldName)
|
||||
&& TryGetValidator(modelType, fieldName, out var validator))
|
||||
if (TryGetModelField(propertyName, out var modelType, out var fieldName) && 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 })
|
||||
};
|
||||
validator.ToggleMessage(results, true);
|
||||
}
|
||||
new ValidationResult(errorMessage, new string[] { fieldName })
|
||||
};
|
||||
validator.ToggleMessage(results, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,7 +179,7 @@ public partial class ValidateForm : IAsyncDisposable
|
||||
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;
|
||||
return validator != null;
|
||||
@ -333,11 +332,7 @@ public partial class ValidateForm : IAsyncDisposable
|
||||
{
|
||||
rule.ErrorMessage = result.ErrorMessage;
|
||||
}
|
||||
|
||||
var errorMessage = string.IsNullOrEmpty(rule.ErrorMessageResourceName)
|
||||
? rule.FormatErrorMessage(displayName ?? memberName)
|
||||
: rule.ErrorMessage;
|
||||
results.Add(new ValidationResult(errorMessage, new string[] { memberName }));
|
||||
results.Add(new ValidationResult(rule.ErrorMessage, new string[] { memberName }));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -350,8 +345,7 @@ public partial class ValidateForm : IAsyncDisposable
|
||||
private void ValidateProperty(ValidationContext context, List<ValidationResult> results)
|
||||
{
|
||||
// 获得所有可写属性
|
||||
var properties = context.ObjectType.GetRuntimeProperties()
|
||||
.Where(p => IsPublic(p) && p.CanWrite && !p.GetIndexParameters().Any());
|
||||
var properties = context.ObjectType.GetRuntimeProperties().Where(p => IsPublic(p) && p.CanWrite && !p.GetIndexParameters().Any());
|
||||
foreach (var pi in properties)
|
||||
{
|
||||
// 设置其关联属性字段
|
||||
@ -400,8 +394,8 @@ public partial class ValidateForm : IAsyncDisposable
|
||||
// 处理多个上传文件
|
||||
uploader.UploadFiles.ForEach(file =>
|
||||
{
|
||||
// 优先检查 File 流,如果没有检查 FileName
|
||||
ValidateDataAnnotations((object?)file.File ?? file.FileName, context, messages, pi, file.ValidateId);
|
||||
// 优先检查 File 流,不需要检查 FileName
|
||||
ValidateDataAnnotations(file.File, context, messages, pi, file.ValidateId);
|
||||
});
|
||||
}
|
||||
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>
|
||||
/// 注册提交按钮
|
||||
|
@ -18,11 +18,11 @@ internal static class IStringLocalizerExtensions
|
||||
/// <param name="key"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <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;
|
||||
text = null;
|
||||
var l = localizer?[key];
|
||||
var l = localizer[key];
|
||||
if (l != null)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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": "请选择 ...",
|
||||
"Primary": "小学",
|
||||
"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