fix(adminui): Monaco text syncs on demand, diagnostics are sequenced, Problems list (D1)
R04:P1 - the editor no longer ships its full text JS->.NET on every keystroke. It reports at blur, and ScriptEdit / the VirtualTag modal read the authoritative value with MonacoEditor.GetValueAsync at Save (validation runs after the pull). The Blazor hub's MaximumReceiveMessageSize is raised from 32 KB to 1 MB. The JS refuses a value that would not fit instead of sending an oversize frame, and Save surfaces that refusal rather than persisting stale source. R04:P9 - diagnostics carry a generation counter and an AbortController. Every edit aborts the in-flight request, and only the response for the current text is applied. R04:S2 - the always-empty inlay-hints provider, endpoint, service method and contracts are removed. R04:S3 - MarkersChanged is typed (DiagnosticMarker) and feeds a new ScriptProblemsList. Clicking a problem calls RevealLineAsync. Markers cross to .NET only when a list subscribes. Refs #745 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.AdminUI.ScriptAnalysis;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.ScriptAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// R04:S3 / S2 — the marker round-trip now feeds a real Problems list (MarkersChanged → list → RevealLineAsync),
|
||||
/// and the dead inlay-hints endpoint is gone. The Razor wiring is live-verified; the ordering and wording it
|
||||
/// renders are pinned here.
|
||||
/// </summary>
|
||||
public sealed class ProblemsListTests
|
||||
{
|
||||
private static DiagnosticMarker M(int sev, int line, int col, string code = "X") => new(sev, line, col, line, col + 1, "msg", code);
|
||||
|
||||
[Fact]
|
||||
public void Problems_are_ordered_errors_first_then_by_position()
|
||||
{
|
||||
var ordered = ScriptProblems.Order(new[]
|
||||
{
|
||||
M(4, 1, 1, "W1"), M(8, 9, 2, "E3"), M(8, 2, 5, "E2"), M(8, 2, 1, "E1"), M(2, 1, 1, "I1"),
|
||||
});
|
||||
|
||||
ordered.Select(m => m.Code).ShouldBe(new[] { "E1", "E2", "E3", "W1", "I1" });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Null_or_empty_markers_order_to_an_empty_list() => ScriptProblems.Order(null).ShouldBeEmpty();
|
||||
|
||||
[Theory]
|
||||
[InlineData(8, "Error")]
|
||||
[InlineData(4, "Warning")]
|
||||
[InlineData(2, "Info")]
|
||||
[InlineData(1, "Hint")]
|
||||
public void Severity_labels_follow_monaco_values(int severity, string label)
|
||||
=> ScriptProblems.SeverityLabel(severity).ShouldBe(label);
|
||||
|
||||
[Fact]
|
||||
public void Summary_counts_errors_and_warnings()
|
||||
{
|
||||
ScriptProblems.Summary(Array.Empty<DiagnosticMarker>()).ShouldBe("No problems");
|
||||
ScriptProblems.Summary(new[] { M(8, 1, 1) }).ShouldBe("1 error");
|
||||
ScriptProblems.Summary(new[] { M(8, 1, 1), M(8, 2, 1), M(4, 3, 1) }).ShouldBe("2 errors, 1 warning");
|
||||
ScriptProblems.Summary(new[] { M(4, 1, 1), M(4, 2, 1), M(2, 3, 1) }).ShouldBe("2 warnings, 1 other");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_inlay_hints_endpoint_is_gone()
|
||||
{
|
||||
// The always-empty inlay-hints endpoint (and the JS provider that POSTed the full source to it on every
|
||||
// edit) were removed. Positive control: the other five routes are still mapped.
|
||||
var (app, _) = AnalysisCancellationTests.BuildApp();
|
||||
var routes = AnalysisCancellationTests.Endpoints(app).Select(e => e.RoutePattern.RawText!).ToList();
|
||||
|
||||
routes.ShouldNotContain(r => r.Contains("inlay", StringComparison.OrdinalIgnoreCase));
|
||||
foreach (var r in new[] { "/diagnostics", "/completions", "/hover", "/signature-help", "/format" })
|
||||
routes.ShouldContain(x => x.EndsWith(r));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user