using Bunit; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.JSInterop; using ScadaLink.CentralUI.Components.Shared; namespace ScadaLink.CentralUI.Tests.Shared; /// /// Regression tests for CentralUI-018. MonacoEditor wrapped every JS /// interop call in a bare try { ... } catch { } with no logging — a /// genuine Monaco init failure became invisible. The fix narrows the catch to /// the expected prerender / disconnect cases and logs any real /// via ILogger. /// public class MonacoEditorLoggingTests : BunitContext { /// Captures log entries so the test can assert on them. private sealed class CapturingLoggerProvider : ILoggerProvider { public List<(LogLevel Level, string Message, Exception? Exception)> Entries { get; } = new(); public ILogger CreateLogger(string categoryName) => new CapturingLogger(Entries); public void Dispose() { } private sealed class CapturingLogger : ILogger { private readonly List<(LogLevel, string, Exception?)> _entries; public CapturingLogger(List<(LogLevel, string, Exception?)> entries) => _entries = entries; public IDisposable? BeginScope(TState state) where TState : notnull => null; public bool IsEnabled(LogLevel logLevel) => true; public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) => _entries.Add((logLevel, formatter(state, exception), exception)); } } [Fact] public void CreateEditor_GenuineJsException_IsLogged_NotSwallowed() { var provider = new CapturingLoggerProvider(); Services.AddLogging(b => b.AddProvider(provider)); // createEditor is an InvokeVoidAsync call — configure it to throw a // genuine JSException so we exercise the real-failure path. JSInterop.Mode = JSRuntimeMode.Strict; JSInterop.SetupVoid("MonacoBlazor.createEditor", _ => true) .SetException(new JSException("Monaco failed to load")); // Pre-fix: the bare catch {} swallowed this with no trace. Post-fix: // the component renders fine but the failure is logged. var cut = Render(p => p.Add(c => c.ShowToolbar, false)); var errors = provider.Entries.Where(e => e.Level == LogLevel.Error).ToList(); Assert.NotEmpty(errors); Assert.Contains(errors, e => e.Exception is JSException); } [Fact] public void CreateEditor_Prerender_DoesNotLog() { // When JS interop is unavailable (prerender), createEditor throws // InvalidOperationException — that is expected and must NOT be logged. var provider = new CapturingLoggerProvider(); Services.AddLogging(b => b.AddProvider(provider)); JSInterop.Mode = JSRuntimeMode.Strict; JSInterop.SetupVoid("MonacoBlazor.createEditor", _ => true) .SetException(new InvalidOperationException("JS interop not available during prerender")); var cut = Render(p => p.Add(c => c.ShowToolbar, false)); Assert.DoesNotContain(provider.Entries, e => e.Level >= LogLevel.Warning); } }