Skip to content

Commit e5706aa

Browse files
committed
Added "load all items" support for components using the <Virtualize> component
1 parent 13eb090 commit e5706aa

File tree

5 files changed

+597
-0
lines changed

5 files changed

+597
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ List of new features.
1313

1414
- Added support for triggering `@ontoggle` event handlers through a dedicated `Toggle()` method. By [@egil](https://github.com/egil) in [#256](https://github.com/egil/bUnit/pull/256).
1515

16+
- Added out of the box support for `<Virtualize>` component. When a `<Virtualize>` component is used in a component under test, it's JavaScript interop-calls are faked by bUnits JSInterop, and it should result in all items being rendered immediately.
17+
1618
- Added support for components that call `ElementReference.FocusAsync`. These calls are handled by the bUnits JSInterop, that also allows you to verify that `FocusAsync` has been called for a specific element. For example, if a component has rendered an `<input>` element, then the following code will verify that it has been focused using `FocusAsync`:
1719

1820
```csharp
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace Bunit.JSInterop.ComponentSupport
2+
{
3+
/// <summary>
4+
/// Helper methods for registering handlers on the <see cref="BunitJSInterop"/>.
5+
/// </summary>
6+
public static class BunitJSInteropExtensions
7+
{
8+
/// <summary>
9+
/// Adds the built-in JSRuntime invocation handlers to the <paramref name="jSInterop"/>.
10+
/// </summary>
11+
public static BunitJSInterop AddBuiltInJSRuntimeInvocationHandlers(this BunitJSInterop jSInterop)
12+
{
13+
#if NET5_0
14+
jSInterop.AddInvocationHandler(new Virtualize.VirtualizeJSRuntimeInvocationHandler());
15+
#endif
16+
return jSInterop;
17+
}
18+
19+
}
20+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#if NET5_0
2+
using System;
3+
using System.Diagnostics;
4+
using System.Reflection;
5+
using Microsoft.AspNetCore.Components.Web.Virtualization;
6+
using Microsoft.JSInterop;
7+
8+
namespace Bunit.JSInterop.ComponentSupport.Virtualize
9+
{
10+
/// <summary>
11+
/// Represents an JSInterop handler for the <see cref="Virtualize{TItem}"/> component.
12+
/// </summary>
13+
public class VirtualizeJSRuntimeInvocationHandler : JSRuntimeInvocationHandler
14+
{
15+
private const string JsFunctionsPrefix = "Blazor._internal.Virtualize.";
16+
private static readonly Type VirtualizeJsInteropType;
17+
private static readonly Type DotNetObjectReferenceVirtualizeJsInteropType;
18+
private static readonly PropertyInfo DotNetObjectReferenceValuePropertyInfo;
19+
private static readonly MethodInfo OnSpacerBeforeVisibleMethodInfo;
20+
21+
static VirtualizeJSRuntimeInvocationHandler()
22+
{
23+
// Get <Virtualize> types needed to emulate the <Virtualize>'s JavaScript
24+
VirtualizeJsInteropType = typeof(Virtualize<>)
25+
.Assembly
26+
.GetType("Microsoft.AspNetCore.Components.Web.Virtualization.VirtualizeJsInterop")
27+
?? throw new InvalidOperationException("Did not find the VirtualizeJsInterop in the expected namespace/assembly.");
28+
DotNetObjectReferenceVirtualizeJsInteropType = typeof(DotNetObjectReference<>).MakeGenericType(VirtualizeJsInteropType);
29+
DotNetObjectReferenceValuePropertyInfo = DotNetObjectReferenceVirtualizeJsInteropType
30+
.GetProperty("Value", BindingFlags.Public | BindingFlags.Instance)
31+
?? throw new InvalidOperationException("Did not find the Value property on the DotNetObjectReference<VirtualizeJsInterop> type.");
32+
OnSpacerBeforeVisibleMethodInfo = VirtualizeJsInteropType?.GetMethod("OnSpacerBeforeVisible")
33+
?? throw new InvalidOperationException("Did not find the OnSpacerBeforeVisible method on the VirtualizeJsInterop type.");
34+
}
35+
36+
internal VirtualizeJSRuntimeInvocationHandler()
37+
: base(CatchAllIdentifier, i => i.Identifier.StartsWith(JsFunctionsPrefix, StringComparison.Ordinal))
38+
{ }
39+
40+
/// <inheritdoc/>
41+
protected override void OnInvocation(JSRuntimeInvocation invocation)
42+
{
43+
if (invocation.Identifier.Equals(JsFunctionsPrefix + "dispose", StringComparison.Ordinal))
44+
return;
45+
46+
// Assert expectations about the internals of the <Virtualize> component
47+
Debug.Assert(invocation.Identifier.Equals(JsFunctionsPrefix + "init", StringComparison.Ordinal), "Received an unexpected invocation identifier from the <Virtualize> component.");
48+
Debug.Assert(invocation.Arguments.Count == 3, "Received an unexpected amount of arguments from the <Virtualize> component.");
49+
Debug.Assert(invocation.Arguments[0] is not null, "Received an unexpected null argument, expected an DotNetObjectReference<VirtualizeJsInterop> instance.");
50+
51+
var onSpacerBeforeVisible = GetOnSpacerBeforeVisibleCallback(invocation.Arguments[0]!);
52+
53+
onSpacerBeforeVisible(
54+
0f /* spacerSize */,
55+
0f /* spacerSeparation */,
56+
10_000_000f /* containerSize - very large number to ensure all items are loaded at once */
57+
);
58+
59+
SetVoidResult();
60+
}
61+
62+
private static Action<float, float, float> GetOnSpacerBeforeVisibleCallback(object dotNetObjectReference)
63+
{
64+
var virtualizeJsInterop = DotNetObjectReferenceValuePropertyInfo?.GetValue(dotNetObjectReference);
65+
66+
return (spacerSize, spacerSeparation, containerSize)
67+
=> OnSpacerBeforeVisibleMethodInfo.Invoke(virtualizeJsInterop, new object[] { spacerSize, spacerSeparation, containerSize });
68+
}
69+
}
70+
}
71+
#endif

0 commit comments

Comments
 (0)