68 lines
3.1 KiB
C#
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");
|
|
}
|
|
}
|