feat(adminui): {{equip}}-aware hover + {{equip}}. leaf completion in the script editor
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.AdminUI.ScriptAnalysis;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.ScriptAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// Covers the two {{equip}}-aware editor niceties: hover on a path literal containing the
|
||||
/// <c>{{equip}}</c> token shows an "equipment-relative" note (not the "not a known configured
|
||||
/// tag path" warning), and completion after <c>{{equip}}.</c> offers the attribute leaf names.
|
||||
/// </summary>
|
||||
public sealed class EquipTokenEditorTests
|
||||
{
|
||||
/// <summary>A fake catalog whose configured paths all share an object prefix, so the attribute
|
||||
/// leaf names ("Source", "Other") are what {{equip}}. completion should surface.</summary>
|
||||
private sealed class FakeCatalog : IScriptTagCatalog
|
||||
{
|
||||
private static readonly string[] Paths = { "Mixer_001.Source", "Mixer_001.Other" };
|
||||
|
||||
public Task<IReadOnlyList<string>> GetPathsAsync(string? filter, CancellationToken ct)
|
||||
=> Task.FromResult<IReadOnlyList<string>>(Paths);
|
||||
|
||||
public Task<ScriptTagInfo?> GetTagInfoAsync(string path, CancellationToken ct)
|
||||
=> Task.FromResult<ScriptTagInfo?>(null);
|
||||
|
||||
public Task<IReadOnlyList<string>> GetEquipmentRelativeLeavesAsync(string? filter, CancellationToken ct)
|
||||
{
|
||||
IEnumerable<string> leaves = new[] { "Source", "Other" };
|
||||
if (!string.IsNullOrWhiteSpace(filter))
|
||||
leaves = leaves.Where(n => n.StartsWith(filter, StringComparison.OrdinalIgnoreCase));
|
||||
return Task.FromResult<IReadOnlyList<string>>(leaves.ToList());
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly ScriptAnalysisService Svc = new(new FakeCatalog());
|
||||
|
||||
private static async Task<IReadOnlyList<CompletionItem>> Items(string code, int line, int col)
|
||||
=> (await Svc.CompleteAsync(new CompletionsRequest(code, line, col))).Items;
|
||||
|
||||
[Fact] public async Task Completion_after_equip_dot_offers_attribute_leaves()
|
||||
{
|
||||
// caret right after the dot of "{{equip}}." — column 30 sits between the trailing dot and
|
||||
// the closing quote of ctx.GetTag("{{equip}}.").
|
||||
var items = await Items("""return ctx.GetTag("{{equip}}.").Value;""", 1, 30);
|
||||
items.Select(i => i.Label).ShouldContain("{{equip}}.Source");
|
||||
items.ShouldAllBe(i => i.Detail == "tag path");
|
||||
}
|
||||
|
||||
[Fact] public async Task Completion_after_equip_dot_inserts_the_full_token_qualified_leaf()
|
||||
{
|
||||
var items = await Items("""return ctx.GetTag("{{equip}}.").Value;""", 1, 30);
|
||||
var source = items.FirstOrDefault(i => i.Label == "{{equip}}.Source");
|
||||
source.ShouldNotBeNull();
|
||||
source!.InsertText.ShouldBe("{{equip}}.Source");
|
||||
}
|
||||
|
||||
[Fact] public async Task Hover_on_equip_token_literal_notes_equipment_relative()
|
||||
{
|
||||
// caret inside ctx.GetTag("{{equip}}.Source")
|
||||
var md = (await Svc.Hover(new HoverRequest("""return ctx.GetTag("{{equip}}.Source").Value;""", 1, 24))).Markdown;
|
||||
md.ShouldNotBeNull();
|
||||
md!.ShouldContain("Equipment-relative");
|
||||
// the {{equip}} token must render literally (non-interpolated markdown segment)
|
||||
md.ShouldContain("{{equip}}");
|
||||
md.ShouldNotContain("Not a known");
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@ public sealed class HoverSignatureTests
|
||||
=> Task.FromResult<IReadOnlyList<string>>(System.Array.Empty<string>());
|
||||
public Task<ScriptTagInfo?> GetTagInfoAsync(string path, CancellationToken ct)
|
||||
=> Task.FromResult(path == "Line1.Speed" ? new ScriptTagInfo("Line1.Speed", "Tag", "Double", "MAIN-modbus") : null);
|
||||
public Task<IReadOnlyList<string>> GetEquipmentRelativeLeavesAsync(string? filter, CancellationToken ct)
|
||||
=> Task.FromResult<IReadOnlyList<string>>(new[] { "Speed" });
|
||||
}
|
||||
|
||||
[Fact] public async Task Hover_on_GetTag_returns_member_markdown()
|
||||
|
||||
@@ -13,6 +13,10 @@ public sealed class TagPathCompletionTests
|
||||
|
||||
public Task<ScriptTagInfo?> GetTagInfoAsync(string path, CancellationToken ct)
|
||||
=> Task.FromResult<ScriptTagInfo?>(null);
|
||||
|
||||
public Task<IReadOnlyList<string>> GetEquipmentRelativeLeavesAsync(string? filter, CancellationToken ct)
|
||||
// canned leaves derived from the canned paths above (substring after the first dot)
|
||||
=> Task.FromResult<IReadOnlyList<string>>(new[] { "Speed", "Temp" });
|
||||
}
|
||||
|
||||
private static readonly ScriptAnalysisService Svc = new(new FakeCatalog());
|
||||
|
||||
Reference in New Issue
Block a user