1+ using Microsoft . Extensions . Logging ;
12using System . Collections . Concurrent ;
23using System . Diagnostics ;
34using System . Reflection ;
45using System . Runtime . CompilerServices ;
56using System . Runtime . ExceptionServices ;
6- using Microsoft . Extensions . Logging ;
77
88namespace Bunit . Rendering ;
99
@@ -12,9 +12,10 @@ namespace Bunit.Rendering;
1212/// </summary>
1313public sealed class BunitRenderer : Renderer
1414{
15+ private static readonly ConcurrentDictionary < Type , ConstructorInfo > ComponentActivatorCache = new ( ) ;
16+
1517 private readonly BunitServiceProvider services ;
1618 private readonly List < Task > disposalTasks = [ ] ;
17- private static readonly ConcurrentDictionary < Type , ConstructorInfo > componentActivatorCache = new ( ) ;
1819
1920 [ UnsafeAccessor ( UnsafeAccessorKind . Field , Name = "_isBatchInProgress" ) ]
2021 private static extern ref bool GetIsBatchInProgressField ( Renderer renderer ) ;
@@ -60,22 +61,22 @@ private bool IsBatchInProgress
6061 internal int RenderCount { get ; }
6162
6263#if NET9_0_OR_GREATER
63- private RendererInfo ? rendererInfo ;
64-
65- /// <inheritdoc/>
66- [ SuppressMessage (
67- "Design" ,
68- "CA1065:Do not raise exceptions in unexpected locations" ,
69- Justification = "The exception is raised to guide users."
70- ) ]
71- protected override RendererInfo RendererInfo =>
72- rendererInfo ?? throw new MissingRendererInfoException ( ) ;
73-
74- /// <inheritdoc/>
75- public void SetRendererInfo ( RendererInfo ? rendererInfo )
76- {
77- this . rendererInfo = rendererInfo ;
78- }
64+ private RendererInfo ? rendererInfo ;
65+
66+ /// <inheritdoc/>
67+ [ SuppressMessage (
68+ "Design" ,
69+ "CA1065:Do not raise exceptions in unexpected locations" ,
70+ Justification = "The exception is raised to guide users."
71+ ) ]
72+ protected override RendererInfo RendererInfo =>
73+ rendererInfo ?? throw new MissingRendererInfoException ( ) ;
74+
75+ /// <inheritdoc/>
76+ public void SetRendererInfo ( RendererInfo ? rendererInfo )
77+ {
78+ this . rendererInfo = rendererInfo ;
79+ }
7980#endif
8081
8182 /// <summary>
@@ -112,6 +113,21 @@ public BunitRenderer(BunitServiceProvider services, ILoggerFactory loggerFactory
112113 ElementReferenceContext = new WebElementReferenceContext ( services . GetRequiredService < IJSRuntime > ( ) ) ;
113114 }
114115
116+ /// <summary>
117+ /// Renders a <typeparamref name="TComponent"/> with the parameters build with the <paramref name="parameterBuilder"/> passed to it.
118+ /// </summary>
119+ /// <typeparam name = "TComponent" > The type of component to render.</typeparam>
120+ /// <param name="parameterBuilder">The a builder to create parameters to pass to the component.</param>
121+ /// <returns>A <see cref="RenderedComponent{TComponent}"/> that provides access to the rendered component.</returns>
122+ public IRenderedComponent < TComponent > Render < TComponent > ( Action < ComponentParameterCollectionBuilder < TComponent > > ? parameterBuilder = null )
123+ where TComponent : IComponent
124+ {
125+ var builder = new ComponentParameterCollectionBuilder < TComponent > ( parameterBuilder ) ;
126+ var renderFragment = builder . Build ( ) . ToRenderFragment < TComponent > ( ) ;
127+ var renderedComponent = RenderFragment ( renderFragment ) ;
128+ return renderedComponent . FindComponent < TComponent > ( ) ;
129+ }
130+
115131 /// <summary>
116132 /// Renders the <paramref name="renderFragment"/>.
117133 /// </summary>
@@ -255,7 +271,7 @@ protected override ComponentState CreateComponentState(int componentId, ICompone
255271
256272 object CreateComponentInstance ( )
257273 {
258- var constructorInfo = componentActivatorCache . GetOrAdd ( renderedComponentType , type
274+ var constructorInfo = ComponentActivatorCache . GetOrAdd ( renderedComponentType , type
259275 => type . GetConstructor (
260276 [
261277 typeof ( BunitRenderer ) ,
@@ -281,78 +297,78 @@ protected override IComponent ResolveComponentForRenderMode(Type componentType,
281297 }
282298
283299#if NET9_0_OR_GREATER
284- /// <inheritdoc/>
285- protected override IComponentRenderMode ? GetComponentRenderMode ( IComponent component )
286- {
287- ArgumentNullException . ThrowIfNull ( component ) ;
288-
289- // Search from the current component all the way up the render tree.
290- // All components must have the same render mode specified (or none at all).
291- // Return the render mode that is found after checking the full tree.
292- return GetAndValidateRenderMode ( component , childRenderMode : null ) ;
293-
294- IComponentRenderMode ? GetAndValidateRenderMode (
295- IComponent component ,
296- IComponentRenderMode ? childRenderMode
297- )
298- {
299- var componentState = GetComponentState ( component ) ;
300- var renderMode = GetRenderModeForComponent ( componentState ) ;
301-
302- if (
303- childRenderMode is not null
304- && renderMode is not null
305- && childRenderMode != renderMode
306- )
307- {
308- throw new RenderModeMisMatchException ( ) ;
309- }
310-
311- return componentState . ParentComponentState is null
312- ? renderMode ?? childRenderMode
313- : GetAndValidateRenderMode (
314- componentState . ParentComponentState . Component ,
315- renderMode ?? childRenderMode
316- ) ;
317- }
318-
319- IComponentRenderMode ? GetRenderModeForComponent ( ComponentState componentState )
320- {
321- var renderModeAttribute = componentState . Component
322- . GetType ( )
323- . GetCustomAttribute < RenderModeAttribute > ( ) ;
324- if ( renderModeAttribute is { Mode : not null } )
325- {
326- return renderModeAttribute . Mode ;
327- }
328-
329- if ( componentState . ParentComponentState is not null )
330- {
331- var parentFrames = GetCurrentRenderTreeFrames (
332- componentState . ParentComponentState . ComponentId
333- ) ;
334- var foundComponentStart = false ;
335- for ( var i = 0 ; i < parentFrames . Count ; i ++ )
336- {
337- ref var frame = ref parentFrames . Array [ i ] ;
338-
339- if ( frame . FrameType is RenderTreeFrameType . Component )
340- {
341- foundComponentStart = frame . ComponentId == componentState . ComponentId ;
342- }
343- else if (
344- foundComponentStart
345- && frame . FrameType is RenderTreeFrameType . ComponentRenderMode
346- )
347- {
348- return frame . ComponentRenderMode ;
349- }
350- }
351- }
352-
353- return null ;
354- }
355- }
300+ /// <inheritdoc/>
301+ protected override IComponentRenderMode ? GetComponentRenderMode ( IComponent component )
302+ {
303+ ArgumentNullException . ThrowIfNull ( component ) ;
304+
305+ // Search from the current component all the way up the render tree.
306+ // All components must have the same render mode specified (or none at all).
307+ // Return the render mode that is found after checking the full tree.
308+ return GetAndValidateRenderMode ( component , childRenderMode : null ) ;
309+
310+ IComponentRenderMode ? GetAndValidateRenderMode (
311+ IComponent component ,
312+ IComponentRenderMode ? childRenderMode
313+ )
314+ {
315+ var componentState = GetComponentState ( component ) ;
316+ var renderMode = GetRenderModeForComponent ( componentState ) ;
317+
318+ if (
319+ childRenderMode is not null
320+ && renderMode is not null
321+ && childRenderMode != renderMode
322+ )
323+ {
324+ throw new RenderModeMisMatchException ( ) ;
325+ }
326+
327+ return componentState . ParentComponentState is null
328+ ? renderMode ?? childRenderMode
329+ : GetAndValidateRenderMode (
330+ componentState . ParentComponentState . Component ,
331+ renderMode ?? childRenderMode
332+ ) ;
333+ }
334+
335+ IComponentRenderMode ? GetRenderModeForComponent ( ComponentState componentState )
336+ {
337+ var renderModeAttribute = componentState . Component
338+ . GetType ( )
339+ . GetCustomAttribute < RenderModeAttribute > ( ) ;
340+ if ( renderModeAttribute is { Mode : not null } )
341+ {
342+ return renderModeAttribute . Mode ;
343+ }
344+
345+ if ( componentState . ParentComponentState is not null )
346+ {
347+ var parentFrames = GetCurrentRenderTreeFrames (
348+ componentState . ParentComponentState . ComponentId
349+ ) ;
350+ var foundComponentStart = false ;
351+ for ( var i = 0 ; i < parentFrames . Count ; i ++ )
352+ {
353+ ref var frame = ref parentFrames . Array [ i ] ;
354+
355+ if ( frame . FrameType is RenderTreeFrameType . Component )
356+ {
357+ foundComponentStart = frame . ComponentId == componentState . ComponentId ;
358+ }
359+ else if (
360+ foundComponentStart
361+ && frame . FrameType is RenderTreeFrameType . ComponentRenderMode
362+ )
363+ {
364+ return frame . ComponentRenderMode ;
365+ }
366+ }
367+ }
368+
369+ return null ;
370+ }
371+ }
356372#endif
357373
358374 /// <inheritdoc/>
0 commit comments