using Bunit;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;
using ZB.MOM.WW.ScadaBridge.CentralUI.Services;
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Design;
///
/// The raw-JSON escape hatch in
/// (the fallback for an unresolved $ref /
/// unknown-type node) is a Monaco json editor wired with THAT FIELD's own
/// sub-schema — not the whole root object schema — so each field's editor
/// validates/completes against just its own portion of the type. These tests
/// assert at the C#/interop boundary that the correct per-field fragment reaches
/// each editor's JsonSchema parameter (and that a sibling field's resolved
/// shape never leaks in), using FindComponents when 2+ escape-hatch fields
/// are present. The JS hover/completion itself is Monaco-built-in and not
/// unit-testable here.
///
public class ParameterValueFormMonacoSchemaTests : BunitContext
{
private readonly ISchemaLibraryQueryService _library = Substitute.For();
/// Initializes a new instance of the class.
public ParameterValueFormMonacoSchemaTests()
{
// The escape-hatch surface renders a MonacoEditor whose JS interop is not
// exercised here — Loose mode lets the unconfigured createEditor call no-op
// so the render completes and we can assert on the editor's parameters.
JSInterop.Mode = JSRuntimeMode.Loose;
_library.GetSchemaMapAsync(Arg.Any())
.Returns(new Dictionary());
Services.AddSingleton(_library);
}
/// Raw JSON errors describe the named composite and disappear after correction.
[Fact]
public async Task EscapeHatch_ParseErrorIsLinkedToNamedGroupUntilCorrection()
{
const string schema = """{"type":"object","properties":{"payload":{"$ref":"lib:Missing"}}}""";
var cut = Render(p => p.Add(c => c.ParameterDefinitions, schema));
var editor = cut.FindComponent();
Assert.Equal("Parameter payload JSON", editor.Instance.AriaLabel);
await cut.InvokeAsync(() => editor.Instance.ValueChanged.InvokeAsync("{bad"));
var group = cut.Find(".param-json-editor");
Assert.Equal("group", group.GetAttribute("role"));
Assert.Equal("Parameter payload JSON", group.GetAttribute("aria-label"));
Assert.Equal("true", group.GetAttribute("aria-invalid"));
Assert.Contains("JSON parse error", cut.Find("#" + group.GetAttribute("aria-describedby")).TextContent);
await cut.InvokeAsync(() => editor.Instance.ValueChanged.InvokeAsync("{}"));
group = cut.Find(".param-json-editor");
Assert.Equal("false", group.GetAttribute("aria-invalid"));
Assert.Null(group.GetAttribute("aria-describedby"));
Assert.Equal("Parameter payload JSON", cut.FindComponent().Instance.AriaLabel);
}
/// Verifies the case: escape Hatch, renders Monaco Json Editor, not Plain Textarea.
[Fact]
public void EscapeHatch_RendersMonacoJsonEditor_NotPlainTextarea()
{
// A nested field whose $ref cannot be resolved drops to the raw-JSON
// escape hatch. That surface must now be a Monaco json editor.
const string schema = """
{
"type": "object",
"properties": {
"payload": { "$ref": "lib:Missing" }
}
}
""";
var cut = Render(p => p
.Add(x => x.ParameterDefinitions, schema)
.Add(x => x.Values, new Dictionary()));
var editor = cut.FindComponent();
Assert.Equal("json", editor.Instance.Language);
// No plain