using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.ScriptAnalysis;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.ScriptAnalysis;
///
/// Covers the two {{equip}}-aware editor niceties: hover on a path literal containing the
/// {{equip}} token shows an "equipment-relative" note (not the "not a known configured
/// tag path" warning), and completion after {{equip}}. offers the attribute leaf names.
///
public sealed class EquipTokenEditorTests
{
/// A fake catalog whose configured paths all share an object prefix, so the attribute
/// leaf names ("Source", "Other") are what {{equip}}. completion should surface.
private sealed class FakeCatalog : IScriptTagCatalog
{
private static readonly string[] Paths = { "Mixer_001.Source", "Mixer_001.Other" };
public Task> GetPathsAsync(string? filter, CancellationToken ct)
=> Task.FromResult>(Paths);
public Task GetTagInfoAsync(string path, CancellationToken ct)
=> Task.FromResult(null);
public Task> GetEquipmentRelativeLeavesAsync(string? filter, CancellationToken ct)
{
IEnumerable leaves = new[] { "Source", "Other" };
if (!string.IsNullOrWhiteSpace(filter))
leaves = leaves.Where(n => n.StartsWith(filter, StringComparison.OrdinalIgnoreCase));
return Task.FromResult>(leaves.ToList());
}
}
private static readonly ScriptAnalysisService Svc = new(new FakeCatalog());
private static async Task> 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");
}
}