Files
lmxopcua/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ScriptAnalysis/EquipTokenEditorTests.cs
T

68 lines
3.1 KiB
C#

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");
}
}