Skip to content

Commit ec2ae83

Browse files
committed
feat: additional ComponentFactories.Add methods for easy integration points for mocking libraries
1 parent e5ee0e7 commit ec2ae83

12 files changed

Lines changed: 403 additions & 208 deletions
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#if NET5_0_OR_GREATER
2+
namespace Bunit.ComponentFactories;
3+
4+
internal sealed class ConditionalComponentFactory : IComponentFactory
5+
{
6+
private readonly Predicate<Type> condition;
7+
private readonly Func<Type, IComponent> factory;
8+
9+
public ConditionalComponentFactory(Predicate<Type> condition, Func<Type, IComponent> factory)
10+
{
11+
this.condition = condition;
12+
this.factory = factory;
13+
}
14+
15+
public bool CanCreate(Type componentType)
16+
=> condition(componentType);
17+
18+
public IComponent Create(Type componentType)
19+
=> factory(componentType);
20+
}
21+
#endif

src/bunit.core/ComponentFactories/GenericComponentFactory{TComponent,TReplacementComponent}.cs renamed to src/bunit.core/ComponentFactories/GenericComponentFactory{TComponent,TSubstituteComponent}.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
#if NET5_0_OR_GREATER
2-
using System;
3-
using Microsoft.AspNetCore.Components;
4-
52
namespace Bunit.ComponentFactories;
63

7-
internal sealed class GenericComponentFactory<TComponent, TReplacementComponent> : IComponentFactory
4+
internal sealed class GenericComponentFactory<TComponent, TSubstituteComponent> : IComponentFactory
85
where TComponent : IComponent
9-
where TReplacementComponent : IComponent
6+
where TSubstituteComponent : IComponent
107
{
118
public bool CanCreate(Type componentType) => componentType == typeof(TComponent);
12-
public IComponent Create(Type componentType) => Activator.CreateInstance<TReplacementComponent>()!;
9+
10+
public IComponent Create(Type componentType) => Activator.CreateInstance<TSubstituteComponent>()!;
1311
}
1412
#endif

src/bunit.core/ComponentFactories/InstanceComponentFactory.cs renamed to src/bunit.core/ComponentFactories/InstanceComponentFactory{TComponent}.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
#if NET5_0_OR_GREATER
2-
using System;
3-
using Microsoft.AspNetCore.Components;
4-
52
namespace Bunit.ComponentFactories;
63

74
internal sealed class InstanceComponentFactory<TComponent> : IComponentFactory
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#if NET5_0_OR_GREATER
2+
namespace Bunit.ComponentFactories;
3+
4+
internal sealed class TypeBasedComponentFactory<TComponent> : IComponentFactory
5+
where TComponent : IComponent
6+
{
7+
private readonly Func<TComponent> componentFactory;
8+
9+
public TypeBasedComponentFactory(Func<TComponent> componentFactory)
10+
=> this.componentFactory = componentFactory;
11+
12+
public bool CanCreate(Type componentType)
13+
=> componentType == typeof(TComponent);
14+
15+
public IComponent Create(Type componentType)
16+
=> componentFactory.Invoke();
17+
}
18+
#endif
Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
#if NET5_0_OR_GREATER
2-
using System;
32
using Bunit.ComponentFactories;
4-
using Microsoft.AspNetCore.Components;
53

64
namespace Bunit;
75

@@ -10,35 +8,35 @@ namespace Bunit;
108
/// </summary>
119
public static class ComponentFactoryCollectionExtensions
1210
{
13-
/// <summary>
14-
/// Configures bUnit to replace all components of type <typeparamref name="TComponent"/> with a component
15-
/// of type <typeparamref name="TReplacementComponent"/>.
11+
/// <summary>
12+
/// Configures bUunit to substitute all components of type <typeparamref name="TComponent"/>
13+
/// with components of type <typeparamref name="TSubstituteComponent"/>.
1614
/// </summary>
1715
/// <typeparam name="TComponent">Type of component to replace.</typeparam>
18-
/// <typeparam name="TReplacementComponent">Type of component to replace with.</typeparam>
16+
/// <typeparam name="TSubstituteComponent">Type of component to substitute with.</typeparam>
1917
/// <param name="factories">The bUnit <see cref="ComponentFactoryCollection"/> to configure.</param>
18+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="factories"/> is null.</exception>
2019
/// <returns>A <see cref="ComponentFactoryCollection"/>.</returns>
21-
public static ComponentFactoryCollection Add<TComponent, TReplacementComponent>(this ComponentFactoryCollection factories)
20+
public static ComponentFactoryCollection Add<TComponent, TSubstituteComponent>(this ComponentFactoryCollection factories)
2221
where TComponent : IComponent
23-
where TReplacementComponent : IComponent
22+
where TSubstituteComponent : IComponent
2423
{
2524
if (factories is null)
2625
throw new ArgumentNullException(nameof(factories));
2726

28-
factories.Add(new GenericComponentFactory<TComponent, TReplacementComponent>());
27+
factories.Add(new GenericComponentFactory<TComponent, TSubstituteComponent>());
2928

3029
return factories;
3130
}
3231

3332
/// <summary>
34-
/// Configures bUnit to replace a <typeparamref name="TComponent"/> component in the render tree
35-
/// with the provided <paramref name="instance"/>.
33+
/// Configures bUnit to substitute a component of type <typeparamref name="TComponent"/> with the provided <paramref name="instance"/>.
3634
/// </summary>
3735
/// <remarks>
38-
/// Only one <typeparamref name="TComponent"/> component can be replaced with the replacement component (<paramref name="instance"/>).
36+
/// Only one <typeparamref name="TComponent"/> component can be substituted with the component (<paramref name="instance"/>).
3937
/// If there are two or more <typeparamref name="TComponent"/> components in the render tree, an exception is thrown.
4038
/// </remarks>
41-
/// <typeparam name="TComponent">Type of component to replace.</typeparam>
39+
/// <typeparam name="TComponent">Type of component to substitute.</typeparam>
4240
/// <param name="factories">The bUnit <see cref="ComponentFactoryCollection"/> to configure.</param>
4341
/// <param name="instance">The instance of the replacement component.</param>
4442
/// <exception cref="ArgumentNullException">Thrown when <paramref name="factories"/> and/or <paramref name="instance"/> is null.</exception>
@@ -55,5 +53,58 @@ public static ComponentFactoryCollection Add<TComponent>(this ComponentFactoryCo
5553

5654
return factories;
5755
}
56+
57+
/// <summary>
58+
/// Configures bUnit to substitute components of type <typeparamref name="TComponent"/>
59+
/// with one created by the provided component <paramref name="factory"/>.
60+
/// </summary>
61+
/// <remarks>
62+
/// The provided <paramref name="factory"/> must return unique instances each time it is called.
63+
/// Blazor does not allow the same component to exists in multiple places in a render tree.
64+
/// </remarks>
65+
/// <typeparam name="TComponent">Type of component to substitute.</typeparam>
66+
/// <param name="factories">The bUnit <see cref="ComponentFactoryCollection"/> to configure.</param>
67+
/// <param name="factory">The component factory to use to create substitute components with.</param>
68+
/// <returns>A <see cref="ComponentFactoryCollection"/>.</returns>
69+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="factories"/> and/or <paramref name="factory"/> is null.</exception>
70+
public static ComponentFactoryCollection Add<TComponent>(this ComponentFactoryCollection factories, Func<TComponent> factory)
71+
where TComponent : IComponent
72+
{
73+
if (factories is null)
74+
throw new ArgumentNullException(nameof(factories));
75+
if (factory is null)
76+
throw new ArgumentNullException(nameof(factory));
77+
78+
factories.Add(new TypeBasedComponentFactory<TComponent>(factory));
79+
80+
return factories;
81+
}
82+
83+
/// <summary>
84+
/// Configures bUnit to substitute components whose type matches the <paramref name="condition"/>,
85+
/// with components created by the provided component <paramref name="factory"/>.
86+
/// </summary>
87+
/// <remarks>
88+
/// The provided <paramref name="factory"/> must return unique instances each time it is called.
89+
/// Blazor does not allow the same component to exists in multiple places in a render tree.
90+
/// </remarks>
91+
/// <param name="factories">The bUnit <see cref="ComponentFactoryCollection"/> to configure.</param>
92+
/// <param name="condition">The condition that must be met for the <paramref name="factory"/> to be used.</param>
93+
/// <param name="factory">The factory to use to create substitute components with.</param>
94+
/// <returns>A <see cref="ComponentFactoryCollection"/>.</returns>
95+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="factories"/>, <paramref name="condition"/>, and/or <paramref name="factory"/> is null.</exception>
96+
public static ComponentFactoryCollection Add(this ComponentFactoryCollection factories, Predicate<Type> condition, Func<Type, IComponent> factory)
97+
{
98+
if (factories is null)
99+
throw new ArgumentNullException(nameof(factories));
100+
if (condition is null)
101+
throw new ArgumentNullException(nameof(condition));
102+
if (factory is null)
103+
throw new ArgumentNullException(nameof(factory));
104+
105+
factories.Add(new ConditionalComponentFactory(condition, factory));
106+
107+
return factories;
108+
}
58109
}
59110
#endif

0 commit comments

Comments
 (0)