@@ -718,6 +718,133 @@ public async Task CanPrerender_ComponentWithNullParameters_ServerPrerenderedMode
718718 Assert . Null ( epilogueMarker . Type ) ;
719719 }
720720
721+ [ Fact ]
722+ public async Task CanRender_ClosedGenericComponent ( )
723+ {
724+ // Arrange
725+ var httpContext = GetHttpContext ( ) ;
726+ var writer = new StringWriter ( ) ;
727+
728+ // Act
729+ var parameters = ParameterView . FromDictionary ( new Dictionary < string , object > { { "Value" , 42 } } ) ;
730+ var result = await renderer . PrerenderComponentAsync ( httpContext , typeof ( GenericComponent < int > ) , null , parameters ) ;
731+ await renderer . Dispatcher . InvokeAsync ( ( ) => result . WriteTo ( writer , HtmlEncoder . Default ) ) ;
732+ var content = writer . ToString ( ) ;
733+
734+ // Assert
735+ Assert . Equal ( "<p>Generic value: 42</p>" , content ) ;
736+ }
737+
738+ [ Fact ]
739+ public async Task CanRender_ClosedGenericComponent_ServerMode ( )
740+ {
741+ // Arrange
742+ var httpContext = GetHttpContext ( ) ;
743+ var protector = _dataprotectorProvider . CreateProtector ( ServerComponentSerializationSettings . DataProtectionProviderPurpose )
744+ . ToTimeLimitedDataProtector ( ) ;
745+
746+ // Act
747+ var parameters = ParameterView . FromDictionary ( new Dictionary < string , object > { { "Value" , "TestString" } } ) ;
748+ var result = await renderer . PrerenderComponentAsync ( httpContext , typeof ( GenericComponent < string > ) , new InteractiveServerRenderMode ( false ) , parameters ) ;
749+ var content = await renderer . Dispatcher . InvokeAsync ( ( ) => HtmlContentToString ( result ) ) ;
750+ var match = Regex . Match ( content , ComponentPattern ) ;
751+
752+ // Assert
753+ Assert . True ( match . Success ) ;
754+ var marker = JsonSerializer . Deserialize < ComponentMarker > ( match . Groups [ 1 ] . Value , ServerComponentSerializationSettings . JsonSerializationOptions ) ;
755+ Assert . Equal ( 0 , marker . Sequence ) ;
756+ Assert . Null ( marker . PrerenderId ) ;
757+ Assert . NotNull ( marker . Descriptor ) ;
758+ Assert . Equal ( "server" , marker . Type ) ;
759+
760+ var unprotectedServerComponent = protector . Unprotect ( marker . Descriptor ) ;
761+ var serverComponent = JsonSerializer . Deserialize < ServerComponent > ( unprotectedServerComponent , ServerComponentSerializationSettings . JsonSerializationOptions ) ;
762+ Assert . Equal ( 0 , serverComponent . Sequence ) ;
763+ Assert . Equal ( typeof ( GenericComponent < string > ) . Assembly . GetName ( ) . Name , serverComponent . AssemblyName ) ;
764+ Assert . Equal ( typeof ( GenericComponent < string > ) . FullName , serverComponent . TypeName ) ;
765+ Assert . NotEqual ( Guid . Empty , serverComponent . InvocationId ) ;
766+
767+ var parameterDefinition = Assert . Single ( serverComponent . ParameterDefinitions ) ;
768+ Assert . Equal ( "Value" , parameterDefinition . Name ) ;
769+ Assert . Equal ( "System.String" , parameterDefinition . TypeName ) ;
770+ Assert . Equal ( "System.Private.CoreLib" , parameterDefinition . Assembly ) ;
771+
772+ var value = Assert . Single ( serverComponent . ParameterValues ) ;
773+ var rawValue = Assert . IsType < JsonElement > ( value ) ;
774+ Assert . Equal ( "TestString" , rawValue . GetString ( ) ) ;
775+ }
776+
777+ [ Fact ]
778+ public async Task CanPrerender_ClosedGenericComponent_ServerMode ( )
779+ {
780+ // Arrange
781+ var httpContext = GetHttpContext ( ) ;
782+ var protector = _dataprotectorProvider . CreateProtector ( ServerComponentSerializationSettings . DataProtectionProviderPurpose )
783+ . ToTimeLimitedDataProtector ( ) ;
784+
785+ // Act
786+ var parameters = ParameterView . FromDictionary ( new Dictionary < string , object > { { "Value" , 123 } } ) ;
787+ var result = await renderer . PrerenderComponentAsync ( httpContext , typeof ( GenericComponent < int > ) , RenderMode . InteractiveServer , parameters ) ;
788+ var content = await renderer . Dispatcher . InvokeAsync ( ( ) => HtmlContentToString ( result ) ) ;
789+ var match = Regex . Match ( content , PrerenderedComponentPattern , RegexOptions . Multiline ) ;
790+
791+ // Assert
792+ Assert . True ( match . Success ) ;
793+ var preamble = match . Groups [ "preamble" ] . Value ;
794+ var preambleMarker = JsonSerializer . Deserialize < ComponentMarker > ( preamble , ServerComponentSerializationSettings . JsonSerializationOptions ) ;
795+ Assert . Equal ( 0 , preambleMarker . Sequence ) ;
796+ Assert . NotNull ( preambleMarker . PrerenderId ) ;
797+ Assert . NotNull ( preambleMarker . Descriptor ) ;
798+ Assert . Equal ( "server" , preambleMarker . Type ) ;
799+
800+ var unprotectedServerComponent = protector . Unprotect ( preambleMarker . Descriptor ) ;
801+ var serverComponent = JsonSerializer . Deserialize < ServerComponent > ( unprotectedServerComponent , ServerComponentSerializationSettings . JsonSerializationOptions ) ;
802+ Assert . NotEqual ( default , serverComponent ) ;
803+ Assert . Equal ( 0 , serverComponent . Sequence ) ;
804+ Assert . Equal ( typeof ( GenericComponent < int > ) . Assembly . GetName ( ) . Name , serverComponent . AssemblyName ) ;
805+ Assert . Equal ( typeof ( GenericComponent < int > ) . FullName , serverComponent . TypeName ) ;
806+ Assert . NotEqual ( Guid . Empty , serverComponent . InvocationId ) ;
807+
808+ var prerenderedContent = match . Groups [ "content" ] . Value ;
809+ Assert . Equal ( "<p>Generic value: 123</p>" , prerenderedContent ) ;
810+
811+ var epilogue = match . Groups [ "epilogue" ] . Value ;
812+ var epilogueMarker = JsonSerializer . Deserialize < ComponentMarker > ( epilogue , ServerComponentSerializationSettings . JsonSerializationOptions ) ;
813+ Assert . Equal ( preambleMarker . PrerenderId , epilogueMarker . PrerenderId ) ;
814+ }
815+
816+ [ Fact ]
817+ public async Task CanPrerender_ClosedGenericComponent_ClientMode ( )
818+ {
819+ // Arrange
820+ var httpContext = GetHttpContext ( ) ;
821+ var writer = new StringWriter ( ) ;
822+
823+ // Act
824+ var parameters = ParameterView . FromDictionary ( new Dictionary < string , object > { { "Value" , 456 } } ) ;
825+ var result = await renderer . PrerenderComponentAsync ( httpContext , typeof ( GenericComponent < int > ) , RenderMode . InteractiveWebAssembly , parameters ) ;
826+ await renderer . Dispatcher . InvokeAsync ( ( ) => result . WriteTo ( writer , HtmlEncoder . Default ) ) ;
827+ var content = writer . ToString ( ) ;
828+ content = AssertAndStripWebAssemblyOptions ( content ) ;
829+ var match = Regex . Match ( content , PrerenderedComponentPattern , RegexOptions . Multiline ) ;
830+
831+ // Assert
832+ Assert . True ( match . Success ) ;
833+ var preamble = match . Groups [ "preamble" ] . Value ;
834+ var preambleMarker = JsonSerializer . Deserialize < ComponentMarker > ( preamble , ServerComponentSerializationSettings . JsonSerializationOptions ) ;
835+ Assert . NotNull ( preambleMarker . PrerenderId ) ;
836+ Assert . Equal ( "webassembly" , preambleMarker . Type ) ;
837+ Assert . Equal ( typeof ( GenericComponent < int > ) . Assembly . GetName ( ) . Name , preambleMarker . Assembly ) ;
838+ Assert . Equal ( typeof ( GenericComponent < int > ) . FullName , preambleMarker . TypeName ) ;
839+
840+ var prerenderedContent = match . Groups [ "content" ] . Value ;
841+ Assert . Equal ( "<p>Generic value: 456</p>" , prerenderedContent ) ;
842+
843+ var epilogue = match . Groups [ "epilogue" ] . Value ;
844+ var epilogueMarker = JsonSerializer . Deserialize < ComponentMarker > ( epilogue , ServerComponentSerializationSettings . JsonSerializationOptions ) ;
845+ Assert . Equal ( preambleMarker . PrerenderId , epilogueMarker . PrerenderId ) ;
846+ }
847+
721848 [ Fact ]
722849 public async Task ComponentWithInvalidRenderMode_Throws ( )
723850 {
0 commit comments