Skip to content

Commit 0b7692c

Browse files
authored
Add IJSObjectReference support / JS Module support (#288)
* Code cleanup * First draft of JS Interop module support * Added additional tests, implemented #258 * updated changelog * Added support for casting BunitJSObjectReference to IJSInProcessObjectReference, IJSUnmarshalledObjectReference
1 parent 5c668cb commit 0b7692c

File tree

58 files changed

+1529
-587
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1529
-587
lines changed

.editorconfig

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,19 @@ dotnet_diagnostic.BL0002.severity = none
3838
dotnet_diagnostic.BL0003.severity = none
3939
dotnet_diagnostic.BL0004.severity = none
4040
dotnet_diagnostic.BL0005.severity = none
41-
dotnet_diagnostic.BL0006.severity = none
41+
dotnet_diagnostic.BL0006.severity = none
42+
43+
## Code analysis configuration
44+
45+
[*.cs]
46+
47+
dotnet_diagnostic.S125.severity = suggestion # S125: Sections of code should not be commented out
48+
dotnet_diagnostic.S927.severity = suggestion # S927: Parameter names should match base declaration and other partial definitions
49+
dotnet_diagnostic.S1075.severity = suggestion # S1075: URIs should not be hardcoded
50+
dotnet_diagnostic.S1186.severity = suggestion # S1186: Methods should not be empty
51+
dotnet_diagnostic.S1199.severity = suggestion # S1199: Nested code blocks should not be used
52+
dotnet_diagnostic.S3925.severity = suggestion # S3925: "ISerializable" should be implemented correctly
53+
54+
[tests/**.cs]
55+
56+
dotnet_diagnostic.S3459.severity = suggestion # S3459: Unassigned members should be removed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,21 @@ List of new features.
8383

8484
By [@egil](https://github.com/egil) in [#262](https://github.com/egil/bUnit/pull/262).
8585

86+
- Added support for `IJSRuntime.InvokeAsync<IJSObjectReference>(...)` calls from components. There is now a new setup helper methods for configuring how invocations towards JS modules should be handled. This is done with the various `SetupModule` methods available on the `BunitJSInterop` type available through the `TestContext.JSInterop` property. For example, to set up a module for handling calls to `foo.js`, do the following:
87+
88+
```c#
89+
using var ctx = new TestContext();
90+
var moduleJsInterop = ctx.JSInterop.SetupModule("foo.js");
91+
```
92+
93+
The returned `moduleJsInterop` is a `BunitJSInterop` type, which means all the normal `Setup<TResult>` and `SetupVoid` methods can be used to configure it to handle calls to the module from a component. For example, to configure a handler for a call to `hello` in the `foo.js` module, do the following:
94+
95+
```c#
96+
moduleJsInterop.SetupVoid("hello");
97+
```
98+
99+
By [@egil](https://github.com/egil) in [#288](https://github.com/egil/bUnit/pull/288).
100+
86101
### Changed
87102
List of changes in existing functionality.
88103

global.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"sdk": {
3+
"rollForward": "latestMajor",
4+
"allowPrerelease": false
5+
}
6+
}

src/bunit.core/ComponentParameterCollection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ void AddCascadingValue(RenderTreeBuilder builder)
9090

9191
builder.OpenComponent(0, cv.Type);
9292

93-
if (cv.Parameter.Name is string)
93+
if (cv.Parameter.Name is not null)
9494
builder.AddAttribute(1, nameof(CascadingValue<object>.Name), cv.Parameter.Name);
9595

9696
builder.AddAttribute(2, nameof(CascadingValue<object>.Value), cv.Parameter.Value);

src/bunit.core/RazorTesting/FixtureBase.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
using System;
2-
using System.Diagnostics.CodeAnalysis;
3-
using System.Runtime.Serialization;
42
using System.Threading.Tasks;
53
using Microsoft.AspNetCore.Components;
64

src/bunit.core/RazorTesting/FragmentContainer.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System.Threading.Tasks;
21
using Microsoft.AspNetCore.Components;
32
using Microsoft.AspNetCore.Components.Rendering;
43

src/bunit.template/bunit.template.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,8 @@
3535
<None Remove="template\obj\**" />
3636
</ItemGroup>
3737

38+
<ItemGroup>
39+
<None Include="..\..\.editorconfig" Link=".editorconfig" />
40+
</ItemGroup>
41+
3842
</Project>

src/bunit.web/JSInterop/InvocationHandlers/FocusAsyncAssertJSInteropExtensions.cs renamed to src/bunit.web/Asserting/FocusAsyncAssertJSInteropExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#if NET5_0
2-
using Bunit.JSInterop.InvocationHandlers;
2+
using Bunit.JSInterop.InvocationHandlers.Implementation;
33
using Microsoft.AspNetCore.Components;
44
using System.Collections.Generic;
55

src/bunit.web/JSInterop/JSInvokeCountExpectedException.cs renamed to src/bunit.web/Asserting/JSInvokeCountExpectedException.cs

File renamed without changes.

src/bunit.web/JSInterop/JSRuntimeAssertExtensions.cs renamed to src/bunit.web/Asserting/JSRuntimeAssertExtensions.cs

Lines changed: 73 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using System.Collections.Generic;
33
using AngleSharp.Dom;
44
using Bunit.Asserting;
5+
using Bunit.JSInterop;
6+
using Bunit.JSInterop.InvocationHandlers;
57
using Microsoft.AspNetCore.Components;
68

79
namespace Bunit
@@ -11,21 +13,44 @@ namespace Bunit
1113
/// </summary>
1214
public static class JSRuntimeAssertExtensions
1315
{
16+
/// <summary>
17+
/// Verifies that the <paramref name="identifier"/> was never invoked on the <paramref name="jsInterop"/>.
18+
/// </summary>
19+
/// <param name="jsInterop">The bUnit JSInterop to verify against.</param>
20+
/// <param name="identifier">Identifier of invocation that should not have happened.</param>
21+
/// <param name="userMessage">A custom user message to display if the assertion fails.</param>
22+
public static void VerifyNotInvoke(this BunitJSInterop jsInterop, string identifier, string? userMessage = null)
23+
=> VerifyNotInvoke(jsInterop?.Invocations ?? throw new ArgumentNullException(nameof(jsInterop)), identifier, userMessage);
24+
1425
/// <summary>
1526
/// Verifies that the <paramref name="identifier"/> was never invoked on the <paramref name="handler"/>.
1627
/// </summary>
1728
/// <param name="handler">Handler to verify against.</param>
1829
/// <param name="identifier">Identifier of invocation that should not have happened.</param>
1930
/// <param name="userMessage">A custom user message to display if the assertion fails.</param>
20-
public static void VerifyNotInvoke(this BunitJSInterop handler, string identifier, string? userMessage = null)
21-
{
22-
if (handler is null)
23-
throw new ArgumentNullException(nameof(handler));
24-
if (handler.Invocations.TryGetValue(identifier, out var invocations) && invocations.Count > 0)
25-
{
26-
throw new JSInvokeCountExpectedException(identifier, 0, invocations.Count, nameof(VerifyNotInvoke), userMessage);
27-
}
28-
}
31+
public static void VerifyNotInvoke<TResult>(this JSRuntimeInvocationHandlerBase<TResult> handler, string identifier, string? userMessage = null)
32+
=> VerifyNotInvoke(handler?.Invocations ?? throw new ArgumentNullException(nameof(handler)), identifier, userMessage);
33+
34+
/// <summary>
35+
/// Verifies that the <paramref name="identifier"/> has been invoked one time.
36+
/// </summary>
37+
/// <param name="jsInterop">The bUnit JSInterop to verify against.</param>
38+
/// <param name="identifier">Identifier of invocation that should have been invoked.</param>
39+
/// <param name="userMessage">A custom user message to display if the assertion fails.</param>
40+
/// <returns>The <see cref="JSRuntimeInvocation"/>.</returns>
41+
public static JSRuntimeInvocation VerifyInvoke(this BunitJSInterop jsInterop, string identifier, string? userMessage = null)
42+
=> jsInterop.VerifyInvoke(identifier, 1, userMessage)[0];
43+
44+
/// <summary>
45+
/// Verifies that the <paramref name="identifier"/> has been invoked <paramref name="calledTimes"/> times.
46+
/// </summary>
47+
/// <param name="jsInterop">The bUnit JSInterop to verify against.</param>
48+
/// <param name="identifier">Identifier of invocation that should have been invoked.</param>
49+
/// <param name="calledTimes">The number of times the invocation is expected to have been called.</param>
50+
/// <param name="userMessage">A custom user message to display if the assertion fails.</param>
51+
/// <returns>The <see cref="JSRuntimeInvocation"/>.</returns>
52+
public static IReadOnlyList<JSRuntimeInvocation> VerifyInvoke(this BunitJSInterop jsInterop, string identifier, int calledTimes, string? userMessage = null)
53+
=> VerifyInvoke(jsInterop?.Invocations ?? throw new ArgumentNullException(nameof(jsInterop)), identifier, calledTimes, userMessage);
2954

3055
/// <summary>
3156
/// Verifies that the <paramref name="identifier"/> has been invoked one time.
@@ -34,7 +59,7 @@ public static void VerifyNotInvoke(this BunitJSInterop handler, string identifie
3459
/// <param name="identifier">Identifier of invocation that should have been invoked.</param>
3560
/// <param name="userMessage">A custom user message to display if the assertion fails.</param>
3661
/// <returns>The <see cref="JSRuntimeInvocation"/>.</returns>
37-
public static JSRuntimeInvocation VerifyInvoke(this BunitJSInterop handler, string identifier, string? userMessage = null)
62+
public static JSRuntimeInvocation VerifyInvoke<TResult>(this JSRuntimeInvocationHandlerBase<TResult> handler, string identifier, string? userMessage = null)
3863
=> handler.VerifyInvoke(identifier, 1, userMessage)[0];
3964

4065
/// <summary>
@@ -45,26 +70,8 @@ public static JSRuntimeInvocation VerifyInvoke(this BunitJSInterop handler, stri
4570
/// <param name="calledTimes">The number of times the invocation is expected to have been called.</param>
4671
/// <param name="userMessage">A custom user message to display if the assertion fails.</param>
4772
/// <returns>The <see cref="JSRuntimeInvocation"/>.</returns>
48-
public static IReadOnlyList<JSRuntimeInvocation> VerifyInvoke(this BunitJSInterop handler, string identifier, int calledTimes, string? userMessage = null)
49-
{
50-
if (handler is null)
51-
throw new ArgumentNullException(nameof(handler));
52-
53-
if (calledTimes < 1)
54-
throw new ArgumentException($"Use {nameof(VerifyNotInvoke)} to verify an identifier has not been invoked.", nameof(calledTimes));
55-
56-
if (!handler.Invocations.TryGetValue(identifier, out var invocations))
57-
{
58-
throw new JSInvokeCountExpectedException(identifier, calledTimes, 0, nameof(VerifyInvoke), userMessage);
59-
}
60-
61-
if (invocations.Count != calledTimes)
62-
{
63-
throw new JSInvokeCountExpectedException(identifier, calledTimes, invocations.Count, nameof(VerifyInvoke), userMessage);
64-
}
65-
66-
return invocations;
67-
}
73+
public static IReadOnlyList<JSRuntimeInvocation> VerifyInvoke<TResult>(this JSRuntimeInvocationHandlerBase<TResult> handler, string identifier, int calledTimes, string? userMessage = null)
74+
=> VerifyInvoke(handler?.Invocations ?? throw new ArgumentNullException(nameof(handler)), identifier, calledTimes, userMessage);
6875

6976
/// <summary>
7077
/// Verifies that an argument <paramref name="actualArgument"/>
@@ -93,5 +100,41 @@ public static void ShouldBeElementReferenceTo(this object? actualArgument, IElem
93100
"Element does not have a the expected element reference.");
94101
}
95102
}
103+
104+
private static IReadOnlyList<JSRuntimeInvocation> VerifyInvoke(JSRuntimeInvocationDictionary allInvocations, string identifier, int calledTimes, string? userMessage = null)
105+
{
106+
if (string.IsNullOrWhiteSpace(identifier))
107+
throw new ArgumentException($"'{nameof(identifier)}' cannot be null or whitespace.", nameof(identifier));
108+
109+
if (calledTimes < 1)
110+
throw new ArgumentException($"Use {nameof(VerifyNotInvoke)} to verify an identifier has not been invoked.", nameof(calledTimes));
111+
112+
var invocations = allInvocations[identifier];
113+
114+
if (invocations.Count == 0)
115+
{
116+
throw new JSInvokeCountExpectedException(identifier, calledTimes, 0, nameof(VerifyInvoke), userMessage);
117+
}
118+
119+
if (invocations.Count != calledTimes)
120+
{
121+
throw new JSInvokeCountExpectedException(identifier, calledTimes, allInvocations.Count, nameof(VerifyInvoke), userMessage);
122+
}
123+
124+
return invocations;
125+
}
126+
127+
private static void VerifyNotInvoke(JSRuntimeInvocationDictionary allInvocations, string identifier, string? userMessage = null)
128+
{
129+
if (string.IsNullOrWhiteSpace(identifier))
130+
throw new ArgumentException($"'{nameof(identifier)}' cannot be null or whitespace.", nameof(identifier));
131+
132+
var invocationCount = allInvocations[identifier].Count;
133+
134+
if (invocationCount > 0)
135+
{
136+
throw new JSInvokeCountExpectedException(identifier, 0, invocationCount, nameof(VerifyNotInvoke), userMessage);
137+
}
138+
}
96139
}
97140
}

0 commit comments

Comments
 (0)