Conversation
Reviewer's GuideRefactors the ErrorLogger and BootstrapBlazorErrorBoundary error-handling pipeline to use a dedicated ErrorRender component and an injected IErrorBoundaryLogger, simplifying exception rendering and callback signatures, and updates call sites and tests accordingly. Sequence diagram for BootstrapBlazorErrorBoundary error handling with ErrorRendersequenceDiagram
participant Component as FailingComponent
participant Boundary as BootstrapBlazorErrorBoundary
participant BoundaryLogger as IErrorBoundaryLogger
participant ErrorView as ErrorRender
Component->>Boundary: lifecycle method throws Exception
Boundary->>Boundary: OnErrorAsync(exception)
Boundary->>BoundaryLogger: LogErrorAsync(exception)
BoundaryLogger-->>Boundary: Task completed
Boundary->>Boundary: BuildRenderTree(builder)
alt CurrentException is null
Boundary->>Boundary: render ChildContent
else ErrorContent is not null
Boundary->>Boundary: builder.AddContent(ErrorContent(CurrentException))
else page lifecycle exception
Boundary->>Boundary: IsPageException(CurrentException)
alt IsPageException returns true
Boundary->>Boundary: RenderException(builder, CurrentException)
alt OnErrorHandleAsync is not null
Boundary->>Boundary: OnErrorHandleAsync(Logger, exception)
Boundary-->>Boundary: continue after callback
else OnErrorHandleAsync is null
Boundary->>ErrorView: instantiate component
Boundary->>ErrorView: set Exception parameter
ErrorView->>ErrorView: BuildRenderTree(builder)
ErrorView-->>Boundary: rendered error markup
end
Boundary->>Boundary: ResetException()
else not a page exception
Boundary->>Boundary: render ChildContent
Boundary->>Boundary: ResetException()
end
end
Sequence diagram for ErrorLogger.HandlerExceptionAsync pipelinesequenceDiagram
participant Caller as CallerComponent
participant Logger as ErrorLogger
participant Handler as IHandlerException
participant ErrorView as ErrorRender
Caller->>Logger: HandlerExceptionAsync(exception)
activate Logger
Logger->>Logger: handler = _cache.LastOrDefault()
alt handler is not null
Logger->>Handler: HandlerExceptionAsync(exception, exceptionContent)
activate Handler
note over Logger,Handler: exceptionContent builds ErrorRender with Exception
Handler-->>Logger: Task completed
deactivate Handler
else handler is null
Logger->>Logger: skip handler invocation
end
alt OnErrorHandleAsync is not null
Logger->>Logger: OnErrorHandleAsync(exception)
Logger-->>Caller: Task completed
else no external callback
Logger-->>Caller: Task completed
end
deactivate Logger
Class diagram for redesigned ErrorLogger and error boundary pipelineclassDiagram
direction LR
class BootstrapBlazorErrorBoundary {
+bool ShowToast
+bool EnableILogger
+string ToastTitle
+Func~ILogger, Exception, Task~ OnErrorHandleAsync
-ILogger~BootstrapBlazorErrorBoundary~ Logger
-IErrorBoundaryLogger ErrorBoundaryLogger
-NavigationManager NavigationManager
-IToastService ToastService
-PropertyInfo _currentExceptionPropertyInfo
+Task OnErrorAsync(Exception exception)
+void BuildRenderTree(RenderTreeBuilder builder)
-void RenderException(RenderTreeBuilder builder, Exception ex)
-static bool IsPageException(Exception ex)
-void ResetException()
}
class ErrorLogger {
+RenderFragment? ErrorContent
+RenderFragment? ChildContent
+bool EnableILogger
+Func~IErrorLogger, Task~? OnInitializedCallback
+Func~Exception, Task~? OnErrorHandleAsync
-List~IHandlerException~ _cache
+Task HandlerExceptionAsync(Exception exception)
+void Register(IHandlerException handler)
+void UnRegister(IHandlerException handler)
+RenderFragment BuildRenderTree(RenderTreeBuilder builder)
}
class ErrorRender {
-IConfiguration Configuration
-RenderHandle _renderHandle
-Exception _ex
-bool? _errorDetails
+Task SetParametersAsync(ParameterView parameters)
-void BuildRenderTree(RenderTreeBuilder builder)
-MarkupString GetErrorContentMarkupString(Exception ex)
}
class IErrorLogger {
<<interface>>
+Task HandlerExceptionAsync(Exception exception)
+void Register(IHandlerException handler)
+void UnRegister(IHandlerException handler)
}
class IHandlerException {
<<interface>>
+Task HandlerExceptionAsync(Exception exception, RenderFragment exceptionContent)
}
class IErrorBoundaryLogger {
<<interface>>
+Task LogErrorAsync(Exception exception)
}
class TabItemContent {
-ErrorLogger _logger
-bool _detailedErrorsLoaded
-bool _showDetailedErrors
-Task HandlerErrorCoreAsync(ILogger logger, Exception ex)
+void Render()
}
ErrorLogger ..|> IErrorLogger
ErrorRender ..|> IComponent
BootstrapBlazorErrorBoundary ..|> ErrorBoundaryBase
ErrorLogger o-- IHandlerException : caches
ErrorLogger --> IErrorLogger : OnInitializedCallback
ErrorLogger --> IHandlerException : HandlerExceptionAsync uses
BootstrapBlazorErrorBoundary --> IErrorBoundaryLogger : uses
BootstrapBlazorErrorBoundary --> ErrorRender : renders on page exception
TabItemContent --> ErrorLogger : uses
TabItemContent --> ILogger : HandlerErrorCoreAsync
IErrorLogger <.. ErrorLogger : implements
IHandlerException <.. ErrorLogger : calls
IErrorBoundaryLogger <.. BootstrapBlazorErrorBoundary : injected
ErrorRender --> IConfiguration : uses for details
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 7 issues, and left some high level feedback:
- In both
BootstrapBlazorErrorBoundary.RenderExceptionandErrorLogger.HandlerExceptionAsyncthe render fragments callbuilder.OpenComponent<ErrorRender>(0);but then usebuilder.CloseElement();instead ofbuilder.CloseComponent();, which will generate an invalid render tree. - The new
ErrorRenderclass implementsIComponentbut relies on[Inject] IConfiguration Configuration { get; set; }; property injection is only performed forComponentBase-derived components, soConfigurationwill remain null unless you refactor this to derive fromComponentBaseor injectIConfigurationvia some other explicit mechanism. - The delegate type for
ErrorLogger.OnErrorHandleAsyncappears to have changed toFunc<Exception, Task>, butTabItemContent.HandlerErrorCoreAsyncstill has the signature(ILogger logger, Exception ex), which will not match the expected delegate type and should be updated to accept only the exception.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In both `BootstrapBlazorErrorBoundary.RenderException` and `ErrorLogger.HandlerExceptionAsync` the render fragments call `builder.OpenComponent<ErrorRender>(0);` but then use `builder.CloseElement();` instead of `builder.CloseComponent();`, which will generate an invalid render tree.
- The new `ErrorRender` class implements `IComponent` but relies on `[Inject] IConfiguration Configuration { get; set; }`; property injection is only performed for `ComponentBase`-derived components, so `Configuration` will remain null unless you refactor this to derive from `ComponentBase` or inject `IConfiguration` via some other explicit mechanism.
- The delegate type for `ErrorLogger.OnErrorHandleAsync` appears to have changed to `Func<Exception, Task>`, but `TabItemContent.HandlerErrorCoreAsync` still has the signature `(ILogger logger, Exception ex)`, which will not match the expected delegate type and should be updated to accept only the exception.
## Individual Comments
### Comment 1
<location> `src/BootstrapBlazor/Components/ErrorLogger/BootstrapBlazorErrorBoundary.cs:101-76` </location>
<code_context>
- private Exception? _exception = null;
-
- private RenderFragment<Exception> ExceptionContent => ex => builder =>
+ private void RenderException(RenderTreeBuilder builder, Exception ex)
{
- if (ErrorContent != null)
+ if (OnErrorHandleAsync is not null)
{
- builder.AddContent(0, ErrorContent(ex));
+ var renderTask = OnErrorHandleAsync(Logger, ex);
}
else
{
</code_context>
<issue_to_address>
**issue (bug_risk):** RenderException does not render any content when OnErrorHandleAsync is set and also ignores the returned Task.
With `OnErrorHandleAsync` set, this path only calls the callback and never writes to the render tree, so the error results in no UI. It also assigns the callback’s `Task` to `renderTask` but never awaits or observes it, risking unobserved exceptions. Please either (a) have the callback return a fragment that you render here, or (b) use the callback only for side effects and still render fallback UI, and in either case make this path properly async/await or clearly treat it as fire-and-forget with appropriate exception handling.
</issue_to_address>
### Comment 2
<location> `src/BootstrapBlazor/Components/ErrorLogger/BootstrapBlazorErrorBoundary.cs:109-111` </location>
<code_context>
- builder.OpenElement(0, "div");
- builder.AddAttribute(1, "class", "error-stack");
- builder.AddContent(2, GetErrorContentMarkupString(ex));
+ builder.OpenComponent<ErrorRender>(0);
+ builder.AddAttribute(1, "Exception", ex);
builder.CloseElement();
}
- };
</code_context>
<issue_to_address>
**issue (bug_risk):** CloseElement is used for a component, which should be closed with CloseComponent instead.
`OpenComponent<ErrorRender>(0)` must be paired with `CloseComponent()`, not `CloseElement()`. Using `CloseElement()` here will cause an `InvalidOperationException` at runtime when the renderer validates the render tree calls.
</issue_to_address>
### Comment 3
<location> `src/BootstrapBlazor/Components/ErrorLogger/ErrorLogger.cs:142-146` </location>
<code_context>
+ var handler = _cache.LastOrDefault();
+ if (handler is not null)
+ {
+ await handler.HandlerExceptionAsync(exception, ex => builder =>
+ {
+ builder.OpenComponent<ErrorRender>(0);
+ builder.AddAttribute(1, "Exception", ex);
+ builder.CloseElement();
+ });
+ }
</code_context>
<issue_to_address>
**issue (bug_risk):** ErrorLogger’s error fragment also closes a component with CloseElement instead of CloseComponent.
Here you open an `ErrorRender` with `builder.OpenComponent<ErrorRender>(0);` but close it with `builder.CloseElement();`. This mismatch will throw at render time. Use `builder.CloseComponent();` to correctly close the component.
</issue_to_address>
### Comment 4
<location> `src/BootstrapBlazor/Components/Tab/TabItemContent.cs:83` </location>
<code_context>
return Task.CompletedTask;
}));
- builder.AddAttribute(7, nameof(ErrorLogger.OnErrorHandleAsync), TabSet.OnErrorHandleAsync);
+ builder.AddAttribute(7, nameof(ErrorLogger.OnErrorHandleAsync), TabSet.OnErrorHandleAsync ?? HandlerErrorCoreAsync);
builder.CloseComponent();
}
</code_context>
<issue_to_address>
**issue (bug_risk):** TabItemContent wires OnErrorHandleAsync to a (ILogger, Exception) handler, which may not match ErrorLogger’s parameter type.
`TabSet.OnErrorHandleAsync ?? HandlerErrorCoreAsync` means `ErrorLogger.OnErrorHandleAsync` is treated as a `(ILogger logger, Exception ex) => Task` delegate, but `ErrorLogger.HandlerExceptionAsync` invokes `OnErrorHandleAsync` with only an `Exception`. Please align the `OnErrorHandleAsync` delegate signature and its invocation (and any public surface using it) so they consistently match, then adjust either this binding or the call site.
</issue_to_address>
### Comment 5
<location> `src/BootstrapBlazor/Components/ErrorLogger/ErrorRender.cs:11-15` </location>
<code_context>
+
+namespace BootstrapBlazor.Components;
+
+class ErrorRender : IComponent
+{
+ [Inject]
+ [NotNull]
+ private IConfiguration? Configuration { get; set; }
+
+ private RenderHandle _renderHandle;
</code_context>
<issue_to_address>
**issue (bug_risk):** Using [Inject] on an IComponent implementation is unlikely to work and can lead to null Configuration at runtime.
Blazor only applies `[Inject]` to `ComponentBase`-derived components; it does not perform property injection on a raw `IComponent`, so this property will never be set. Because `GetErrorContentMarkupString` dereferences `Configuration` without a null check, this can result in a `NullReferenceException`. Consider either deriving `ErrorRender` from `ComponentBase` so DI works as expected, or passing `IConfiguration` explicitly (e.g., via a `[Parameter]`).
</issue_to_address>
### Comment 6
<location> `test/UnitTest/Components/ErrorLoggerTest.cs:89-90` </location>
<code_context>
});
});
var button = cut.Find("button");
- button.TriggerEvent("onclick", EventArgs.Empty);
+ await cut.InvokeAsync(() => button.Click());
var result = await tcs.Task;
Assert.True(result);
</code_context>
<issue_to_address>
**suggestion (testing):** Add tests for the new ErrorLogger.HandlerExceptionAsync behavior (handler and callback interactions)
`ErrorLogger.HandlerExceptionAsync` now (1) uses the last registered `IHandlerException` to render via `ErrorRender`, and (2) always calls `OnErrorHandleAsync(exception)` without the logger. This test only checks that `OnErrorHandleAsync` runs on button click.
Please add tests that:
- Register a fake `IHandlerException` and assert `HandlerExceptionAsync` calls the handler exactly once with the thrown exception and a non-null `RenderFragment`.
- Cover the case where no handlers are registered and confirm `OnErrorHandleAsync` is still invoked.
- Optionally assert that the handler’s render fragment uses `ErrorRender` (e.g., `.error-stack` wrapper or the exception message when `DetailedErrors` is disabled).
This will more fully exercise the new error pipeline and `ErrorRender` integration.
Suggested implementation:
```csharp
});
});
var button = cut.Find("button");
await cut.InvokeAsync(() => button.Click());
var result = await tcs.Task;
Assert.True(result);
}
[Fact]
public async Task HandlerExceptionAsync_UsesLastRegisteredHandler_WithExceptionAndRenderFragment()
{
using var ctx = new TestContext();
var thrownException = new InvalidOperationException("boom");
Exception? receivedException = null;
RenderFragment? receivedRenderFragment = null;
var handlerMock = new Mock<IHandlerException>();
handlerMock
.Setup(h => h.HandlerExceptionAsync(It.IsAny<Exception>(), It.IsAny<RenderFragment>()))
.Returns<Exception, RenderFragment>((ex, fragment) =>
{
receivedException = ex;
receivedRenderFragment = fragment;
return Task.CompletedTask;
});
// Register multiple handlers to ensure the last one is used
ctx.Services.AddSingleton<IHandlerException, NoopHandlerException>();
ctx.Services.AddSingleton<IHandlerException>(handlerMock.Object);
var cut = ctx.RenderComponent<ErrorLoggerTestHost>(parameters => parameters
.Add(p => p.ThrowOnClick, thrownException));
// Act
await cut.InvokeAsync(() => cut.Find("button").Click());
// Assert
handlerMock.Verify(
h => h.HandlerExceptionAsync(It.IsAny<Exception>(), It.IsAny<RenderFragment>()),
Times.Once);
Assert.Same(thrownException, receivedException);
Assert.NotNull(receivedRenderFragment);
}
[Fact]
public async Task HandlerExceptionAsync_WhenNoHandlersRegistered_StillInvokesOnErrorHandleAsync()
{
using var ctx = new TestContext();
var tcs = new TaskCompletionSource<Exception>();
var cut = ctx.RenderComponent<ErrorLoggerTestHost>(parameters => parameters
.Add(p => p.OnErrorHandleAsync, (Exception ex) =>
{
tcs.TrySetResult(ex);
return Task.CompletedTask;
}));
// Act
await cut.InvokeAsync(() => cut.Find("button").Click());
// Assert
var exceptionFromCallback = await tcs.Task;
Assert.NotNull(exceptionFromCallback);
}
[Fact]
public async Task HandlerExceptionAsync_UsesErrorRender_ForHandlerRenderFragment()
{
using var ctx = new TestContext();
var thrownException = new InvalidOperationException("render error");
RenderFragment? receivedRenderFragment = null;
var handlerMock = new Mock<IHandlerException>();
handlerMock
.Setup(h => h.HandlerExceptionAsync(It.IsAny<Exception>(), It.IsAny<RenderFragment>()))
.Returns<Exception, RenderFragment>((ex, fragment) =>
{
receivedRenderFragment = fragment;
return Task.CompletedTask;
});
ctx.Services.AddSingleton<IHandlerException>(handlerMock.Object);
var cut = ctx.RenderComponent<ErrorLoggerTestHost>(parameters => parameters
.Add(p => p.ThrowOnClick, thrownException));
// Act
await cut.InvokeAsync(() => cut.Find("button").Click());
// Assert - fragment is provided
Assert.NotNull(receivedRenderFragment);
// Render the fragment and assert ErrorRender integration
var fragmentHost = ctx.Render(receivedRenderFragment);
var markup = fragmentHost.Markup;
// When DetailedErrors is enabled, ErrorRender typically wraps stack/details in a container,
// e.g. `.error-stack`. When disabled, at least the message should be present.
Assert.True(
markup.Contains("error-stack", StringComparison.OrdinalIgnoreCase) ||
markup.Contains(thrownException.Message, StringComparison.OrdinalIgnoreCase),
"The handler render fragment should be produced by ErrorRender (wrapper or message).");
}
/// <summary>
/// Simple no-op handler used to verify that the *last* registered IHandlerException is used.
/// </summary>
private sealed class NoopHandlerException : IHandlerException
{
public Task HandlerExceptionAsync(Exception exception, RenderFragment renderFragment)
=> Task.CompletedTask;
}
```
These tests assume the following are already available in the project:
- `ErrorLoggerTestHost` (or equivalent host component used in the existing tests) with parameters:
- `Exception ThrowOnClick` (or similar) that causes the exception to be thrown when the button is clicked.
- `Func<Exception, Task> OnErrorHandleAsync` callback used by `ErrorLogger`.
- `IHandlerException` interface with `Task HandlerExceptionAsync(Exception exception, RenderFragment renderFragment)` signature.
- Usings and packages:
- `using Bunit;`
- `using Microsoft.AspNetCore.Components;`
- `using Moq;`
- `using Microsoft.Extensions.DependencyInjection;`
- `using System;`
- `using System.Threading.Tasks;`
If any of these differ (e.g. host component name, parameter names, or the handler method signature), adjust the parameter/property names and the `HandlerExceptionAsync` signature in the tests accordingly. Also, if `ErrorRender` produces different CSS classes/markup than `.error-stack`, adapt the `markup.Contains(...)` assertions to match the actual rendered output.
</issue_to_address>
### Comment 7
<location> `test/UnitTest/Components/LayoutTest.cs:613-611` </location>
<code_context>
{
pb.Add(a => a.UseTabSet, false);
- pb.Add(a => a.OnErrorHandleAsync, (logger, ex) =>
+ pb.Add(a => a.OnErrorHandleAsync, ex =>
{
ex1 = ex;
return Task.CompletedTask;
</code_context>
<issue_to_address>
**suggestion (testing):** Add coverage for the default Tab error handling path when no custom OnErrorHandleAsync is provided
The layout tests now match the new `OnErrorHandleAsync(Exception ex)` signature, but they only cover the case where a custom callback is provided. The default behavior in `TabItemContent` (`TabSet.OnErrorHandleAsync ?? HandlerErrorCoreAsync`) isn’t exercised.
Please add a test that:
- Uses a `TabSet` without `OnErrorHandleAsync` so `HandlerErrorCoreAsync` runs.
- Triggers an exception in the tab content.
- Verifies the error is rendered via `ErrorRender` (e.g., `class="error-stack"` or the exception text in the DOM) and that the normal tab content is not rendered in the error state.
This will validate the default error path and guard against regressions when `OnErrorHandleAsync` is not set.
Suggested implementation:
```csharp
pb.AddChildContent<Layout>(pb =>
{
pb.Add(a => a.UseTabSet, false);
pb.Add(a => a.OnErrorHandleAsync, ex =>
{
ex1 = ex;
return Task.CompletedTask;
pb.Add(a => a.EnableErrorLogger, true);
pb.AddChildContent<Layout>(pb =>
{
pb.Add(a => a.OnErrorHandleAsync, ex =>
{
ex1 = ex;
return Task.CompletedTask;
```
To implement the requested coverage for the default `TabSet` error handling path (when `OnErrorHandleAsync` is not set), add a new test method to `LayoutTest.cs` similar to the existing Tab error-handling tests. Place it alongside the other layout/tab tests inside the `LayoutTest` class.
Here is a concrete example you can paste into the class:
```csharp
[Fact]
public void Layout_TabSet_DefaultErrorHandler_RendersErrorAndHidesContent()
{
// Arrange
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
var cut = ctx.RenderComponent<Layout>(pb =>
{
// Use TabSet with default error handler – do NOT set OnErrorHandleAsync here
pb.Add(a => a.UseTabSet, true);
pb.Add(a => a.EnableErrorLogger, true);
pb.AddChildContent<Tabs>(tabPb =>
{
tabPb.AddChildContent<TabItem>(itemPb =>
{
itemPb.Add(a => a.Text, "Tab 1");
itemPb.AddChildContent(builder =>
{
// This will throw during render to trigger the Tab error path
throw new InvalidOperationException("Tab content failure");
});
});
});
});
// Act
// Force a render that evaluates the tab content and triggers the exception.
// The exception should be handled by TabSet.HandlerErrorCoreAsync via
// TabItemContent (TabSet.OnErrorHandleAsync ?? HandlerErrorCoreAsync).
cut.Render();
// Assert
// Verify error UI is rendered by the default TabSet handler
cut.MarkupMatches(markup =>
markup.Contains("error-stack", StringComparison.OrdinalIgnoreCase) ||
markup.Contains("Tab content failure", StringComparison.OrdinalIgnoreCase));
// Optionally, assert that the normal tab content is not present when in error state.
// Replace "Tab 1 content" with any known normal content text that would appear in a non-error render.
cut.MarkupDoesNotContain("Tab 1 content");
}
```
You may need to align:
1. The component types (`Layout`, `Tabs`, `TabItem`) and parameter names (`UseTabSet`, `EnableErrorLogger`, `Text`) with the actual components in your project.
2. The error container selector: instead of checking for `"error-stack"` via `Contains`, you might use your existing pattern, e.g. `cut.Find(".error-stack")` if the tests normally use CSS selectors.
3. The way you assert markup. If you use `cut.Markup.Contains(...)` or FluentAssertions-style helpers already in `LayoutTest.cs`, adjust the assertions to match the existing convention.
4. The normal tab content text you assert is absent (replace `"Tab 1 content"` with something your Layout/TabItem would normally render on success).
This test ensures that when `OnErrorHandleAsync` is not provided, the default `HandlerErrorCoreAsync` path is exercised, the error is rendered via `ErrorRender`, and the original tab content is hidden in the error state.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Pull request overview
This PR redesigns the ErrorLogger component by refactoring the error handling architecture. The main goal is to simplify the error handling callback signature and introduce a new ErrorRender component for displaying exceptions.
Changes:
- Refactored OnErrorHandleAsync callback to remove the ILogger parameter (changed from 2 parameters to 1)
- Introduced new ErrorRender component to encapsulate error rendering logic
- Simplified BootstrapBlazorErrorBoundary by delegating logging to IErrorBoundaryLogger
- Updated tests to use the new API signature and modern bUnit testing patterns
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/BootstrapBlazor/Components/ErrorLogger/ErrorLogger.cs | Modified HandlerExceptionAsync implementation and removed _errorBoundary field; introduced direct rendering approach |
| src/BootstrapBlazor/Components/ErrorLogger/BootstrapBlazorErrorBoundary.cs | Refactored error handling logic; replaced Configuration with IErrorBoundaryLogger; simplified BuildRenderTree |
| src/BootstrapBlazor/Components/ErrorLogger/ErrorRender.cs | New component to encapsulate exception rendering logic with configuration-based detail display |
| src/BootstrapBlazor/Components/Tab/TabItemContent.cs | Added HandlerErrorCoreAsync method and integrated ErrorRender component |
| test/UnitTest/Components/LayoutTest.cs | Updated tests to use new single-parameter OnErrorHandleAsync signature |
| test/UnitTest/Components/ErrorLoggerTest.cs | Modernized test to use Click() instead of TriggerEvent |
| src/BootstrapBlazor.Server/Components/Samples/TreeViews.razor.cs | Minor formatting: added spaces in log message |
| src/BootstrapBlazor.Server/Components/Samples/GlobalException.razor.cs | Removed BOM character from file |
| src/BootstrapBlazor.Server/Components/Pages/Error.razor | Formatting cleanup: removed extra blank lines |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7558 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 748 749 +1
Lines 33004 32974 -30
Branches 4589 4580 -9
=========================================
- Hits 33004 32974 -30
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Link issues
fixes #7557
Summary By Copilot
Regression?
Risk
Verification
Packaging changes reviewed?
☑️ Self Check before Merge
Summary by Sourcery
Redesign the error logging and rendering pipeline to centralize exception display logic and simplify ErrorLogger integration across components.
New Features:
Enhancements: