2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
4
using System . Diagnostics ;
5
+ using System . Globalization ;
5
6
using System . Text . Json ;
6
7
using Microsoft . AspNetCore . Components . Endpoints ;
7
8
using Microsoft . AspNetCore . Components . Rendering ;
@@ -24,6 +25,8 @@ public class RemoteRendererTest
24
25
// failures.
25
26
private static readonly TimeSpan Timeout = Debugger . IsAttached ? System . Threading . Timeout . InfiniteTimeSpan : TimeSpan . FromSeconds ( 10 ) ;
26
27
28
+ private const int MaxInteractiveServerRootComponentCount = 3 ;
29
+
27
30
private readonly IDataProtectionProvider _ephemeralDataProtectionProvider = new EphemeralDataProtectionProvider ( ) ;
28
31
29
32
[ Fact ]
@@ -425,6 +428,210 @@ await renderer.Dispatcher.InvokeAsync(() => renderer.RenderComponentAsync<AutoPa
425
428
exception . Message ) ;
426
429
}
427
430
431
+ [ Fact ]
432
+ public async Task WebRootComponentManager_AddRootComponentAsync_Throws_IfMaxInteractiveServerComponentCountIsExceeded ( )
433
+ {
434
+ // Arrange
435
+ var serviceProvider = CreateServiceProvider ( ) ;
436
+ var renderer = GetRemoteRenderer ( serviceProvider ) ;
437
+
438
+ // Act
439
+ for ( var i = 0 ; i < MaxInteractiveServerRootComponentCount ; i ++ )
440
+ {
441
+ await AddWebRootComponentAsync ( renderer , i ) ;
442
+ }
443
+
444
+ // Assert
445
+ var ex = await Assert . ThrowsAsync < InvalidOperationException > ( ( ) => AddWebRootComponentAsync ( renderer , MaxInteractiveServerRootComponentCount ) ) ;
446
+
447
+ Assert . Equal ( "Exceeded the maximum number of allowed server interactive root components." , ex . Message ) ;
448
+ }
449
+
450
+ [ Fact ]
451
+ public async Task WebRootComponentManager_AddRootComponentAsync_Throws_IfDuplicateSsrComponentIdIsProvided ( )
452
+ {
453
+ // Arrange
454
+ var serviceProvider = CreateServiceProvider ( ) ;
455
+ var renderer = GetRemoteRenderer ( serviceProvider ) ;
456
+
457
+ // Act
458
+ await AddWebRootComponentAsync ( renderer , 0 ) ;
459
+
460
+ // Assert
461
+ var ex = await Assert . ThrowsAsync < InvalidOperationException > ( ( ) => AddWebRootComponentAsync ( renderer , 0 ) ) ;
462
+
463
+ Assert . Equal ( "A root component with SSR component ID 0 already exists." , ex . Message ) ;
464
+ }
465
+
466
+ [ Fact ]
467
+ public async Task WebRootComponentManager_AddRootComponentAsync_Throws_IfKeyIsMalformed ( )
468
+ {
469
+ // Arrange
470
+ var serviceProvider = CreateServiceProvider ( ) ;
471
+ var renderer = GetRemoteRenderer ( serviceProvider ) ;
472
+
473
+ // Act/assert
474
+ var ex = await Assert . ThrowsAsync < InvalidOperationException > ( async ( ) =>
475
+ {
476
+ var webRootComponentManager = renderer . GetOrCreateWebRootComponentManager ( ) ;
477
+ await webRootComponentManager . AddRootComponentAsync (
478
+ 0 ,
479
+ typeof ( TestComponent ) ,
480
+ "malformed-key" ,
481
+ WebRootComponentParameters . Empty ) ;
482
+ } ) ;
483
+
484
+ Assert . Equal ( "The key 'malformed-key' had an invalid format." , ex . Message ) ;
485
+ }
486
+
487
+ [ Fact ]
488
+ public async Task WebRootComponentManager_AddRootComponentAsync_CanAddAndRenderRootComponent ( )
489
+ {
490
+ // Arrange
491
+ var serviceProvider = CreateServiceProvider ( ) ;
492
+ var renderer = GetRemoteRenderer ( serviceProvider ) ;
493
+
494
+ // Act
495
+ await AddWebRootComponentAsync ( renderer , 0 ) ;
496
+
497
+ // Assert
498
+ Assert . Single ( renderer . _unacknowledgedRenderBatches ) ;
499
+ }
500
+
501
+ [ Fact ]
502
+ public async Task WebRootComponentManager_UpdateRootComponentAsync_Throws_IfSsrComponentIdIsInvalid ( )
503
+ {
504
+ // Arrange
505
+ var serviceProvider = CreateServiceProvider ( ) ;
506
+ var renderer = GetRemoteRenderer ( serviceProvider ) ;
507
+
508
+ // Act
509
+ var key = await AddWebRootComponentAsync ( renderer , 0 ) ;
510
+
511
+ // Assert
512
+ var ex = await Assert . ThrowsAsync < InvalidOperationException > ( async ( ) =>
513
+ {
514
+ var webRootComponentManager = renderer . GetOrCreateWebRootComponentManager ( ) ;
515
+ await webRootComponentManager . UpdateRootComponentAsync ( 1 , key . ToString ( ) , WebRootComponentParameters . Empty ) ;
516
+ } ) ;
517
+
518
+ Assert . Equal ( $ "No root component exists with SSR component ID 1.", ex . Message ) ;
519
+ }
520
+
521
+ [ Fact ]
522
+ public async Task WebRootComponentManager_UpdateRootComponentAsync_Throws_IfKeyDoesNotMatch ( )
523
+ {
524
+ // Arrange
525
+ var serviceProvider = CreateServiceProvider ( ) ;
526
+ var renderer = GetRemoteRenderer ( serviceProvider ) ;
527
+
528
+ // Act
529
+ await AddWebRootComponentAsync ( renderer , 0 ) ;
530
+
531
+ // Assert
532
+ var ex = await Assert . ThrowsAsync < InvalidOperationException > ( async ( ) =>
533
+ {
534
+ var webRootComponentManager = renderer . GetOrCreateWebRootComponentManager ( ) ;
535
+ await webRootComponentManager . UpdateRootComponentAsync ( 0 , "invalid-key" , WebRootComponentParameters . Empty ) ;
536
+ } ) ;
537
+
538
+ Assert . Equal ( "Cannot update components with mismatching keys." , ex . Message ) ;
539
+ }
540
+
541
+ [ Fact ]
542
+ public async Task WebRootComponentManager_UpdateRootComponentAsync_Works_IfComponentKeyWasSupplied ( )
543
+ {
544
+ // Arrange
545
+ var serviceProvider = CreateServiceProvider ( ) ;
546
+ var renderer = GetRemoteRenderer ( serviceProvider ) ;
547
+
548
+ // Act
549
+ var key = await AddWebRootComponentAsync ( renderer , 0 , "mykey" ) ;
550
+ await renderer . Dispatcher . InvokeAsync ( ( ) =>
551
+ {
552
+ var webRootComponentManager = renderer . GetOrCreateWebRootComponentManager ( ) ;
553
+ var parameters = new Dictionary < string , object > { [ "Name" ] = "value" } ;
554
+ webRootComponentManager . UpdateRootComponentAsync ( 0 , key . ToString ( ) , CreateWebRootComponentParameters ( parameters ) ) ;
555
+ } ) ;
556
+
557
+ // Assert
558
+ Assert . Equal ( 2 , renderer . _unacknowledgedRenderBatches . Count ) ; // Initial render, re-render
559
+ }
560
+
561
+ [ Fact ]
562
+ public async Task WebRootComponentManager_UpdateRootComponentAsync_DoesNothing_IfNoComponentKeyWasSuppliedAndParametersDidNotChange ( )
563
+ {
564
+ // Arrange
565
+ var serviceProvider = CreateServiceProvider ( ) ;
566
+ var renderer = GetRemoteRenderer ( serviceProvider ) ;
567
+
568
+ // Act
569
+ var key = await AddWebRootComponentAsync ( renderer , 0 ) ;
570
+ await renderer . Dispatcher . InvokeAsync ( ( ) =>
571
+ {
572
+ var webRootComponentManager = renderer . GetOrCreateWebRootComponentManager ( ) ;
573
+ webRootComponentManager . UpdateRootComponentAsync ( 0 , key . ToString ( ) , WebRootComponentParameters . Empty ) ;
574
+ } ) ;
575
+
576
+ // Assert
577
+ Assert . Single ( renderer . _unacknowledgedRenderBatches ) ;
578
+ }
579
+
580
+ [ Fact ]
581
+ public async Task WebRootComponentManager_UpdateRootComponentAsync_ReinitializesComponent_IfNoComponentKeyWasSuppliedAndParameterChanged ( )
582
+ {
583
+ // Arrange
584
+ var serviceProvider = CreateServiceProvider ( ) ;
585
+ var renderer = GetRemoteRenderer ( serviceProvider ) ;
586
+
587
+ // Act
588
+ var key = await AddWebRootComponentAsync ( renderer , 0 ) ;
589
+ await renderer . Dispatcher . InvokeAsync ( ( ) =>
590
+ {
591
+ var webRootComponentManager = renderer . GetOrCreateWebRootComponentManager ( ) ;
592
+ var parameters = new Dictionary < string , object > { [ "Name" ] = "value" } ;
593
+ webRootComponentManager . UpdateRootComponentAsync ( 0 , key . ToString ( ) , CreateWebRootComponentParameters ( parameters ) ) ;
594
+ } ) ;
595
+
596
+ // Assert
597
+ Assert . Equal ( 3 , renderer . _unacknowledgedRenderBatches . Count ) ; // Initial render, dispose, and re-initialize
598
+ }
599
+
600
+ [ Fact ]
601
+ public async Task WebRootComponentManager_RemoveRootComponent_Throws_IfSsrComponentIdIsInvalid ( )
602
+ {
603
+ // Arrange
604
+ var serviceProvider = CreateServiceProvider ( ) ;
605
+ var renderer = GetRemoteRenderer ( serviceProvider ) ;
606
+
607
+ // Act
608
+ var key = await AddWebRootComponentAsync ( renderer , 0 ) ;
609
+
610
+ // Assert
611
+ var ex = Assert . Throws < InvalidOperationException > ( ( ) => renderer . GetOrCreateWebRootComponentManager ( ) . RemoveRootComponent ( 1 ) ) ;
612
+
613
+ Assert . Equal ( $ "No root component exists with SSR component ID 1.", ex . Message ) ;
614
+ }
615
+
616
+ [ Fact ]
617
+ public async Task WebRootComponentManager_RemoveRootComponent_Works ( )
618
+ {
619
+ // Arrange
620
+ var serviceProvider = CreateServiceProvider ( ) ;
621
+ var renderer = GetRemoteRenderer ( serviceProvider ) ;
622
+
623
+ // Act
624
+ var key = await AddWebRootComponentAsync ( renderer , 0 ) ;
625
+ await renderer . Dispatcher . InvokeAsync ( ( ) =>
626
+ {
627
+ var webRootComponentManager = renderer . GetOrCreateWebRootComponentManager ( ) ;
628
+ webRootComponentManager . RemoveRootComponent ( 0 ) ;
629
+ } ) ;
630
+
631
+ // Assert
632
+ Assert . Equal ( 2 , renderer . _unacknowledgedRenderBatches . Count ) ; // Initial render, dispose
633
+ }
634
+
428
635
private IServiceProvider CreateServiceProvider ( )
429
636
{
430
637
var serviceCollection = new ServiceCollection ( ) ;
@@ -445,12 +652,50 @@ private TestRemoteRenderer GetRemoteRenderer(IServiceProvider serviceProvider, C
445
652
return new TestRemoteRenderer (
446
653
serviceProvider ,
447
654
NullLoggerFactory . Instance ,
448
- new CircuitOptions ( ) ,
655
+ new CircuitOptions
656
+ {
657
+ RootComponents =
658
+ {
659
+ MaxInteractiveServerRootComponentCount = MaxInteractiveServerRootComponentCount
660
+ } ,
661
+ } ,
449
662
circuitClient ?? new CircuitClientProxy ( ) ,
450
663
serverComponentDeserializer ,
451
664
NullLogger . Instance ) ;
452
665
}
453
666
667
+ private static Task < BoundaryMarkerKey > AddWebRootComponentAsync ( RemoteRenderer renderer , int ssrComponentId , string componentKey = null )
668
+ => renderer . Dispatcher . InvokeAsync ( async ( ) =>
669
+ {
670
+ var webRootComponentManager = renderer . GetOrCreateWebRootComponentManager ( ) ;
671
+ var boundaryMarkerKey = new BoundaryMarkerKey (
672
+ nameof ( TestComponent ) . AsMemory ( ) ,
673
+ ssrComponentId . ToString ( CultureInfo . CurrentCulture ) . AsMemory ( ) ,
674
+ componentKey ? . AsMemory ( ) ?? ReadOnlyMemory < char > . Empty ) ;
675
+ await webRootComponentManager . AddRootComponentAsync (
676
+ ssrComponentId ,
677
+ typeof ( TestComponent ) ,
678
+ boundaryMarkerKey . ToString ( ) ,
679
+ WebRootComponentParameters . Empty ) ;
680
+ return boundaryMarkerKey ;
681
+ } ) ;
682
+
683
+ private static WebRootComponentParameters CreateWebRootComponentParameters ( IDictionary < string , object > parameters )
684
+ {
685
+ var parameterView = ParameterView . FromDictionary ( parameters ) ;
686
+ var ( parameterDefinitions , parameterValues ) = ComponentParameter . FromParameterView ( parameterView ) ;
687
+ for ( var i = 0 ; i < parameterValues . Count ; i ++ )
688
+ {
689
+ // WebRootComponentParameters expects serialized parameter values to be JsonElements.
690
+ var jsonElement = JsonSerializer . SerializeToElement ( parameterValues [ i ] ) ;
691
+ parameterValues [ i ] = jsonElement ;
692
+ }
693
+ return new WebRootComponentParameters (
694
+ parameterView ,
695
+ parameterDefinitions . AsReadOnly ( ) ,
696
+ parameterValues . AsReadOnly ( ) ) ;
697
+ }
698
+
454
699
private class TestRemoteRenderer : RemoteRenderer
455
700
{
456
701
public TestRemoteRenderer ( IServiceProvider serviceProvider , ILoggerFactory loggerFactory , CircuitOptions options , CircuitClientProxy client , IServerComponentDeserializer serverComponentDeserializer , ILogger logger )
0 commit comments