Skip to content

Commit d60adcf

Browse files
committed
Deprecated CommponentTestFixture and clean up tests. Closes #108
1 parent 102fd21 commit d60adcf

20 files changed

Lines changed: 417 additions & 222 deletions

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ All notable changes to **bUnit** will be documented in this file. The project ad
44
## [Unreleased]
55
### Changed
66
- Better error description from `MarkupMatches` when two sets of markup are different.
7+
- Render notification and markup notification
78

89
### Fixed
910
- `cut.FindComponent<xxx>()` doesnt return the component in cut. It now searches and finds the first child component.
1011

1112
### Removed
1213
- The generic collection assertion methods `ShouldAllBe<T>(this IEnumerable<T> collection, params Action<T, int>[] elementInspectors)` and `ShouldAllBe<T>(this IEnumerable<T> collection, params Action<T>[] elementInspectors)` have been removed from the library.
14+
- WaitForRender
15+
- WaitFor methods on TestContext
1316

1417
## [1.0.0-beta-6] - 2020-03-01
1518
This release includes a **name change from Blazor Components Testing Library to bUnit**. It also brings along two extra helper methods for working with asynchronously rendering components during testing, and a bunch of internal optimizations and tweaks to the code.

src/.editorconfig

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,32 @@ root = true
22

33
[*]
44
indent_style = tab
5+
indent_size = tab
6+
tab_size = 4
57
charset = utf-8
68
trim_trailing_whitespace = true
79
insert_final_newline = true
810

911
[*.cs]
10-
indent_size = 4
12+
tab_size = 4
1113
dotnet_sort_system_directives_first = true
1214

1315
[*.{xml,config,*proj,nuspec,props,resx,targets,yml,tasks}]
14-
indent_size = 2
16+
tab_size = 2
1517

1618
[*.{htm,html,css,scss}]
17-
indent_size = 2
19+
tab_size = 2
1820

1921
[*.json]
20-
indent_size = 2
22+
tab_size = 2
2123

2224
[*.{ps1,psm1}]
23-
indent_size = 4
25+
tab_size = 4
2426

2527
[*.sh]
26-
indent_size = 4
28+
tab_size = 4
2729
end_of_line = lf
2830

2931
[*.{yml,yaml}]
3032
indent_style = space
31-
indent_size = 2
33+
tab_size = 2

src/bunit.core/ComponentParameterBuilder.cs

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace Bunit
1616
/// A builder to set a value for strongly typed ComponentParameters.
1717
/// </summary>
1818
/// <typeparam name="TComponent">The type of component under test to add the parameters</typeparam>
19-
public sealed class ComponentParameterBuilder<TComponent> where TComponent : class, IComponent
19+
public sealed class ComponentParameterBuilder<TComponent> where TComponent : IComponent
2020
{
2121
private const string ParameterNameChildContent = "ChildContent";
2222
private static readonly PropertyInfo[] ComponentProperties = typeof(TComponent).GetProperties();
@@ -210,42 +210,52 @@ public ComponentParameterBuilder<TComponent> Add<TValue>(Expression<Func<TCompon
210210
/// Add a strongly typed <see cref="RenderFragment" /> parameter with a <see cref="ComponentParameterBuilder{TChildComponent}"/> for the component under test.
211211
/// </summary>
212212
/// <param name="parameterSelector">The parameter selector which defines the parameter to add</param>
213-
/// <param name="childBuilderAction">The builder action for the child component.</param>
213+
/// <param name="childParameterBuilder">An optional builder action for the child component.</param>
214214
/// <returns>A <see cref="ComponentParameterBuilder{TComponent}"/> which can be chained</returns>
215-
public ComponentParameterBuilder<TComponent> Add<TChildComponent>(Expression<Func<TComponent, RenderFragment?>> parameterSelector, Action<ComponentParameterBuilder<TChildComponent>> childBuilderAction) where TChildComponent : class, IComponent
215+
public ComponentParameterBuilder<TComponent> Add<TChildComponent>(Expression<Func<TComponent, RenderFragment?>> parameterSelector, Action<ComponentParameterBuilder<TChildComponent>>? childParameterBuilder = null) where TChildComponent : class, IComponent
216216
{
217-
if (parameterSelector is null)
218-
throw new ArgumentNullException(nameof(parameterSelector));
219-
220-
if (childBuilderAction is null)
221-
throw new ArgumentNullException(nameof(childBuilderAction));
222-
223217
var (name, isCascading) = GetDetailsFromExpression(parameterSelector);
224218

225-
var childComponentParameterBuilder = new ComponentParameterBuilder<TChildComponent>();
226-
childBuilderAction(childComponentParameterBuilder);
219+
RenderFragment childContentFragment;
220+
221+
if (childParameterBuilder is { })
222+
{
223+
var build = new ComponentParameterBuilder<TChildComponent>();
224+
childParameterBuilder?.Invoke(build);
225+
childContentFragment = build.Build().ToComponentRenderFragment<TChildComponent>();
226+
}
227+
else
228+
{
229+
childContentFragment = Array.Empty<ComponentParameter>().ToComponentRenderFragment<TChildComponent>();
230+
}
227231

228-
var childFragment = childComponentParameterBuilder.Build().ToComponentRenderFragment<TChildComponent>();
229-
return AddParameterToList(name, childFragment, isCascading);
232+
return AddParameterToList(name, childContentFragment, isCascading);
230233
}
231234

232235
/// <summary>
233236
/// Add a <see cref="ComponentParameterBuilder{TChildComponent}"/> to build a ChildContent parameter.
234237
/// </summary>
235-
/// <param name="childBuilderAction">The builder action for the child component.</param>
238+
/// <param name="childParameterBuilder">An optional builder action for the child component.</param>
236239
/// <returns>A <see cref="ComponentParameterBuilder{TComponent}"/> which can be chained</returns>
237-
public ComponentParameterBuilder<TComponent> AddChildContent<TChildComponent>(Action<ComponentParameterBuilder<TChildComponent>> childBuilderAction) where TChildComponent : class, IComponent
240+
public ComponentParameterBuilder<TComponent> AddChildContent<TChildComponent>(Action<ComponentParameterBuilder<TChildComponent>>? childParameterBuilder = null) where TChildComponent : class, IComponent
238241
{
239-
if (childBuilderAction is null)
240-
throw new ArgumentNullException(nameof(childBuilderAction));
241-
242242
var (name, isCascading) = GetChildContentParameterDetails();
243243

244-
var childComponentParameterBuilder = new ComponentParameterBuilder<TChildComponent>();
245-
childBuilderAction(childComponentParameterBuilder);
246244

247-
var childFragment = childComponentParameterBuilder.Build().ToList().ToComponentRenderFragment<TChildComponent>();
248-
return AddParameterToList(name, childFragment, isCascading);
245+
RenderFragment childContentFragment;
246+
247+
if (childParameterBuilder is { })
248+
{
249+
var builder = new ComponentParameterBuilder<TChildComponent>();
250+
childParameterBuilder?.Invoke(builder);
251+
childContentFragment = builder.Build().ToComponentRenderFragment<TChildComponent>();
252+
}
253+
else
254+
{
255+
childContentFragment = Array.Empty<ComponentParameter>().ToComponentRenderFragment<TChildComponent>();
256+
}
257+
258+
return AddParameterToList(name, childContentFragment, isCascading);
249259
}
250260

251261
/// <summary>
@@ -266,7 +276,7 @@ public ComponentParameterBuilder<TComponent> AddChildContent(string markup)
266276
/// Create a <see cref="IReadOnlyList{ComponentParameter}"/>.
267277
/// </summary>
268278
/// <returns>A list of <see cref="ComponentParameter"/></returns>
269-
public IList<ComponentParameter> Build()
279+
public IReadOnlyList<ComponentParameter> Build()
270280
{
271281
return _componentParameters;
272282
}
@@ -312,13 +322,15 @@ private static bool TryGetDetailsFromPropertyInfo(PropertyInfo propertyInfo, out
312322
{
313323
if (!string.IsNullOrEmpty(cascadingParameterAttribute.Name))
314324
{
315-
// The CascadingParameterAttribute is defined and has a valid name, get the defined name from this attribute and indicate that it's a cascading property
325+
// The CascadingParameterAttribute is defined and has a valid name, get the defined
326+
// name from this attribute and indicate that it's a cascading property
316327
name = cascadingParameterAttribute.Name;
317328
isCascading = true;
318329
return true;
319330
}
320331

321-
// The CascadingParameterAttribute is defined, get the name from the property and indicate that it's a cascading property
332+
// The CascadingParameterAttribute is defined, get the name from the property
333+
// and indicate that it's a cascading property
322334
name = propertyInfo.Name;
323335
isCascading = true;
324336
return true;
@@ -332,14 +344,12 @@ private static bool TryGetDetailsFromPropertyInfo(PropertyInfo propertyInfo, out
332344

333345
private ComponentParameterBuilder<TComponent> AddParameterToList(string? name, object? value, bool isCascading)
334346
{
335-
if (_componentParameters.All(cp => cp.Name != name))
336-
{
337-
_componentParameters.Add((name, value, isCascading));
347+
if (_componentParameters.Any(cp => cp.Name == name))
348+
throw new ArgumentException($"A parameter with the name '{name}' has already been added to the {typeof(TComponent).Name}.");
338349

339-
return this;
340-
}
350+
_componentParameters.Add(new ComponentParameter(name, value, isCascading));
341351

342-
throw new ArgumentException($"A parameter with the name '{name}' has already been added to the {nameof(ComponentParameterBuilder<TComponent>)}.");
352+
return this;
343353
}
344354
}
345355
}
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
4+
using Bunit.Rendering;
5+
6+
using Microsoft.AspNetCore.Components;
7+
using Microsoft.AspNetCore.Components.Rendering;
8+
9+
namespace Bunit
10+
{
11+
/// <summary>
12+
/// <see cref="ComponentParameter"/> factory methods.
13+
/// </summary>
14+
public static class ComponentParameterFactory
15+
{
16+
/// <summary>
17+
/// Creates a <see cref="ComponentParameter"/> with an <see cref="Microsoft.AspNetCore.Components.EventCallback"/> that will call the provided <paramref name="callback"/>.
18+
/// </summary>
19+
/// <param name="name">Parameter name.</param>
20+
/// <param name="callback">The event callback.</param>
21+
/// <returns>The <see cref="ComponentParameter"/>.</returns>
22+
public static ComponentParameter EventCallback(string name, Action callback)
23+
{
24+
return ComponentParameter.CreateParameter(name, new EventCallback(null, callback));
25+
}
26+
27+
/// <summary>
28+
/// Creates a <see cref="ComponentParameter"/> with an <see cref="Microsoft.AspNetCore.Components.EventCallback"/> that will call the provided <paramref name="callback"/>.
29+
/// </summary>
30+
/// <param name="name">Parameter name.</param>
31+
/// <param name="callback">The event callback.</param>
32+
/// <returns>The <see cref="ComponentParameter"/>.</returns>
33+
public static ComponentParameter EventCallback(string name, Action<object> callback)
34+
{
35+
return ComponentParameter.CreateParameter(name, new EventCallback(null, callback));
36+
}
37+
38+
/// <summary>
39+
/// Creates a <see cref="ComponentParameter"/> with an <see cref="Microsoft.AspNetCore.Components.EventCallback"/> that will call the provided <paramref name="callback"/>.
40+
/// </summary>
41+
/// <param name="name">Parameter name.</param>
42+
/// <param name="callback">The event callback.</param>
43+
/// <returns>The <see cref="ComponentParameter"/>.</returns>
44+
public static ComponentParameter EventCallback(string name, Func<Task> callback)
45+
{
46+
return ComponentParameter.CreateParameter(name, new EventCallback(null, callback));
47+
}
48+
49+
/// <summary>
50+
/// Creates a <see cref="ComponentParameter"/> with an <see cref="Microsoft.AspNetCore.Components.EventCallback"/> that will call the provided <paramref name="callback"/>.
51+
/// </summary>
52+
/// <param name="name">Parameter name.</param>
53+
/// <param name="callback">The event callback.</param>
54+
/// <returns>The <see cref="ComponentParameter"/>.</returns>
55+
public static ComponentParameter EventCallback(string name, Func<object, Task> callback)
56+
{
57+
return ComponentParameter.CreateParameter(name, new EventCallback(null, callback));
58+
}
59+
60+
/// <summary>
61+
/// Creates a <see cref="ComponentParameter"/> with an <see cref="Microsoft.AspNetCore.Components.EventCallback"/> that will call the provided <paramref name="callback"/>.
62+
/// </summary>
63+
/// <param name="name">Parameter name.</param>
64+
/// <param name="callback">The event callback.</param>
65+
/// <returns>The <see cref="ComponentParameter"/>.</returns>
66+
public static ComponentParameter EventCallback<TValue>(string name, Action callback)
67+
{
68+
return ComponentParameter.CreateParameter(name, new EventCallback<TValue>(null, callback));
69+
}
70+
71+
/// <summary>
72+
/// Creates a <see cref="ComponentParameter"/> with an <see cref="Microsoft.AspNetCore.Components.EventCallback"/> that will call the provided <paramref name="callback"/>.
73+
/// </summary>
74+
/// <param name="name">Parameter name.</param>
75+
/// <param name="callback">The event callback.</param>
76+
/// <returns>The <see cref="ComponentParameter"/>.</returns>
77+
public static ComponentParameter EventCallback<TValue>(string name, Action<TValue> callback)
78+
{
79+
return ComponentParameter.CreateParameter(name, new EventCallback<TValue>(null, callback));
80+
}
81+
82+
/// <summary>
83+
/// Creates a <see cref="ComponentParameter"/> with an <see cref="Microsoft.AspNetCore.Components.EventCallback"/> that will call the provided <paramref name="callback"/>.
84+
/// </summary>
85+
/// <param name="name">Parameter name.</param>
86+
/// <param name="callback">The event callback.</param>
87+
/// <returns>The <see cref="ComponentParameter"/>.</returns>
88+
public static ComponentParameter EventCallback<TValue>(string name, Func<Task> callback)
89+
{
90+
return ComponentParameter.CreateParameter(name, new EventCallback<TValue>(null, callback));
91+
}
92+
93+
/// <summary>
94+
/// Creates a <see cref="ComponentParameter"/> with an <see cref="Microsoft.AspNetCore.Components.EventCallback"/> that will call the provided <paramref name="callback"/>.
95+
/// </summary>
96+
/// <param name="name">Parameter name.</param>
97+
/// <param name="callback">The event callback.</param>
98+
/// <returns>The <see cref="ComponentParameter"/>.</returns>
99+
public static ComponentParameter EventCallback<TValue>(string name, Func<TValue, Task> callback)
100+
{
101+
return ComponentParameter.CreateParameter(name, new EventCallback<TValue>(null, callback));
102+
}
103+
104+
/// <summary>
105+
/// Creates a component parameter which can be passed to a test contexts render methods.
106+
/// </summary>
107+
/// <param name="name">Parameter name</param>
108+
/// <param name="value">Value or null of the parameter</param>
109+
/// <returns>The <see cref="ComponentParameter"/>.</returns>
110+
public static ComponentParameter Parameter(string name, object? value)
111+
{
112+
return ComponentParameter.CreateParameter(name, value);
113+
}
114+
115+
/// <summary>
116+
/// Creates a cascading value which can be passed to a test contexts render methods.
117+
/// </summary>
118+
/// <param name="name">Parameter name</param>
119+
/// <param name="value">Value of the parameter</param>
120+
/// <returns>The <see cref="ComponentParameter"/>.</returns>
121+
public static ComponentParameter CascadingValue(string name, object value)
122+
{
123+
return ComponentParameter.CreateCascadingValue(name, value);
124+
}
125+
126+
/// <summary>
127+
/// Creates a cascading value which can be passed to a test contexts render methods.
128+
/// </summary>
129+
/// <param name="value">Value of the parameter</param>
130+
/// <returns>The <see cref="ComponentParameter"/>.</returns>
131+
public static ComponentParameter CascadingValue(object value)
132+
{
133+
return ComponentParameter.CreateCascadingValue(null, value);
134+
}
135+
136+
/// <summary>
137+
/// Creates a ChildContent <see cref="Microsoft.AspNetCore.Components.RenderFragment"/> with the provided
138+
/// <paramref name="markup"/> as rendered output.
139+
/// </summary>
140+
/// <param name="markup">Markup to pass to the child content parameter</param>
141+
/// <returns>The <see cref="ComponentParameter"/>.</returns>
142+
public static ComponentParameter ChildContent(string markup)
143+
{
144+
return RenderFragment(nameof(ChildContent), markup);
145+
}
146+
147+
/// <summary>
148+
/// Creates a ChildContent <see cref="Microsoft.AspNetCore.Components.RenderFragment"/> which will render a <typeparamref name="TComponent"/> component
149+
/// with the provided <paramref name="parameters"/> as input.
150+
/// </summary>
151+
/// <typeparam name="TComponent">The type of the component to render with the <see cref="Microsoft.AspNetCore.Components.RenderFragment"/></typeparam>
152+
/// <param name="parameters">Parameters to pass to the <typeparamref name="TComponent"/>.</param>
153+
/// <returns>The <see cref="ComponentParameter"/>.</returns>
154+
public static ComponentParameter ChildContent<TComponent>(params ComponentParameter[] parameters) where TComponent : class, IComponent
155+
{
156+
return RenderFragment<TComponent>(nameof(ChildContent), parameters);
157+
}
158+
159+
/// <summary>
160+
/// Creates a <see cref="Microsoft.AspNetCore.Components.RenderFragment"/> with the provided
161+
/// <paramref name="markup"/> as rendered output and passes it to the parameter specified in <paramref name="name"/>.
162+
/// </summary>
163+
/// <param name="name">Parameter name.</param>
164+
/// <param name="markup">Markup to pass to the render fragment parameter</param>
165+
/// <returns>The <see cref="ComponentParameter"/>.</returns>
166+
public static ComponentParameter RenderFragment(string name, string markup)
167+
{
168+
return ComponentParameter.CreateParameter(name, markup.ToMarkupRenderFragment());
169+
}
170+
171+
/// <summary>
172+
/// Creates a <see cref="Microsoft.AspNetCore.Components.RenderFragment"/> which will render a <typeparamref name="TComponent"/> component
173+
/// with the provided <paramref name="parameters"/> as input, and passes it to the parameter specified in <paramref name="name"/>.
174+
/// </summary>
175+
/// <typeparam name="TComponent">The type of the component to render with the <see cref="Microsoft.AspNetCore.Components.RenderFragment"/></typeparam>
176+
/// <param name="name">Parameter name.</param>
177+
/// <param name="parameters">Parameters to pass to the <typeparamref name="TComponent"/>.</param>
178+
/// <returns>The <see cref="ComponentParameter"/>.</returns>
179+
public static ComponentParameter RenderFragment<TComponent>(string name, params ComponentParameter[] parameters) where TComponent : class, IComponent
180+
{
181+
return ComponentParameter.CreateParameter(name, parameters.ToComponentRenderFragment<TComponent>());
182+
}
183+
184+
/// <summary>
185+
/// Creates a template component parameter which will pass the <paramref name="template"/> <see cref="Microsoft.AspNetCore.Components.RenderFragment{TValue}" />
186+
/// to the parameter with the name <paramref name="name"/>.
187+
/// </summary>
188+
/// <typeparam name="TValue">The value used to build the content.</typeparam>
189+
/// <param name="name">Parameter name.</param>
190+
/// <param name="template"><see cref="Microsoft.AspNetCore.Components.RenderFragment{TValue}" /> to pass to the parameter.</param>
191+
/// <returns>The <see cref="ComponentParameter"/>.</returns>
192+
public static ComponentParameter Template<TValue>(string name, RenderFragment<TValue> template)
193+
{
194+
return ComponentParameter.CreateParameter(name, template);
195+
}
196+
197+
/// <summary>
198+
/// Creates a template component parameter which will pass the a <see cref="Microsoft.AspNetCore.Components.RenderFragment{TValue}" />
199+
/// to the parameter with the name <paramref name="name"/>.
200+
/// The <paramref name="markupFactory"/> will be used to generate the markup inside the template.
201+
/// </summary>
202+
/// <typeparam name="TValue">The value used to build the content.</typeparam>
203+
/// <param name="name">Parameter name.</param>
204+
/// <param name="markupFactory">A markup factory that takes a <typeparamref name="TValue"/> as input and returns markup/HTML.</param>
205+
/// <returns>The <see cref="ComponentParameter"/>.</returns>
206+
public static ComponentParameter Template<TValue>(string name, Func<TValue, string> markupFactory)
207+
{
208+
return Template<TValue>(name, value => (RenderTreeBuilder builder) => builder.AddMarkupContent(0, markupFactory(value)));
209+
}
210+
}
211+
}

0 commit comments

Comments
 (0)