Skip to content

Commit 38d73fd

Browse files
committed
fix: allow using 3rd party IComponentActivator at the same time as component factories
1 parent 56bcb8a commit 38d73fd

6 files changed

Lines changed: 91 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ All notable changes to **bUnit** will be documented in this file. The project ad
88

99
### Fixed
1010

11+
- 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.
12+
1113
- 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.
1214

1315
## [1.20.8] - 2023-05-21

src/bunit.core/ComponentFactoryCollection.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#if NET5_0_OR_GREATER
22
using System.Collections;
3-
using System.Collections.Generic;
43

54
namespace Bunit;
65

src/bunit.core/Rendering/BunitComponentActivator.cs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
#if NET5_0_OR_GREATER
2-
using System;
3-
using System.Diagnostics.CodeAnalysis;
4-
using Microsoft.AspNetCore.Components;
52

63
namespace Bunit.Rendering;
74

85
internal class BunitComponentActivator : IComponentActivator
96
{
107
private readonly ComponentFactoryCollection factories;
8+
private readonly IComponentActivator componentActivator;
119

12-
public BunitComponentActivator(ComponentFactoryCollection factories)
10+
public BunitComponentActivator(ComponentFactoryCollection factories, IComponentActivator? externalComponentActivator)
1311
{
1412
this.factories = factories ?? throw new ArgumentNullException(nameof(factories));
13+
this.componentActivator = externalComponentActivator ?? DefaultComponentActivator.Instance;
1514
}
1615

1716
public IComponent CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type componentType)
@@ -39,7 +38,18 @@ public IComponent CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessed
3938
}
4039
}
4140

42-
return (IComponent)Activator.CreateInstance(componentType)!;
41+
return componentActivator.CreateInstance(componentType);
42+
}
43+
44+
private sealed class DefaultComponentActivator : IComponentActivator
45+
{
46+
public static IComponentActivator Instance { get; } = new DefaultComponentActivator();
47+
48+
/// <inheritdoc />
49+
public IComponent CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type componentType)
50+
{
51+
return (IComponent)Activator.CreateInstance(componentType)!;
52+
}
4353
}
4454
}
4555
#endif

src/bunit.core/Rendering/TestRenderer.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ private bool IsBatchInProgress
4343
/// </summary>
4444
internal int RenderCount { get; private set; }
4545

46+
#if NETSTANDARD
4647
/// <summary>
4748
/// Initializes a new instance of the <see cref="TestRenderer"/> class.
4849
/// </summary>
@@ -52,13 +53,22 @@ public TestRenderer(IRenderedComponentActivator renderedComponentActivator, Test
5253
logger = loggerFactory.CreateLogger<TestRenderer>();
5354
this.activator = renderedComponentActivator;
5455
}
56+
#elif NET5_0_OR_GREATER
57+
/// <summary>
58+
/// Initializes a new instance of the <see cref="TestRenderer"/> class.
59+
/// </summary>
60+
public TestRenderer(IRenderedComponentActivator renderedComponentActivator, TestServiceProvider services, ILoggerFactory loggerFactory)
61+
: base(services, loggerFactory, new BunitComponentActivator(services.GetRequiredService<ComponentFactoryCollection>(), null))
62+
{
63+
logger = loggerFactory.CreateLogger<TestRenderer>();
64+
this.activator = renderedComponentActivator;
65+
}
5566

56-
#if NET5_0_OR_GREATER
5767
/// <summary>
5868
/// Initializes a new instance of the <see cref="TestRenderer"/> class.
5969
/// </summary>
6070
public TestRenderer(IRenderedComponentActivator renderedComponentActivator, TestServiceProvider services, ILoggerFactory loggerFactory, IComponentActivator componentActivator)
61-
: base(services, loggerFactory, componentActivator)
71+
: base(services, loggerFactory, new BunitComponentActivator(services.GetRequiredService<ComponentFactoryCollection>(), componentActivator))
6272
{
6373
logger = loggerFactory.CreateLogger<TestRenderer>();
6474
this.activator = renderedComponentActivator;

src/bunit.core/TestContextBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ protected TestContextBase()
5454
{
5555
Services = new TestServiceProvider();
5656
#if NET5_0_OR_GREATER
57-
Services.AddSingleton<IComponentActivator>(new BunitComponentActivator(ComponentFactories));
57+
Services.AddSingleton<ComponentFactoryCollection>(_ => ComponentFactories);
5858
#endif
5959
}
6060

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#if NET5_0_OR_GREATER
2+
3+
using Bunit.TestDoubles;
4+
5+
namespace Bunit.Rendering;
6+
7+
public class BunitComponentActivatorTest : TestContext
8+
{
9+
[Fact(DisplayName = "Default activator")]
10+
public void Test001()
11+
{
12+
var activator = new CustomComponentActivator();
13+
Services.AddSingleton<IComponentActivator>(activator);
14+
15+
var cut = RenderComponent<Simple1>();
16+
17+
cut.Instance.ShouldBeOfType<Simple1>();
18+
}
19+
20+
[Fact(DisplayName = "Custom singleton IComponentActivator registered in Services")]
21+
public void Test002()
22+
{
23+
var activator = new CustomComponentActivator();
24+
Services.AddSingleton<IComponentActivator>(activator);
25+
26+
RenderComponent<Simple1>();
27+
28+
activator.RequestedComponentTypes
29+
.ShouldHaveSingleItem()
30+
.ShouldBe(typeof(Simple1));
31+
}
32+
33+
[Fact(DisplayName = "Custom singleton IComponentActivator registered in Services with ComponentFactories in use")]
34+
public void Test003()
35+
{
36+
var activator = new CustomComponentActivator();
37+
Services.AddSingleton<IComponentActivator>(activator);
38+
ComponentFactories.AddStub<ClickCounter>();
39+
40+
var cut = RenderComponent<Wrapper>(ps => ps.AddChildContent<ClickCounter>());
41+
42+
activator.RequestedComponentTypes
43+
.ShouldHaveSingleItem()
44+
.ShouldBe(typeof(Wrapper));
45+
cut.HasComponent<ClickCounter>().ShouldBeFalse();
46+
cut.HasComponent<Stub<ClickCounter>>().ShouldBeTrue();
47+
}
48+
49+
private sealed class CustomComponentActivator : IComponentActivator
50+
{
51+
public List<Type> RequestedComponentTypes { get; } = new();
52+
53+
public IComponent CreateInstance(Type componentType)
54+
{
55+
RequestedComponentTypes.Add(componentType);
56+
return (IComponent)Activator.CreateInstance(componentType)!;
57+
}
58+
}
59+
}
60+
61+
#endif

0 commit comments

Comments
 (0)