!2391 test(#I4SMS6): add Select unit test

* test: 增加 OnBeforeSelectedItemChange 单元测试
* test: 增加 ItemTemplate 单元测试
* test: 临时注销
* test: 增加 Validate 单元测试
* test: 增加 Color  单元测试
* test: 增加 OnSelectedItemChanged 单元测试
* refactor: 格式化文档
* refactor: 增加空判断
* refactor: 重构代码精简逻辑
* revert: 撤销更改
* test: 增加枚举值类型单元测试
* test: 增加 SelectOption 单元测试
* test: 增加 Options 模板单元测试
* test: 增加 OnSearchTextChanged 单元测试
* fix: 修复参数丢失问题
* refactor: 更改为可为空类型
* refactor: 更新注释文档
* refactor: 移除不使用的属性
* doc: 更新资源文件
* refactor: 移除 IsDisabled 条件判断
* refactor: 移除 Enum 自动生成候选项逻辑
* refactor: 精简 placeholder 逻辑
* test: 重构代码消除警告信息
* test: 增加 Select 单元测试
This commit is contained in:
Argo 2022-02-06 08:02:19 +00:00 committed by Argo-Lenovo
parent 9a9d9001d0
commit 3d50fcedd1
10 changed files with 287 additions and 91 deletions

View File

@ -1438,7 +1438,7 @@
"Block3Intro": "The values in the text box change as you change the drop-down option by binding the <code>Model.Name</code> property to the component with <code>Select </code> .",
"Block4Title": "Select cascading binding",
"Block4Intro": "The second drop-down box dynamically populates the content by selecting the different options for the first drop-down box.",
"ButtonText1": "Example of a window intermediate joint",
"ButtonText1": "In Dialog",
"Block5Title": "Select client validation",
"Block5Intro": "When the drop-down box is not selected, it is blocked when the submit button is clicked.",
"Option1": "Please select ...",

View File

@ -15,6 +15,7 @@ public abstract class IdComponentBase : BootstrapComponentBase
/// 获得/设置 组件 id 属性
/// </summary>
[Parameter]
[NotNull]
public virtual string? Id { get; set; }
[Inject]

View File

@ -192,10 +192,13 @@ public abstract class LayoutBase : BootstrapComponentBase, IAsyncDisposable
private async void Navigation_LocationChanged(object? sender, LocationChangedEventArgs e)
{
var auth = await OnAuthorizing!(e.Location);
if (!auth)
if (OnAuthorizing != null)
{
Navigation.NavigateTo(NotAuthorizeUrl, true);
var auth = await OnAuthorizing(e.Location);
if (!auth)
{
Navigation.NavigateTo(NotAuthorizeUrl, true);
}
}
}

View File

@ -109,7 +109,7 @@ public partial class Select<TValue> : ISelect
/// <summary>
/// 获得/设置 Select 内部 Input 组件 Id
/// </summary>
private string? InputId => string.IsNullOrEmpty(Id) ? null : $"{Id}_input";
private string? InputId => $"{Id}_input";
/// <summary>
/// 获得/设置 搜索文字
@ -139,19 +139,7 @@ public partial class Select<TValue> : ISelect
{
base.OnParametersSet();
if (NullableUnderlyingType != null)
{
if (string.IsNullOrEmpty(PlaceHolder))
{
// 设置 placeholder
if (AdditionalAttributes != null && AdditionalAttributes.TryGetValue("placeholder", out var pl))
{
PlaceHolder = pl?.ToString();
AdditionalAttributes.Remove("placeholder");
}
}
PlaceHolder ??= Localizer[nameof(PlaceHolder)];
}
PlaceHolder ??= Localizer[nameof(PlaceHolder)];
// 内置对枚举类型的支持
var t = NullableUnderlyingType ?? typeof(TValue);
@ -164,12 +152,6 @@ public partial class Select<TValue> : ISelect
private void ResetSelectedItem()
{
// 合并 Items 与 Options 集合
if (!Items.Any() && typeof(TValue).IsEnum())
{
Items = typeof(TValue).ToSelectList();
}
if (string.IsNullOrEmpty(SearchText))
{
DataSource = Items.ToList();
@ -231,39 +213,36 @@ public partial class Select<TValue> : ISelect
/// </summary>
private async Task OnItemClick(SelectedItem item)
{
if (!IsDisabled && !item.IsDisabled)
var ret = true;
if (OnBeforeSelectedItemChange != null)
{
var ret = true;
if (OnBeforeSelectedItemChange != null)
{
ret = await OnBeforeSelectedItemChange(item);
if (ret)
{
// 返回 True 弹窗提示
var option = new SwalOption()
{
Category = SwalCategory,
Title = SwalTitle,
Content = SwalContent,
IsConfirm = true
};
if (!string.IsNullOrEmpty(SwalFooter))
{
option.ShowFooter = true;
option.FooterTemplate = builder => builder.AddContent(0, SwalFooter);
}
ret = await SwalService.ShowModal(option);
}
else
{
// 返回 False 直接运行
ret = true;
}
}
ret = await OnBeforeSelectedItemChange(item);
if (ret)
{
await ItemChanged(item);
// 返回 True 弹窗提示
var option = new SwalOption()
{
Category = SwalCategory,
Title = SwalTitle,
Content = SwalContent,
IsConfirm = true
};
if (!string.IsNullOrEmpty(SwalFooter))
{
option.ShowFooter = true;
option.FooterTemplate = builder => builder.AddContent(0, SwalFooter);
}
ret = await SwalService.ShowModal(option);
}
else
{
// 返回 False 直接运行
ret = true;
}
}
if (ret)
{
await ItemChanged(item);
}
}

View File

@ -17,31 +17,31 @@ public abstract class SelectBase<TValue> : ValidateBase<TValue>
protected SelectedItem? SelectedItem { get; set; }
/// <summary>
/// 获得/设置 按钮颜色
/// 获得/设置 颜色 默认 Color.None 无设置
/// </summary>
[Parameter]
public Color Color { get; set; } = Color.None;
public Color Color { get; set; }
/// <summary>
/// 获得/设置 SWal 图标
/// 获得/设置 Swal 图标 默认 Question
/// </summary>
[Parameter]
public SwalCategory SwalCategory { get; set; } = SwalCategory.Question;
/// <summary>
/// 获得/设置 Swal 标题
/// 获得/设置 Swal 标题 默认 null
/// </summary>
[Parameter]
public string? SwalTitle { get; set; }
/// <summary>
/// 获得/设置 Swal 内容
/// 获得/设置 Swal 内容 默认 null
/// </summary>
[Parameter]
public string? SwalContent { get; set; }
/// <summary>
/// 获得/设置 是否显示 Swal Footer
/// 获得/设置 Footer 默认 null
/// </summary>
[Parameter]
public string? SwalFooter { get; set; }

View File

@ -9,28 +9,28 @@ namespace BootstrapBlazor.Components;
/// <summary>
/// SelectOption 组件
/// </summary>
public partial class SelectOption : ComponentBase
public class SelectOption : ComponentBase
{
/// <summary>
/// 获得/设置 显示名称
/// </summary>
[Parameter]
public string Text { get; set; } = "";
public string? Text { get; set; }
/// <summary>
/// 获得/设置 选项值
/// </summary>
[Parameter]
public string Value { get; set; } = "";
public string? Value { get; set; }
/// <summary>
/// 获得/设置 是否选中
/// 获得/设置 是否选中 默认 false
/// </summary>
[Parameter]
public bool Active { get; set; }
/// <summary>
/// 获得/设置 是否禁用
/// 获得/设置 是否禁用 默认 false
/// </summary>
[Parameter]
public bool IsDisabled { get; set; }
@ -39,7 +39,7 @@ public partial class SelectOption : ComponentBase
/// 获得/设置 分组名称
/// </summary>
[Parameter]
public string GroupName { get; set; } = "";
public string? GroupName { get; set; }
/// <summary>
/// 父组件通过级联参数获得
@ -60,9 +60,9 @@ public partial class SelectOption : ComponentBase
private SelectedItem ToSelectedItem() => new()
{
Active = Active,
GroupName = GroupName,
Text = Text,
Value = Value,
GroupName = GroupName ?? "",
Text = Text ?? "",
Value = Value ?? "",
IsDisabled = IsDisabled
};
}

View File

@ -62,11 +62,6 @@ public class SwalOption : PopupOptionBase
/// <remarks>此属性给模态弹窗时使用</remarks>
public bool IsConfirm { get; set; }
/// <summary>
/// 获得/设置 是否保持弹窗内组件状态 默认为 false 不保持
/// </summary>
public bool KeepChildrenState { get; set; }
/// <summary>
/// 获得/设置 按钮模板
/// </summary>

View File

@ -241,12 +241,12 @@ public class DialogTest : BootstrapBlazorTestBase
Assert.Equal(2, cut.FindComponents<ModalDialog>().Count);
// 关闭第二个弹窗
var btnClose = cut.FindAll(".btn-close").Last();
var btnClose = cut.FindAll(".btn-close")[cut.FindAll(".btn-close").Count - 1];
btnClose.Click();
Assert.Equal(1, cut.FindComponents<ModalDialog>().Count);
// 关闭第一个弹窗
btnClose = cut.FindAll(".btn-close").Last();
btnClose = cut.FindAll(".btn-close")[cut.FindAll(".btn-close").Count - 1];
btnClose.Click();
Assert.Equal(0, cut.FindComponents<ModalDialog>().Count);
#endregion
@ -257,7 +257,7 @@ public class DialogTest : BootstrapBlazorTestBase
FullScreenSize = FullScreenSize.Large
}));
Assert.Contains("modal-fullscreen-lg-down", cut.Markup);
btnClose = cut.FindAll(".btn-close").Last();
btnClose = cut.FindAll(".btn-close")[cut.FindAll(".btn-close").Count - 1];
btnClose.Click();
#endregion
@ -267,7 +267,7 @@ public class DialogTest : BootstrapBlazorTestBase
IsCentered = true
}));
Assert.Contains("modal-dialog-centered", cut.Markup);
btnClose = cut.FindAll(".btn-close").Last();
btnClose = cut.FindAll(".btn-close")[cut.FindAll(".btn-close").Count - 1];
btnClose.Click();
cut.InvokeAsync(() => dialog.Show(new DialogOption()
@ -275,7 +275,7 @@ public class DialogTest : BootstrapBlazorTestBase
IsCentered = false
}));
Assert.DoesNotContain("modal-dialog-centered", cut.Markup);
btnClose = cut.FindAll(".btn-close").Last();
btnClose = cut.FindAll(".btn-close")[cut.FindAll(".btn-close").Count - 1];
btnClose.Click();
#endregion
@ -285,7 +285,7 @@ public class DialogTest : BootstrapBlazorTestBase
IsKeyboard = true
}));
Assert.Contains("data-bs-keyboard=\"true\"", cut.Markup);
btnClose = cut.FindAll(".btn-close").Last();
btnClose = cut.FindAll(".btn-close")[cut.FindAll(".btn-close").Count - 1];
btnClose.Click();
cut.InvokeAsync(() => dialog.Show(new DialogOption()
@ -293,7 +293,7 @@ public class DialogTest : BootstrapBlazorTestBase
IsKeyboard = false
}));
Assert.DoesNotContain("data-bs-keyboard\"false\"", cut.Markup);
btnClose = cut.FindAll(".btn-close").Last();
btnClose = cut.FindAll(".btn-close")[cut.FindAll(".btn-close").Count - 1];
btnClose.Click();
#endregion
@ -302,7 +302,7 @@ public class DialogTest : BootstrapBlazorTestBase
{
ShowHeaderCloseButton = true
}));
btnClose = cut.FindAll(".btn-close").Last();
btnClose = cut.FindAll(".btn-close")[cut.FindAll(".btn-close").Count - 1];
btnClose.Click();
cut.InvokeAsync(() => dialog.Show(new DialogOption()
@ -310,7 +310,7 @@ public class DialogTest : BootstrapBlazorTestBase
ShowHeaderCloseButton = false
}));
Assert.DoesNotContain("btn-close", cut.Markup);
btnClose = cut.FindAll(".btn-secondary").Last();
btnClose = cut.FindAll(".btn-secondary")[cut.FindAll(".btn-secondary").Count - 1];
btnClose.Click();
#endregion
@ -320,7 +320,7 @@ public class DialogTest : BootstrapBlazorTestBase
ShowPrintButton = true
}));
Assert.Contains("btn-print", cut.Markup);
btnClose = cut.FindAll(".btn-close").Last();
btnClose = cut.FindAll(".btn-close")[cut.FindAll(".btn-close").Count - 1];
btnClose.Click();
cut.InvokeAsync(() => dialog.Show(new DialogOption()
@ -328,7 +328,7 @@ public class DialogTest : BootstrapBlazorTestBase
ShowPrintButton = false
}));
Assert.DoesNotContain("btn-print", cut.Markup);
btnClose = cut.FindAll(".btn-close").Last();
btnClose = cut.FindAll(".btn-close")[cut.FindAll(".btn-close").Count - 1];
btnClose.Click();
cut.InvokeAsync(() => dialog.Show(new DialogOption()
@ -339,7 +339,7 @@ public class DialogTest : BootstrapBlazorTestBase
}));
Assert.Contains("btn-print", cut.Markup);
Assert.Contains("Print-Test", cut.Markup);
btnClose = cut.FindAll(".btn-close").Last();
btnClose = cut.FindAll(".btn-close")[cut.FindAll(".btn-close").Count - 1];
btnClose.Click();
#endregion
@ -353,7 +353,7 @@ public class DialogTest : BootstrapBlazorTestBase
}));
Assert.Contains("Save-Test", cut.Markup);
Assert.Contains("Close-Test", cut.Markup);
btnClose = cut.FindAll(".btn-close").Last();
btnClose = cut.FindAll(".btn-close")[cut.FindAll(".btn-close").Count - 1];
btnClose.Click();
#endregion
@ -370,7 +370,7 @@ public class DialogTest : BootstrapBlazorTestBase
return Task.FromResult(save);
}
}));
btnClose = cut.FindAll(".btn-primary").Last();
btnClose = cut.FindAll(".btn-primary")[cut.FindAll(".btn-primary").Count - 1];
btnClose.Click();
Assert.True(save);
#endregion

View File

@ -33,10 +33,10 @@ public class PrintTest : BootstrapBlazorTestBase
var printService = cut.Services.CreateScope().ServiceProvider.GetRequiredService<PrintService>();
Assert.ThrowsAsync<InvalidOperationException>(() => printService.PrintAsync<Button>(op =>
{
// 弹窗配置
op.Title = "数据查询窗口";
// 弹窗组件所需参数
return new Dictionary<string, object?>();
// 弹窗配置
op.Title = "数据查询窗口";
// 弹窗组件所需参数
return new Dictionary<string, object?>();
}));
}

View File

@ -0,0 +1,218 @@
// 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 UnitTest.Extensions;
namespace UnitTest.Components;
public class SelectTest : BootstrapBlazorTestBase
{
[Fact]
public void OnSearchTextChanged_Null()
{
var cut = Context.RenderComponent<BootstrapBlazorRoot>(pb =>
{
pb.AddChildContent<Select<string>>(pb =>
{
pb.Add(a => a.ShowSearch, true);
pb.Add(a => a.Items, new List<SelectedItem>()
{
new SelectedItem("1", "Test1"),
new SelectedItem("2", "Test2") { IsDisabled = true }
});
});
});
var ctx = cut.FindComponent<Select<string>>();
ctx.InvokeAsync(() => ctx.Instance.ConfirmSelectedItem(0));
// 搜索 T
ctx.Find(".search-text").Input("T");
ctx.InvokeAsync(() => ctx.Instance.ConfirmSelectedItem(0));
ctx.SetParametersAndRender(pb =>
{
pb.Add(a => a.OnBeforeSelectedItemChange, item => Task.FromResult(false));
pb.Add(a => a.OnSelectedItemChanged, item => Task.CompletedTask);
});
ctx.InvokeAsync(() => ctx.Instance.ConfirmSelectedItem(0));
}
[Fact]
public void Options_Ok()
{
var cut = Context.RenderComponent<Select<string>>(pb =>
{
pb.Add(a => a.Options, builder =>
{
builder.OpenComponent<SelectOption>(0);
builder.AddAttribute(1, "Text", "Test-Select");
builder.CloseComponent();
builder.OpenComponent<SelectOption>(2);
builder.CloseComponent();
});
});
Assert.Contains("Test-Select", cut.Markup);
}
[Fact]
public void SelectOption_Ok()
{
var cut = Context.RenderComponent<SelectOption>(pb =>
{
pb.Add(a => a.Text, "Test-SelectOption");
pb.Add(a => a.GroupName, "Test-GroupName");
pb.Add(a => a.IsDisabled, false);
pb.Add(a => a.Active, true);
pb.Add(a => a.Value, "");
});
}
[Fact]
public void Enum_Ok()
{
var cut = Context.RenderComponent<Select<EnumEducation>>();
Assert.Equal(2, cut.FindAll(".dropdown-item").Count);
}
[Fact]
public void NullableEnum_Ok()
{
var cut = Context.RenderComponent<Select<EnumEducation?>>(pb =>
{
pb.Add(a => a.AdditionalAttributes, new Dictionary<string, object>()
{
["placeholder"] = ""
});
});
Assert.Equal(3, cut.FindAll(".dropdown-item").Count);
}
[Fact]
public void OnSelectedItemChanged_OK()
{
var triggered = false;
// 首次加载触发 OnSelectedItemChanged 回调测试
var cut = Context.RenderComponent<Select<string>>(pb =>
{
pb.Add(a => a.Items, new SelectedItem[]
{
new SelectedItem("1", "Test1"),
new SelectedItem("2", "Test2")
});
pb.Add(a => a.Value, "2");
pb.Add(a => a.OnSelectedItemChanged, item =>
{
triggered = true;
return Task.CompletedTask;
});
});
Assert.True(triggered);
}
[Fact]
public void Color_Ok()
{
var cut = Context.RenderComponent<Select<string>>(pb =>
{
pb.Add(a => a.Color, Color.Danger);
});
Assert.Contains("border-danger", cut.Markup);
}
[Fact]
public void Validate_Ok()
{
var valid = false;
var invalid = false;
var model = new Foo() { Name = "Test-Select1" };
var cut = Context.RenderComponent<ValidateForm>(builder =>
{
builder.Add(a => a.OnValidSubmit, context =>
{
valid = true;
return Task.CompletedTask;
});
builder.Add(a => a.OnInvalidSubmit, context =>
{
invalid = true;
return Task.CompletedTask;
});
builder.Add(a => a.Model, model);
builder.AddChildContent<Select<string>>(pb =>
{
pb.Add(a => a.Value, model.Name);
pb.Add(a => a.OnValueChanged, v =>
{
model.Name = v;
return Task.CompletedTask;
});
pb.Add(a => a.ValueExpression, model.GenerateValueExpression());
pb.Add(a => a.Items, new SelectedItem[]
{
new SelectedItem("", "Test"),
new SelectedItem("1", "Test1") { GroupName = "Test1" },
new SelectedItem("2", "Test2") { GroupName = "Test2" }
});
});
});
var form = cut.Find("form");
form.Submit();
Assert.True(valid);
var ctx = cut.FindComponent<Select<string>>();
ctx.InvokeAsync(() => ctx.Instance.ConfirmSelectedItem(0));
form.Submit();
Assert.True(invalid);
}
[Fact]
public void ItemTemplate_Ok()
{
var cut = Context.RenderComponent<Select<string>>(pb =>
{
pb.Add(a => a.Items, new SelectedItem[]
{
new SelectedItem("1", "Test1") { GroupName = "Test1" },
new SelectedItem("2", "Test2") { GroupName = "Test2" }
});
pb.Add(a => a.Value, "2");
pb.Add(a => a.ItemTemplate, item => builder =>
{
builder.OpenElement(0, "div");
builder.AddContent(1, item.Text);
builder.CloseComponent();
});
});
cut.Find(".dropdown-item").Click();
}
[Fact]
public void OnBeforeSelectedItemChange_Ok()
{
var cut = Context.RenderComponent<BootstrapBlazorRoot>(pb =>
{
pb.AddChildContent<Select<string>>(pb =>
{
pb.Add(a => a.OnBeforeSelectedItemChange, item => Task.FromResult(true));
pb.Add(a => a.SwalFooter, "Test-Swal-Footer");
pb.Add(a => a.SwalCategory, SwalCategory.Question);
pb.Add(a => a.SwalTitle, "Test-Swal-Title");
pb.Add(a => a.SwalContent, "Test-Swal-Content");
pb.Add(a => a.Items, new SelectedItem[]
{
new SelectedItem("1", "Test1"),
new SelectedItem("2", "Test2")
});
});
});
cut.Find(".dropdown-item").Click();
//Assert.Contains("Test-Swal-Title", cut.Markup);
//Assert.Contains("Test-Swal-Content", cut.Markup);
//Assert.Contains("Test-Swal-Footer", cut.Markup);
}
}