@@ -372,6 +372,126 @@ public async Task DoesNotRenderAfterOnInitAsyncTaskIsCancelledUsingCancellationT
372372 Assert . NotEmpty ( renderer . Batches ) ;
373373 }
374374
375+ [ Fact ]
376+ public async Task ErrorBoundaryHandlesOnInitializedAsyncReturnFaultedTask ( )
377+ {
378+ // Arrange
379+ var renderer = new TestRenderer ( ) ;
380+ TestErrorBoundary capturedBoundary = null ;
381+
382+ // Create root component that wraps the TestComponentErrorBuildRenderTree in an TestErrorBoundary
383+ var rootComponent = new TestComponent ( ) ;
384+ rootComponent . ChildContent = builder =>
385+ {
386+ builder . OpenComponent < TestErrorBoundary > ( 0 ) ;
387+ builder . AddComponentParameter ( 1 , nameof ( TestErrorBoundary . ChildContent ) , ( RenderFragment ) ( childBuilder =>
388+ {
389+ childBuilder . OpenComponent < TestComponentErrorBuildRenderTree > ( 0 ) ;
390+ childBuilder . AddComponentParameter ( 1 , nameof ( TestComponentErrorBuildRenderTree . FaultedTaskOnInitializedAsync ) , true ) ;
391+ childBuilder . CloseComponent ( ) ;
392+ } ) ) ;
393+ builder . AddComponentReferenceCapture ( 2 , inst => capturedBoundary = ( TestErrorBoundary ) inst ) ;
394+ builder . CloseComponent ( ) ;
395+ } ;
396+
397+ // Act
398+ var rootComponentId = renderer . AssignRootComponentId ( rootComponent ) ;
399+ await renderer . RenderRootComponentAsync ( rootComponentId ) ;
400+
401+ // Assert
402+ Assert . NotNull ( capturedBoundary ) ;
403+ Assert . NotNull ( capturedBoundary ! . ReceivedException ) ;
404+ Assert . Equal ( typeof ( InvalidTimeZoneException ) , capturedBoundary ! . ReceivedException . GetType ( ) ) ;
405+ }
406+
407+ [ Fact ]
408+ public async Task ErrorBoundaryHandlesCallOnParametersSetAsyncReturnFaultedTask ( )
409+ {
410+ // Arrange
411+ var renderer = new TestRenderer ( ) ;
412+ TestErrorBoundary capturedBoundary = null ;
413+
414+ // Create root component that wraps the TestComponentErrorBuildRenderTree in an TestErrorBoundary
415+ var rootComponent = new TestComponent ( ) ;
416+ rootComponent . ChildContent = builder =>
417+ {
418+ builder . OpenComponent < TestErrorBoundary > ( 0 ) ;
419+ builder . AddComponentParameter ( 1 , nameof ( TestErrorBoundary . ChildContent ) , ( RenderFragment ) ( childBuilder =>
420+ {
421+ childBuilder . OpenComponent < TestComponentErrorBuildRenderTree > ( 0 ) ;
422+ childBuilder . AddComponentParameter ( 1 , nameof ( TestComponentErrorBuildRenderTree . FaultedTaskOnParametersSetAsync ) , true ) ;
423+ childBuilder . CloseComponent ( ) ;
424+ } ) ) ;
425+ builder . AddComponentReferenceCapture ( 2 , inst => capturedBoundary = ( TestErrorBoundary ) inst ) ;
426+ builder . CloseComponent ( ) ;
427+ } ;
428+
429+ // Act
430+ var rootComponentId = renderer . AssignRootComponentId ( rootComponent ) ;
431+ await renderer . RenderRootComponentAsync ( rootComponentId ) ;
432+
433+ // Assert
434+ Assert . NotNull ( capturedBoundary ) ;
435+ Assert . NotNull ( capturedBoundary ! . ReceivedException ) ;
436+ Assert . Equal ( typeof ( InvalidTimeZoneException ) , capturedBoundary ! . ReceivedException . GetType ( ) ) ;
437+ }
438+
439+ [ Fact ]
440+ public async Task ComponentBaseDoesntRenderWhenOnInitializedAsyncFaultedTask ( )
441+ {
442+ // Arrange
443+ var renderer = new TestRenderer ( ) ;
444+ renderer . ShouldHandleExceptions = true ;
445+ TestComponentErrorBuildRenderTree testComponentErrorBuildRenderTree = null ;
446+
447+ // Create root component that wraps the TestComponentErrorBuildRenderTree in an TestErrorBoundary
448+ var rootComponent = new TestComponent ( ) ;
449+ rootComponent . ChildContent = builder =>
450+ {
451+ builder . OpenComponent < TestComponentErrorBuildRenderTree > ( 0 ) ;
452+ builder . AddComponentParameter ( 1 , nameof ( TestComponentErrorBuildRenderTree . FaultedTaskOnInitializedAsync ) , true ) ;
453+ builder . AddComponentReferenceCapture ( 2 , inst => testComponentErrorBuildRenderTree = ( TestComponentErrorBuildRenderTree ) inst ) ;
454+ builder . CloseComponent ( ) ;
455+ } ;
456+
457+ // Act
458+ var rootComponentId = renderer . AssignRootComponentId ( rootComponent ) ;
459+ await renderer . RenderRootComponentAsync ( rootComponentId ) ;
460+
461+ // Assert
462+ Assert . IsType < InvalidTimeZoneException > ( renderer . HandledExceptions [ 0 ] ) ;
463+ Assert . NotNull ( testComponentErrorBuildRenderTree ) ;
464+ Assert . Equal ( 0 , testComponentErrorBuildRenderTree . StateHasChangedCalled ) ;
465+ }
466+
467+ [ Fact ]
468+ public async Task ComponentBaseDoesntRenderWhenOnSetParametersSetAsyncFaultedTask ( )
469+ {
470+ // Arrange
471+ var renderer = new TestRenderer ( ) ;
472+ renderer . ShouldHandleExceptions = true ;
473+ TestComponentErrorBuildRenderTree testComponentErrorBuildRenderTree = null ;
474+
475+ // Create root component that wraps the TestComponentErrorBuildRenderTree in an TestErrorBoundary
476+ var rootComponent = new TestComponent ( ) ;
477+ rootComponent . ChildContent = builder =>
478+ {
479+ builder . OpenComponent < TestComponentErrorBuildRenderTree > ( 0 ) ;
480+ builder . AddComponentParameter ( 1 , nameof ( TestComponentErrorBuildRenderTree . FaultedTaskOnParametersSetAsync ) , true ) ;
481+ builder . AddComponentReferenceCapture ( 2 , inst => testComponentErrorBuildRenderTree = ( TestComponentErrorBuildRenderTree ) inst ) ;
482+ builder . CloseComponent ( ) ;
483+ } ;
484+
485+ // Act
486+ var rootComponentId = renderer . AssignRootComponentId ( rootComponent ) ;
487+ await renderer . RenderRootComponentAsync ( rootComponentId ) ;
488+
489+ // Assert
490+ Assert . IsType < InvalidTimeZoneException > ( renderer . HandledExceptions [ 0 ] ) ;
491+ Assert . NotNull ( testComponentErrorBuildRenderTree ) ;
492+ Assert . Equal ( 0 , testComponentErrorBuildRenderTree . StateHasChangedCalled ) ;
493+ }
494+
375495 [ Fact ]
376496 public async Task DoesNotRenderAfterOnParametersSetAsyncTaskIsCanceled ( )
377497 {
@@ -491,11 +611,20 @@ private class TestComponent : ComponentBase
491611
492612 public int Counter { get ; set ; }
493613
614+ public RenderFragment ChildContent { get ; set ; }
615+
494616 protected override void BuildRenderTree ( RenderTreeBuilder builder )
495617 {
496- builder . OpenElement ( 0 , "p" ) ;
497- builder . AddContent ( 1 , Counter ) ;
498- builder . CloseElement ( ) ;
618+ if ( ChildContent != null )
619+ {
620+ builder . AddContent ( 0 , ChildContent ) ;
621+ }
622+ else
623+ {
624+ builder . OpenElement ( 0 , "p" ) ;
625+ builder . AddContent ( 1 , Counter ) ;
626+ builder . CloseElement ( ) ;
627+ }
499628 }
500629
501630 protected override void OnInitialized ( )
@@ -570,4 +699,65 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
570699 }
571700 }
572701 }
702+
703+ private class TestErrorBoundary : ErrorBoundaryBase
704+ {
705+ public Exception ReceivedException => CurrentException ;
706+
707+ protected override Task OnErrorAsync ( Exception exception )
708+ {
709+ return Task . CompletedTask ;
710+ }
711+
712+ protected override void BuildRenderTree ( RenderTreeBuilder builder )
713+ {
714+ if ( CurrentException == null )
715+ {
716+ builder . AddContent ( 0 , ChildContent ) ;
717+ }
718+ else
719+ {
720+ builder . OpenElement ( 2 , "div" ) ;
721+ builder . AddAttribute ( 3 , "class" , "blazor-error-boundary" ) ;
722+ builder . CloseElement ( ) ;
723+ }
724+ }
725+ }
726+
727+ private class TestComponentErrorBuildRenderTree : ComponentBase
728+ {
729+ [ Parameter ] public bool FaultedTaskOnInitializedAsync { get ; set ; } = false ;
730+ [ Parameter ] public bool FaultedTaskOnParametersSetAsync { get ; set ; } = false ;
731+
732+ public int StateHasChangedCalled { get ; set ; } = 0 ;
733+
734+ protected new void StateHasChanged ( )
735+ {
736+ StateHasChangedCalled ++ ;
737+ base . StateHasChanged ( ) ;
738+ }
739+
740+ protected override void BuildRenderTree ( RenderTreeBuilder builder )
741+ {
742+ throw new InvalidOperationException ( "Error in BuildRenderTree" ) ;
743+ }
744+
745+ protected override Task OnInitializedAsync ( )
746+ {
747+ if ( FaultedTaskOnInitializedAsync )
748+ {
749+ return Task . FromException ( new InvalidTimeZoneException ( ) ) ;
750+ }
751+ return Task . CompletedTask ;
752+ }
753+
754+ protected override Task OnParametersSetAsync ( )
755+ {
756+ if ( FaultedTaskOnParametersSetAsync )
757+ {
758+ return Task . FromException ( new InvalidTimeZoneException ( ) ) ;
759+ }
760+ return Task . CompletedTask ;
761+ }
762+ }
573763}
0 commit comments