mirror of
https://gitee.com/LongbowEnterprise/BootstrapBlazor.git
synced 2024-12-05 05:29:47 +08:00
!2389 test(#I4SMH1): add TabLink unit test
* test: 恢复默认导航 * test: 提高代码覆盖率 * refactor: 重构 Tab 代码提高代码覆盖率 * test: 增加 ActiveTab 单元测试 * refactor: 增加代码覆盖率 * test: 增加代码覆盖率 * refactor: 重构逻辑 * test: 增加 Key 单元测试 * test: TabItem 代码覆盖率 100% * test: 增加 ChildContent 单元测试 * test: 增加 Click 单元测试 * test: 增加 TabLink 单元测试
This commit is contained in:
parent
bfb2fc1d77
commit
1073ac8c7f
@ -170,13 +170,6 @@ public partial class Tab
|
||||
[NotNull]
|
||||
public string? CloseCurrentTabText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 空白 Tab 显示文字
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
[NotNull]
|
||||
public string? NullTabText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 关闭所有 TabItem 菜单文本
|
||||
/// </summary>
|
||||
@ -256,44 +249,24 @@ public partial class Tab
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckUrl(string url)
|
||||
{
|
||||
var ret = false;
|
||||
foreach (var rule in ExcludeUrls ?? Enumerable.Empty<string>())
|
||||
{
|
||||
var checkUrl = rule.TrimStart('/');
|
||||
var startIndex = checkUrl.IndexOf("?");
|
||||
if (startIndex > 0)
|
||||
{
|
||||
checkUrl = checkUrl[..startIndex];
|
||||
if (url.StartsWith(checkUrl, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (url.Equals(checkUrl, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void AddTabByUrl()
|
||||
{
|
||||
var requestUrl = Navigator.ToBaseRelativePath(Navigator.Uri);
|
||||
|
||||
// 判断是否排除
|
||||
Excluded = CheckUrl(requestUrl);
|
||||
|
||||
var urls = ExcludeUrls ?? Enumerable.Empty<string>();
|
||||
if (requestUrl == "")
|
||||
{
|
||||
Excluded = urls.Any(u => u == "" || u == "/");
|
||||
}
|
||||
else
|
||||
{
|
||||
Excluded = urls.Any(u => u != "/" && requestUrl.StartsWith(u.TrimStart('/'), StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
if (!Excluded)
|
||||
{
|
||||
var tab = Items.FirstOrDefault(tab => tab.Url?.Equals(requestUrl, StringComparison.OrdinalIgnoreCase) ?? false);
|
||||
// 地址相同参数不同需要重新渲染 TabItem
|
||||
var tab = Items.FirstOrDefault(tab => tab.Url.TrimStart('/').Equals(requestUrl, StringComparison.OrdinalIgnoreCase));
|
||||
if (tab != null)
|
||||
{
|
||||
ActiveTabItem(tab);
|
||||
@ -366,7 +339,7 @@ public partial class Tab
|
||||
item = Items.ElementAt(index);
|
||||
if (ClickTabToNavigation)
|
||||
{
|
||||
Navigator.NavigateTo(item.Url!);
|
||||
Navigator.NavigateTo(item.Url);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -404,7 +377,7 @@ public partial class Tab
|
||||
|
||||
if (ClickTabToNavigation)
|
||||
{
|
||||
Navigator.NavigateTo(item.Url!);
|
||||
Navigator.NavigateTo(item.Url);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -523,31 +496,18 @@ public partial class Tab
|
||||
{
|
||||
var text = Options.Text;
|
||||
var icon = Options.Icon ?? string.Empty;
|
||||
var active = Options.IsActive ?? true;
|
||||
var closable = Options.Closable ?? true;
|
||||
var active = Options.IsActive;
|
||||
var closable = Options.Closable;
|
||||
Options.Reset();
|
||||
|
||||
parameters.Add(nameof(TabItem.Url), url);
|
||||
parameters.Add(nameof(TabItem.Icon), icon);
|
||||
parameters.Add(nameof(TabItem.Closable), closable);
|
||||
parameters.Add(nameof(TabItem.IsActive), active);
|
||||
parameters.Add(nameof(TabItem.Text), GetTabText(text, context.Segments));
|
||||
parameters.Add(nameof(TabItem.Text), text);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetTabText(string? text, string[]? segments)
|
||||
{
|
||||
if (NullTabText == null)
|
||||
{
|
||||
var t = Localizer[nameof(NullTabText)];
|
||||
if (!t.ResourceNotFound)
|
||||
{
|
||||
NullTabText = t.Value;
|
||||
}
|
||||
}
|
||||
return text ?? NullTabText ?? segments?.FirstOrDefault() ?? "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加 TabItem 方法
|
||||
/// </summary>
|
||||
@ -596,7 +556,7 @@ public partial class Tab
|
||||
{
|
||||
if (ClickTabToNavigation)
|
||||
{
|
||||
Navigator.NavigateTo(activeItem.Url!);
|
||||
Navigator.NavigateTo(activeItem.Url);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -21,6 +21,7 @@ public class TabItem : ComponentBase
|
||||
/// 获得/设置 请求地址
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
[NotNull]
|
||||
public string? Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@ -72,6 +73,7 @@ public class TabItem : ComponentBase
|
||||
{
|
||||
base.OnInitialized();
|
||||
|
||||
Url ??= "";
|
||||
TabSet?.AddItem(this);
|
||||
}
|
||||
|
||||
|
@ -21,13 +21,12 @@ public static class NavigationManagerExtensions
|
||||
/// <param name="text"></param>
|
||||
/// <param name="icon"></param>
|
||||
/// <param name="closable"></param>
|
||||
public static void NavigateTo(this NavigationManager navigation, IServiceProvider provider, string url, string text, string? icon = null, bool? closable = null)
|
||||
public static void NavigateTo(this NavigationManager navigation, IServiceProvider provider, string url, string text, string? icon = null, bool closable = true)
|
||||
{
|
||||
var option = provider.GetRequiredService<TabItemTextOptions>();
|
||||
option.Text = text;
|
||||
option.Icon = icon;
|
||||
option.IsActive = true;
|
||||
option.Closable = closable ?? true;
|
||||
option.Closable = closable;
|
||||
navigation.NavigateTo(url);
|
||||
}
|
||||
}
|
||||
|
@ -20,29 +20,29 @@ internal class TabItemTextOptions
|
||||
public string? Icon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 是否激活 默认为 null
|
||||
/// 获得/设置 是否激活 默认为 true
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public bool? IsActive { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 当前 TabItem 是否可关闭 默认为 null
|
||||
/// 获得/设置 当前 TabItem 是否可关闭 默认为 true
|
||||
/// </summary>
|
||||
public bool? Closable { get; set; }
|
||||
public bool Closable { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// 重置方法
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
Text = null;
|
||||
Icon = null;
|
||||
IsActive = null;
|
||||
Closable = null;
|
||||
IsActive = true;
|
||||
Closable = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// 是否可用方法
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool Valid() => Text != null;
|
||||
|
@ -211,6 +211,10 @@ public class LayoutTest : BootstrapBlazorTestBase
|
||||
});
|
||||
navMan.NavigateTo("/");
|
||||
Assert.Equal("http://localhost/Test", navMan.Uri);
|
||||
cut.SetParametersAndRender(pb =>
|
||||
{
|
||||
pb.Add(a => a.OnAuthorizing, null);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
38
test/UnitTest/Components/TabLinkTest.cs
Normal file
38
test/UnitTest/Components/TabLinkTest.cs
Normal file
@ -0,0 +1,38 @@
|
||||
// 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/
|
||||
|
||||
namespace UnitTest.Components;
|
||||
|
||||
public class TabLinkTest : BootstrapBlazorTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void Click_Ok()
|
||||
{
|
||||
var clicked = false;
|
||||
var cut = Context.RenderComponent<TabLink>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Url, "/Cat");
|
||||
pb.Add(a => a.Text, "Cat");
|
||||
pb.Add(a => a.Icon, "fa fa-fa");
|
||||
pb.Add(a => a.Closable, false);
|
||||
pb.Add(a => a.OnClick, () =>
|
||||
{
|
||||
clicked = true;
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
});
|
||||
cut.Find("a").Click();
|
||||
Assert.True(clicked);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChildContent_Ok()
|
||||
{
|
||||
var cut = Context.RenderComponent<TabLink>(pb =>
|
||||
{
|
||||
pb.Add(a => a.ChildContent, builder => builder.AddContent(0, "Body"));
|
||||
});
|
||||
Assert.Contains("Body", cut.Markup);
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
using Bunit.TestDoubles;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Reflection;
|
||||
using UnitTest.Misc;
|
||||
|
||||
namespace UnitTest.Components;
|
||||
|
||||
@ -27,6 +28,20 @@ public class TabTest : BootstrapBlazorTestBase
|
||||
});
|
||||
});
|
||||
Assert.Contains("Tab1-Content", cut.Markup);
|
||||
Assert.Equal("TabItem-Key", cut.FindComponent<TabItem>().Instance.Key);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TabItemCreate_Ok()
|
||||
{
|
||||
TabItem.Create(new Dictionary<string, object?>()
|
||||
{
|
||||
["Url"] = null
|
||||
});
|
||||
TabItem.Create(new Dictionary<string, object?>()
|
||||
{
|
||||
["Url"] = new NullString()
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -34,7 +49,6 @@ public class TabTest : BootstrapBlazorTestBase
|
||||
{
|
||||
var cut = Context.RenderComponent<Tab>(pb =>
|
||||
{
|
||||
pb.Add(a => a.ShowExtendButtons, true);
|
||||
pb.Add(a => a.Placement, Placement.Left);
|
||||
pb.Add(a => a.Height, 100);
|
||||
});
|
||||
@ -68,6 +82,9 @@ public class TabTest : BootstrapBlazorTestBase
|
||||
var clicked = false;
|
||||
var cut = Context.RenderComponent<Tab>(pb =>
|
||||
{
|
||||
pb.Add(a => a.ShowExtendButtons, true);
|
||||
pb.Add(a => a.Placement, Placement.Bottom);
|
||||
pb.Add(a => a.ShowClose, true);
|
||||
pb.Add(a => a.OnClickTab, item =>
|
||||
{
|
||||
clicked = true;
|
||||
@ -92,13 +109,56 @@ public class TabTest : BootstrapBlazorTestBase
|
||||
cut.Find(".tabs-item").Click();
|
||||
Assert.True(clicked);
|
||||
|
||||
var tab = cut.Instance;
|
||||
cut.InvokeAsync(() => tab.ClickNextTab());
|
||||
// Click Prev
|
||||
var button = cut.Find(".nav-link-bar.left");
|
||||
button.Click();
|
||||
button.Click();
|
||||
button.Click();
|
||||
Assert.Equal("Tab1-Content", cut.Find(".tabs-body .d-none").InnerHtml);
|
||||
|
||||
cut.InvokeAsync(() => tab.ClickPrevTab());
|
||||
cut.InvokeAsync(() => tab.CloseCurrentTab());
|
||||
cut.InvokeAsync(() => tab.CloseAllTabs());
|
||||
// Click Next
|
||||
button = cut.Find(".nav-link-bar.right");
|
||||
button.Click();
|
||||
button.Click();
|
||||
button.Click();
|
||||
Assert.Equal("Tab2-Content", cut.Find(".tabs-body .d-none").InnerHtml);
|
||||
|
||||
// Close
|
||||
button = cut.Find(".tabs-item-close");
|
||||
button.Click();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClickTabToNavigation_True()
|
||||
{
|
||||
var cut = Context.RenderComponent<Tab>(pb =>
|
||||
{
|
||||
pb.Add(a => a.AdditionalAssemblies, new Assembly[] { GetType().Assembly });
|
||||
pb.Add(a => a.ShowExtendButtons, true);
|
||||
pb.Add(a => a.Placement, Placement.Top);
|
||||
pb.Add(a => a.ClickTabToNavigation, true);
|
||||
pb.Add(a => a.ShowClose, true);
|
||||
pb.Add(a => a.DefaultUrl, "/");
|
||||
});
|
||||
cut.InvokeAsync(() => cut.Instance.AddTab("/", "Index"));
|
||||
cut.InvokeAsync(() => cut.Instance.AddTab("/Cat", null!));
|
||||
|
||||
// Click Prev
|
||||
var button = cut.Find(".nav-link-bar.left");
|
||||
button.Click();
|
||||
|
||||
// Click Next
|
||||
button = cut.Find(".nav-link-bar.right");
|
||||
button.Click();
|
||||
|
||||
button = cut.Find(".tabs-item-close");
|
||||
button.Click();
|
||||
|
||||
// Close Current
|
||||
cut.InvokeAsync(() => cut.Instance.CloseAllTabs());
|
||||
|
||||
button = cut.Find(".dropdown-item");
|
||||
button.Click();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -132,25 +192,61 @@ public class TabTest : BootstrapBlazorTestBase
|
||||
public void AddTabByUrl_Ok()
|
||||
{
|
||||
var navMan = Context.Services.GetRequiredService<FakeNavigationManager>();
|
||||
var cut = Context.RenderComponent<Tab>(pb =>
|
||||
{
|
||||
pb.Add(a => a.AdditionalAssemblies, new Assembly[] { GetType().Assembly });
|
||||
pb.Add(a => a.ClickTabToNavigation, true);
|
||||
});
|
||||
navMan.NavigateTo("/");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExcludeUrls_Ok()
|
||||
{
|
||||
var navMan = Context.Services.GetRequiredService<FakeNavigationManager>();
|
||||
var cut = Context.RenderComponent<Tab>(pb =>
|
||||
{
|
||||
pb.Add(a => a.AdditionalAssemblies, new Assembly[] { GetType().Assembly });
|
||||
pb.Add(a => a.ClickTabToNavigation, true);
|
||||
pb.Add(a => a.ExcludeUrls, new String[] { "/Cat" });
|
||||
});
|
||||
|
||||
navMan.NavigateTo("/");
|
||||
cut.SetParametersAndRender(pb =>
|
||||
{
|
||||
pb.Add(a => a.ExcludeUrls, new String[] { "/" });
|
||||
});
|
||||
|
||||
navMan.NavigateTo("/");
|
||||
cut.SetParametersAndRender(pb =>
|
||||
{
|
||||
pb.Add(a => a.ExcludeUrls, new String[] { "" });
|
||||
});
|
||||
|
||||
navMan.NavigateTo("/Cat");
|
||||
cut.SetParametersAndRender(pb =>
|
||||
{
|
||||
pb.Add(a => a.ExcludeUrls, new String[] { "/", "Cat" });
|
||||
});
|
||||
|
||||
navMan.NavigateTo("/");
|
||||
cut.InvokeAsync(() => cut.Instance.AddTab(new Dictionary<string, object?>
|
||||
{
|
||||
["Text"] = "Cat",
|
||||
["Url"] = "Cat"
|
||||
}));
|
||||
cut.SetParametersAndRender(pb =>
|
||||
{
|
||||
pb.Add(a => a.ExcludeUrls, new String[] { "/Test" });
|
||||
});
|
||||
cut.InvokeAsync(() => cut.Instance.CloseCurrentTab());
|
||||
|
||||
// AddTab
|
||||
cut.InvokeAsync(() => cut.Instance.AddTab(new Dictionary<string, object?>
|
||||
{
|
||||
["Text"] = "Cat",
|
||||
["Url"] = null,
|
||||
["IsActive"] = true
|
||||
}));
|
||||
cut.SetParametersAndRender(pb =>
|
||||
{
|
||||
pb.Add(a => a.ExcludeUrls, new String[] { "/Test" });
|
||||
});
|
||||
|
||||
// Remove Tab
|
||||
var item = cut.Instance.GetActiveTab();
|
||||
Assert.NotNull(item);
|
||||
cut.InvokeAsync(() => cut.Instance.RemoveTab(item!));
|
||||
item = cut.Instance.GetActiveTab();
|
||||
Assert.Null(item);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -158,7 +254,82 @@ public class TabTest : BootstrapBlazorTestBase
|
||||
{
|
||||
var cut = Context.RenderComponent<Tab>(pb =>
|
||||
{
|
||||
pb.Add(a => a.AdditionalAssemblies, new Assembly[] { GetType().Assembly });
|
||||
pb.Add(a => a.DefaultUrl, "/");
|
||||
});
|
||||
var item = cut.Instance.GetActiveTab();
|
||||
Assert.Contains("Index", cut.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsOnlyRenderActiveTab_Ok()
|
||||
{
|
||||
var cut = Context.RenderComponent<Tab>(pb =>
|
||||
{
|
||||
pb.Add(a => a.AdditionalAssemblies, new Assembly[] { GetType().Assembly });
|
||||
pb.Add(a => a.IsOnlyRenderActiveTab, true);
|
||||
pb.AddChildContent<TabItem>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Text, "Tab1");
|
||||
pb.Add(a => a.Url, "/Cat");
|
||||
pb.Add(a => a.ChildContent, "Tab1-Content");
|
||||
});
|
||||
pb.AddChildContent<TabItem>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Text, "Tab2");
|
||||
pb.Add(a => a.Url, "/");
|
||||
pb.Add(a => a.Closable, false);
|
||||
pb.Add(a => a.ChildContent, "Tab2-Content");
|
||||
});
|
||||
});
|
||||
Assert.Equal(1, cut.FindAll(".tabs-body-content").Count);
|
||||
|
||||
// 提高代码覆盖率
|
||||
cut.InvokeAsync(() => cut.Instance.CloseOtherTabs());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ActiveTab_Ok()
|
||||
{
|
||||
var cut = Context.RenderComponent<Tab>(pb =>
|
||||
{
|
||||
pb.Add(a => a.AdditionalAssemblies, new Assembly[] { GetType().Assembly });
|
||||
pb.Add(a => a.DefaultUrl, "/");
|
||||
});
|
||||
cut.InvokeAsync(() => cut.Instance.ActiveTab(0));
|
||||
var item = cut.Instance.GetActiveTab();
|
||||
Assert.NotNull(item);
|
||||
cut.InvokeAsync(() =>
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
cut.Instance.ActiveTab(item);
|
||||
}
|
||||
});
|
||||
cut.InvokeAsync(() => cut.Instance.RemoveTab(item!));
|
||||
item = cut.Instance.GetActiveTab();
|
||||
Assert.Null(item);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NavigationActiveTab_Ok()
|
||||
{
|
||||
var navMan = Context.Services.GetRequiredService<FakeNavigationManager>();
|
||||
navMan.NavigateTo("/");
|
||||
var cut = Context.RenderComponent<Tab>(pb =>
|
||||
{
|
||||
pb.Add(a => a.AdditionalAssemblies, new Assembly[] { GetType().Assembly });
|
||||
pb.Add(a => a.ClickTabToNavigation, true);
|
||||
pb.AddChildContent<TabItem>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Text, "Tab1");
|
||||
pb.Add(a => a.Url, "/Cat");
|
||||
});
|
||||
pb.AddChildContent<TabItem>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Text, "Tab2");
|
||||
pb.Add(a => a.Url, "/");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
10
test/UnitTest/Misc/NullString.cs
Normal file
10
test/UnitTest/Misc/NullString.cs
Normal file
@ -0,0 +1,10 @@
|
||||
// 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/
|
||||
|
||||
namespace UnitTest.Misc;
|
||||
|
||||
internal class NullString
|
||||
{
|
||||
public override string? ToString() => null;
|
||||
}
|
@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Components.Rendering;
|
||||
namespace UnitTest.Pages;
|
||||
|
||||
[Route("/Cat")]
|
||||
[TabItemOption(Icon = "fa fa-fa", Closable = true, Text = "Cat")]
|
||||
public class Cat : ComponentBase
|
||||
{
|
||||
protected override void BuildRenderTree(RenderTreeBuilder builder)
|
||||
|
Loading…
Reference in New Issue
Block a user