feat(ui): List attribute editor in TemplateEdit
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
using Bunit;
|
||||
using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Design;
|
||||
|
||||
/// <summary>
|
||||
/// MV-13: the shared <c>AttributeListEditor</c> reveals an element-type select
|
||||
/// plus a repeatable list-value editor for structured List attributes. These are
|
||||
/// real bUnit rendering/interaction tests over the self-contained component, plus
|
||||
/// structural assertions pinning the TemplateEdit attribute-form wiring (the page
|
||||
/// itself is heavyweight to render — see <c>TemplateNativeAlarmSourceEditorTests</c>).
|
||||
/// </summary>
|
||||
public class AttributeListEditorTests : BunitContext
|
||||
{
|
||||
private static string TemplateEditMarkup
|
||||
{
|
||||
get
|
||||
{
|
||||
var dir = AppContext.BaseDirectory;
|
||||
for (var i = 0; i < 6 && dir is not null; i++)
|
||||
dir = Directory.GetParent(dir)?.FullName;
|
||||
return File.ReadAllText(Path.Combine(dir!, "src", "ZB.MOM.WW.ScadaBridge.CentralUI",
|
||||
"Components", "Pages", "Design", "TemplateEdit.razor"));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Editor_RendersElementTypeSelect_WithSixValidScalars()
|
||||
{
|
||||
var cut = Render<AttributeListEditor>(p => p
|
||||
.Add(x => x.ElementDataType, DataType.String)
|
||||
.Add(x => x.Rows, new List<string>()));
|
||||
|
||||
var select = cut.Find("select.form-select");
|
||||
var options = select.QuerySelectorAll("option");
|
||||
Assert.Equal(6, options.Length);
|
||||
var texts = options.Select(o => o.TextContent).ToArray();
|
||||
Assert.Contains("String", texts);
|
||||
Assert.Contains("Int32", texts);
|
||||
Assert.Contains("Float", texts);
|
||||
Assert.Contains("Double", texts);
|
||||
Assert.Contains("Boolean", texts);
|
||||
Assert.Contains("DateTime", texts);
|
||||
// List itself must never appear as an element type.
|
||||
Assert.DoesNotContain("List", texts);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShowElementType_False_HidesTheSelect()
|
||||
{
|
||||
var cut = Render<AttributeListEditor>(p => p
|
||||
.Add(x => x.ShowElementType, false)
|
||||
.Add(x => x.Rows, new List<string>()));
|
||||
|
||||
Assert.Throws<ElementNotFoundException>(() => cut.Find("select.form-select"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Editor_RendersOneInputPerRow()
|
||||
{
|
||||
var cut = Render<AttributeListEditor>(p => p
|
||||
.Add(x => x.ElementDataType, DataType.Int32)
|
||||
.Add(x => x.Rows, new List<string> { "1", "2", "3" }));
|
||||
|
||||
var inputs = cut.FindAll("input.form-control");
|
||||
Assert.Equal(3, inputs.Count);
|
||||
Assert.Equal("1", inputs[0].GetAttribute("value"));
|
||||
Assert.Equal("3", inputs[2].GetAttribute("value"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddElement_AppendsRow_AndRaisesRowsChanged()
|
||||
{
|
||||
var rows = new List<string> { "a" };
|
||||
List<string>? changed = null;
|
||||
var cut = Render<AttributeListEditor>(p => p
|
||||
.Add(x => x.Rows, rows)
|
||||
.Add(x => x.RowsChanged, r => changed = r));
|
||||
|
||||
cut.Find("button.btn-outline-secondary").Click();
|
||||
|
||||
Assert.NotNull(changed);
|
||||
Assert.Equal(2, changed!.Count);
|
||||
Assert.Equal("", changed[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveElement_DropsRow_AndRaisesRowsChanged()
|
||||
{
|
||||
var rows = new List<string> { "a", "b" };
|
||||
List<string>? changed = null;
|
||||
var cut = Render<AttributeListEditor>(p => p
|
||||
.Add(x => x.Rows, rows)
|
||||
.Add(x => x.RowsChanged, r => changed = r));
|
||||
|
||||
// First per-row Remove button.
|
||||
cut.FindAll("button.btn-outline-danger")[0].Click();
|
||||
|
||||
Assert.NotNull(changed);
|
||||
Assert.Single(changed!);
|
||||
Assert.Equal("b", changed![0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EncodedRows_RoundTripThroughCodec()
|
||||
{
|
||||
// Mirrors what TemplateEdit.SaveAttribute does on submit.
|
||||
var rows = new List<string> { "10", "20" };
|
||||
var json = AttributeValueCodec.Encode(rows);
|
||||
var decoded = AttributeValueCodec.Decode(json, DataType.List, DataType.Int32);
|
||||
var list = Assert.IsType<List<int>>(decoded);
|
||||
Assert.Equal(new[] { 10, 20 }, list);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TemplateEdit_RevealsListEditor_AndSendsElementType()
|
||||
{
|
||||
var markup = TemplateEditMarkup;
|
||||
// Conditional reveal on DataType.List.
|
||||
Assert.Contains("_attrDataType == DataType.List", markup);
|
||||
Assert.Contains("<AttributeListEditor", markup);
|
||||
Assert.Contains("@bind-ElementDataType=\"_attrElementDataType\"", markup);
|
||||
Assert.Contains("@bind-Rows=\"_attrListRows\"", markup);
|
||||
// Submit encodes rows to canonical JSON and passes the element type.
|
||||
Assert.Contains("AttributeValueCodec.Encode(_attrListRows)", markup);
|
||||
Assert.Contains("ElementDataType = elementType", markup);
|
||||
// Edit decodes the stored JSON value into rows.
|
||||
Assert.Contains("DecodeListRows(", markup);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user