Skip to content

Commit

Permalink
fix: allow using 3rd party IComponentActivator at the same time as co…
Browse files Browse the repository at this point in the history
…mponent factories
  • Loading branch information
egil committed Jul 2, 2023
1 parent 56bcb8a commit 38d73fd
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 9 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ All notable changes to **bUnit** will be documented in this file. The project ad

### Fixed

- Allow using 3rd party `IComponentActivator` at the same time as component factories. By [@egil](https://github.com/egil). Reported by [BenSchoen](https://github.com/BenSchoen) in https://github.com/bUnit-dev/bUnit/issues/1129.

- Calling `IRenderedComponent.Render()` or `IRenderedComponent.SetParametersAndRender()` did not batch up multiple synchronous re-renders after setting parameters. This is now changed such that the method causes the component to re-render with new parameters in the same way as if a parent component had passed new parameters to it. By [@egil](https://github.com/egil). Reported by [@Jcparkyn](https://github.com/Jcparkyn) in https://github.com/bUnit-dev/bUnit/issues/1119.

## [1.20.8] - 2023-05-21
Expand Down
1 change: 0 additions & 1 deletion src/bunit.core/ComponentFactoryCollection.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#if NET5_0_OR_GREATER
using System.Collections;
using System.Collections.Generic;

namespace Bunit;

Expand Down
20 changes: 15 additions & 5 deletions src/bunit.core/Rendering/BunitComponentActivator.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
#if NET5_0_OR_GREATER
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Components;

namespace Bunit.Rendering;

internal class BunitComponentActivator : IComponentActivator
{
private readonly ComponentFactoryCollection factories;
private readonly IComponentActivator componentActivator;

public BunitComponentActivator(ComponentFactoryCollection factories)
public BunitComponentActivator(ComponentFactoryCollection factories, IComponentActivator? externalComponentActivator)
{
this.factories = factories ?? throw new ArgumentNullException(nameof(factories));
this.componentActivator = externalComponentActivator ?? DefaultComponentActivator.Instance;
}

public IComponent CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type componentType)
Expand Down Expand Up @@ -39,7 +38,18 @@ public IComponent CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessed
}
}

return (IComponent)Activator.CreateInstance(componentType)!;
return componentActivator.CreateInstance(componentType);
}

private sealed class DefaultComponentActivator : IComponentActivator
{
public static IComponentActivator Instance { get; } = new DefaultComponentActivator();

/// <inheritdoc />
public IComponent CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type componentType)
{
return (IComponent)Activator.CreateInstance(componentType)!;
}
}
}
#endif
14 changes: 12 additions & 2 deletions src/bunit.core/Rendering/TestRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ private bool IsBatchInProgress
/// </summary>
internal int RenderCount { get; private set; }

#if NETSTANDARD
/// <summary>
/// Initializes a new instance of the <see cref="TestRenderer"/> class.
/// </summary>
Expand All @@ -52,13 +53,22 @@ public TestRenderer(IRenderedComponentActivator renderedComponentActivator, Test
logger = loggerFactory.CreateLogger<TestRenderer>();
this.activator = renderedComponentActivator;
}
#elif NET5_0_OR_GREATER
/// <summary>
/// Initializes a new instance of the <see cref="TestRenderer"/> class.
/// </summary>
public TestRenderer(IRenderedComponentActivator renderedComponentActivator, TestServiceProvider services, ILoggerFactory loggerFactory)
: base(services, loggerFactory, new BunitComponentActivator(services.GetRequiredService<ComponentFactoryCollection>(), null))
{
logger = loggerFactory.CreateLogger<TestRenderer>();
this.activator = renderedComponentActivator;
}

#if NET5_0_OR_GREATER
/// <summary>
/// Initializes a new instance of the <see cref="TestRenderer"/> class.
/// </summary>
public TestRenderer(IRenderedComponentActivator renderedComponentActivator, TestServiceProvider services, ILoggerFactory loggerFactory, IComponentActivator componentActivator)
: base(services, loggerFactory, componentActivator)
: base(services, loggerFactory, new BunitComponentActivator(services.GetRequiredService<ComponentFactoryCollection>(), componentActivator))
{
logger = loggerFactory.CreateLogger<TestRenderer>();
this.activator = renderedComponentActivator;
Expand Down
2 changes: 1 addition & 1 deletion src/bunit.core/TestContextBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ protected TestContextBase()
{
Services = new TestServiceProvider();
#if NET5_0_OR_GREATER
Services.AddSingleton<IComponentActivator>(new BunitComponentActivator(ComponentFactories));
Services.AddSingleton<ComponentFactoryCollection>(_ => ComponentFactories);
#endif
}

Expand Down
61 changes: 61 additions & 0 deletions tests/bunit.core.tests/Rendering/BunitComponentActivatorTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#if NET5_0_OR_GREATER

using Bunit.TestDoubles;

namespace Bunit.Rendering;

public class BunitComponentActivatorTest : TestContext
{
[Fact(DisplayName = "Default activator")]
public void Test001()
{
var activator = new CustomComponentActivator();
Services.AddSingleton<IComponentActivator>(activator);

var cut = RenderComponent<Simple1>();

cut.Instance.ShouldBeOfType<Simple1>();
}

[Fact(DisplayName = "Custom singleton IComponentActivator registered in Services")]
public void Test002()
{
var activator = new CustomComponentActivator();
Services.AddSingleton<IComponentActivator>(activator);

RenderComponent<Simple1>();

activator.RequestedComponentTypes
.ShouldHaveSingleItem()
.ShouldBe(typeof(Simple1));
}

[Fact(DisplayName = "Custom singleton IComponentActivator registered in Services with ComponentFactories in use")]
public void Test003()
{
var activator = new CustomComponentActivator();
Services.AddSingleton<IComponentActivator>(activator);
ComponentFactories.AddStub<ClickCounter>();

var cut = RenderComponent<Wrapper>(ps => ps.AddChildContent<ClickCounter>());

activator.RequestedComponentTypes
.ShouldHaveSingleItem()
.ShouldBe(typeof(Wrapper));
cut.HasComponent<ClickCounter>().ShouldBeFalse();
cut.HasComponent<Stub<ClickCounter>>().ShouldBeTrue();
}

private sealed class CustomComponentActivator : IComponentActivator
{
public List<Type> RequestedComponentTypes { get; } = new();

public IComponent CreateInstance(Type componentType)
{
RequestedComponentTypes.Add(componentType);
return (IComponent)Activator.CreateInstance(componentType)!;
}
}
}

#endif

0 comments on commit 38d73fd

Please sign in to comment.