mirror of
https://gitee.com/LongbowEnterprise/BootstrapBlazor.git
synced 2024-12-02 12:09:59 +08:00
!2475 test(#I4VXYM): add unit test for BootstrapInputNumber
* test: 增加类型异常单元测试 * test: 增加验证错误信息单元测试 * test: 增加数据类型单元测试 * revert: 恢复单元测试 * test: 增加数据类型单元测试 * test: 增加 ShowButtons 单元测试 * test: 增加 Blur 单元测试 * doc: 更新文档 * Merge branch 'main' into test-input * test: 增加表单验证单元测试 * test: 增加 int 单元测试 * test: 增加 double 单元测试 * test: 增加单元测试 * refactor: 移除静态构造函数 * doc: 格式化代码
This commit is contained in:
parent
49c26a9a6f
commit
191023fb5e
@ -23,34 +23,38 @@ public sealed partial class Popovers
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<AttributeItem> GetAttributes() => new AttributeItem[]
|
||||
{
|
||||
// TODO: 移动到数据库中
|
||||
new AttributeItem() {
|
||||
Name = "Cotent",
|
||||
Description = "Popover 弹窗内容",
|
||||
Type = "string",
|
||||
ValueList = "",
|
||||
DefaultValue = "Popover"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "IsHtml",
|
||||
Description = "内容中是否包含 Html 代码",
|
||||
Type = "boolean",
|
||||
ValueList = "",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "Placement",
|
||||
Description = "位置",
|
||||
Type = "Placement",
|
||||
ValueList = "Auto / Top / Left / Bottom / Right",
|
||||
DefaultValue = "Auto"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "Title",
|
||||
Description = "Popover 弹窗标题",
|
||||
Type = "string",
|
||||
ValueList = "",
|
||||
DefaultValue = "Popover"
|
||||
},
|
||||
// TODO: 移动到数据库中
|
||||
new AttributeItem()
|
||||
{
|
||||
Name = "Cotent",
|
||||
Description = "Popover 弹窗内容",
|
||||
Type = "string",
|
||||
ValueList = "",
|
||||
DefaultValue = "Popover"
|
||||
},
|
||||
new AttributeItem()
|
||||
{
|
||||
Name = "IsHtml",
|
||||
Description = "内容中是否包含 Html 代码",
|
||||
Type = "boolean",
|
||||
ValueList = "",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem()
|
||||
{
|
||||
Name = "Placement",
|
||||
Description = "位置",
|
||||
Type = "Placement",
|
||||
ValueList = "Auto / Top / Left / Bottom / Right",
|
||||
DefaultValue = "Auto"
|
||||
},
|
||||
new AttributeItem()
|
||||
{
|
||||
Name = "Title",
|
||||
Description = "Popover 弹窗标题",
|
||||
Type = "string",
|
||||
ValueList = "",
|
||||
DefaultValue = "Popover"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -74,7 +74,12 @@ public partial class BootstrapInputNumber<TValue>
|
||||
[NotNull]
|
||||
private IStringLocalizer<BootstrapInputNumber<TValue>>? Localizer { get; set; }
|
||||
|
||||
static BootstrapInputNumber()
|
||||
/// <summary>
|
||||
/// SetParametersAsync 方法
|
||||
/// </summary>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
public override Task SetParametersAsync(ParameterView parameters)
|
||||
{
|
||||
// Unwrap Nullable<T>, because InputBase already deals with the Nullable aspect
|
||||
// of it for us. We will only get asked to parse the T for nonempty inputs.
|
||||
@ -83,6 +88,8 @@ public partial class BootstrapInputNumber<TValue>
|
||||
{
|
||||
throw new InvalidOperationException($"The type '{targetType}' is not a supported numeric type.");
|
||||
}
|
||||
|
||||
return base.SetParametersAsync(parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -120,17 +127,24 @@ public partial class BootstrapInputNumber<TValue>
|
||||
? Formatter.Invoke(value)
|
||||
: (!string.IsNullOrEmpty(FormatString) && value != null
|
||||
? Utility.Format(value, FormatString)
|
||||
: value switch
|
||||
{
|
||||
null => null,
|
||||
int @int => BindConverter.FormatValue(@int, CultureInfo.InvariantCulture),
|
||||
long @long => BindConverter.FormatValue(@long, CultureInfo.InvariantCulture),
|
||||
short @short => BindConverter.FormatValue(@short, CultureInfo.InvariantCulture),
|
||||
float @float => BindConverter.FormatValue(@float, CultureInfo.InvariantCulture),
|
||||
double @double => BindConverter.FormatValue(@double, CultureInfo.InvariantCulture),
|
||||
decimal @decimal => BindConverter.FormatValue(@decimal, CultureInfo.InvariantCulture),
|
||||
_ => throw new InvalidOperationException($"Unsupported type {value!.GetType()}"),
|
||||
});
|
||||
: InternalFormat(value));
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
protected virtual string? InternalFormat(TValue value) => value switch
|
||||
{
|
||||
int @int => BindConverter.FormatValue(@int, CultureInfo.InvariantCulture),
|
||||
long @long => BindConverter.FormatValue(@long, CultureInfo.InvariantCulture),
|
||||
short @short => BindConverter.FormatValue(@short, CultureInfo.InvariantCulture),
|
||||
float @float => BindConverter.FormatValue(@float, CultureInfo.InvariantCulture),
|
||||
double @double => BindConverter.FormatValue(@double, CultureInfo.InvariantCulture),
|
||||
decimal @decimal => BindConverter.FormatValue(@decimal, CultureInfo.InvariantCulture),
|
||||
_ => throw new InvalidOperationException($"Unsupported type {value!.GetType()}"),
|
||||
};
|
||||
|
||||
private void SetStep()
|
||||
{
|
||||
|
@ -130,9 +130,9 @@ public partial class ValidateForm : IAsyncDisposable
|
||||
if (validator != null)
|
||||
{
|
||||
var results = new List<ValidationResult>
|
||||
{
|
||||
new ValidationResult(errorMessage, new string[] { fieldName })
|
||||
};
|
||||
{
|
||||
new ValidationResult(errorMessage, new string[] { fieldName })
|
||||
};
|
||||
validator.ToggleMessage(results, true);
|
||||
}
|
||||
}
|
||||
@ -150,9 +150,9 @@ public partial class ValidateForm : IAsyncDisposable
|
||||
if (validator != null)
|
||||
{
|
||||
var results = new List<ValidationResult>
|
||||
{
|
||||
new ValidationResult(errorMessage, new string[] { fieldName })
|
||||
};
|
||||
{
|
||||
new ValidationResult(errorMessage, new string[] { fieldName })
|
||||
};
|
||||
validator.ToggleMessage(results, true);
|
||||
}
|
||||
}
|
||||
|
167
test/UnitTest/Components/InputNumberTest.cs
Normal file
167
test/UnitTest/Components/InputNumberTest.cs
Normal file
@ -0,0 +1,167 @@
|
||||
// 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 System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace UnitTest.Components;
|
||||
|
||||
public class InputNumberTest : BootstrapBlazorTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void OnBlur_Ok()
|
||||
{
|
||||
var cut = Context.RenderComponent<BootstrapInputNumber<int>>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Min, "0");
|
||||
pb.Add(a => a.Max, "10");
|
||||
pb.Add(a => a.Step, "2");
|
||||
});
|
||||
cut.Contains("min=\"0\"");
|
||||
cut.Contains("max=\"10\"");
|
||||
cut.Contains("step=\"2\"");
|
||||
|
||||
var input = cut.Find("input");
|
||||
cut.InvokeAsync(() => input.Blur());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateForm()
|
||||
{
|
||||
var foo = new Cat() { Count = 20 };
|
||||
var cut = Context.RenderComponent<ValidateForm>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Model, foo);
|
||||
pb.AddChildContent<BootstrapInputNumber<int>>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Value, foo.Count);
|
||||
pb.Add(a => a.ValueExpression, Utility.GenerateValueExpression(foo, nameof(Cat.Count), typeof(int)));
|
||||
});
|
||||
});
|
||||
cut.Contains("class=\"form-label\"");
|
||||
|
||||
var input = cut.Find("input");
|
||||
cut.InvokeAsync(() => input.Change(""));
|
||||
|
||||
var form = cut.Find("form");
|
||||
cut.InvokeAsync(() => form.Submit());
|
||||
cut.Contains("is-invalid");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidOperationException_Error()
|
||||
{
|
||||
Assert.ThrowsAny<InvalidOperationException>(() => Context.RenderComponent<BootstrapInputNumber<string>>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Formatter_Ok()
|
||||
{
|
||||
var cut = Context.RenderComponent<BootstrapInputNumber<decimal>>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Value, 10.01m);
|
||||
pb.Add(a => a.Formatter, v => $"{v + 1}");
|
||||
});
|
||||
var input = cut.Find("input");
|
||||
Assert.Equal("11.01", input.GetAttribute("value"));
|
||||
|
||||
cut.SetParametersAndRender(pb =>
|
||||
{
|
||||
pb.Add(a => a.Formatter, null);
|
||||
pb.Add(a => a.FormatString, "#0.0");
|
||||
});
|
||||
Assert.Equal("10.0", input.GetAttribute("value"));
|
||||
|
||||
input = cut.Find("input");
|
||||
cut.InvokeAsync(() => input.Change(""));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Formatter_Error()
|
||||
{
|
||||
Assert.ThrowsAny<InvalidOperationException>(() => Context.RenderComponent<MockInputNumber>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShowButton_Ok()
|
||||
{
|
||||
var inc = false;
|
||||
var dec = false;
|
||||
var cut = Context.RenderComponent<BootstrapInputNumber<int>>(pb =>
|
||||
{
|
||||
pb.Add(a => a.ShowButton, true);
|
||||
pb.Add(a => a.OnIncrement, v =>
|
||||
{
|
||||
dec = true;
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
pb.Add(a => a.OnDecrement, v =>
|
||||
{
|
||||
inc = true;
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
});
|
||||
cut.Contains("class=\"input-group\"");
|
||||
|
||||
var buttons = cut.FindAll("button");
|
||||
cut.InvokeAsync(() => buttons[0].Click());
|
||||
Assert.True(inc);
|
||||
|
||||
cut.InvokeAsync(() => buttons[1].Click());
|
||||
Assert.True(dec);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(short))]
|
||||
[InlineData(typeof(int))]
|
||||
[InlineData(typeof(long))]
|
||||
[InlineData(typeof(float))]
|
||||
[InlineData(typeof(double))]
|
||||
[InlineData(typeof(decimal))]
|
||||
public void Type_Ok(Type t)
|
||||
{
|
||||
var cut = Context.Render(builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(BootstrapInputNumber<>).MakeGenericType(t));
|
||||
builder.AddAttribute(1, "ShowButton", true);
|
||||
builder.AddAttribute(1, "Min", "-10");
|
||||
builder.AddAttribute(1, "Max", "10");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
var buttons = cut.FindAll("button");
|
||||
cut.InvokeAsync(() => buttons[0].Click());
|
||||
cut.InvokeAsync(() => buttons[1].Click());
|
||||
}
|
||||
|
||||
private class Cat
|
||||
{
|
||||
[Range(1, 10)]
|
||||
public int Count { get; set; }
|
||||
}
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
private class MockInputNumber : BootstrapInputNumber<string>
|
||||
{
|
||||
public override Task SetParametersAsync(ParameterView parameters)
|
||||
{
|
||||
parameters.SetParameterProperties(this);
|
||||
|
||||
OnInitialized();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected override string? InternalFormat(string value)
|
||||
{
|
||||
return base.InternalFormat(value);
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
|
||||
InternalFormat("");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user