Skip to content

Commit 16b149d

Browse files
Implemented IJSInProcessRuntime and IJSUnmarshalledRuntime interfaces in bUnits JSRuntime
* Implemented `IJSInProcessRuntime` and `IJSUnmarshalledRuntime` interfaces for `BUnitJSRuntime` * Added zero argument test for `IJSUnmarshalledRuntime` invocation * Added suppression for ValueTask message * Removed `DisallowNull` attribute at `InvokeUnmarshalled`'s arguments and parsed an empty array instead of null to `InvokeUnmarshalled` with no arguments. * Added two happy path tests for strict mode of Unmarshelled * Moved .NET 5 tests for BunitJSInterop to their own file and made that test class partial * Added more tests for IJSUnmarshalled * Removed unused CancelationTokens and simplified exception throwing tests. Co-authored-by: KristofferStrube <[email protected]>
1 parent b83bd3f commit 16b149d

File tree

4 files changed

+269
-2
lines changed

4 files changed

+269
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ The following section list all changes in 1.0.0 preview 01.
1111
### Added
1212
List of new features.
1313

14+
- Added support for casting `BUnitJSRuntime` to `IJSInProcessRuntime` and `IJSUnmarshalledRuntime`. By [@KristofferStrube](https://github.com/KristofferStrube) in [#279](https://github.com/egil/bUnit/pull/279)
15+
1416
- 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).
1517

1618
- 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. By [@egil](https://github.com/egil) in [#240](https://github.com/egil/bUnit/issues/240).

src/bunit.web/JSInterop/BunitJSInterop.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.ComponentModel;
4+
using System.Diagnostics.CodeAnalysis;
45
using System.Linq;
56
using System.Threading;
67
using System.Threading.Tasks;
@@ -179,7 +180,13 @@ private void RegisterInvocation(JSRuntimeInvocation invocation)
179180
return result;
180181
}
181182

182-
private class BUnitJSRuntime : IJSRuntime
183+
184+
[SuppressMessage("Design", "CA2012:ValueTask instances should not have their result directly accessed unless the instance has already completed.", Justification = "The ValueTask always wraps a Task object.")]
185+
#if NET5_0
186+
private class BUnitJSRuntime : IJSRuntime, IJSInProcessRuntime, IJSUnmarshalledRuntime
187+
#else
188+
private class BUnitJSRuntime : IJSRuntime, IJSInProcessRuntime
189+
#endif
183190
{
184191
private readonly BunitJSInterop _jsInterop;
185192

@@ -188,6 +195,9 @@ public BUnitJSRuntime(BunitJSInterop bunitJsInterop)
188195
_jsInterop = bunitJsInterop;
189196
}
190197

198+
public TResult Invoke<TResult>(string identifier, params object?[]? args) =>
199+
InvokeAsync<TResult>(identifier, args).GetAwaiter().GetResult();
200+
191201
public ValueTask<TValue> InvokeAsync<TValue>(string identifier, object?[]? args)
192202
=> InvokeAsync<TValue>(identifier, default, args);
193203

@@ -199,6 +209,18 @@ public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToke
199209
return TryHandlePlannedInvocation<TValue>(invocation) ?? new ValueTask<TValue>(default(TValue)!);
200210
}
201211

212+
public TResult InvokeUnmarshalled<TResult>(string identifier) =>
213+
InvokeAsync<TResult>(identifier, Array.Empty<object?>()).GetAwaiter().GetResult();
214+
215+
public TResult InvokeUnmarshalled<T0, TResult>(string identifier, T0 arg0) =>
216+
InvokeAsync<TResult>(identifier, new object?[] {arg0}).GetAwaiter().GetResult();
217+
218+
public TResult InvokeUnmarshalled<T0, T1, TResult>(string identifier, T0 arg0, T1 arg1) =>
219+
InvokeAsync<TResult>(identifier, new object?[] { arg0, arg1 }).GetAwaiter().GetResult();
220+
221+
public TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2) =>
222+
InvokeAsync<TResult>(identifier, new object?[] { arg0, arg1, arg2 }).GetAwaiter().GetResult();
223+
202224
private ValueTask<TValue>? TryHandlePlannedInvocation<TValue>(JSRuntimeInvocation invocation)
203225
{
204226
ValueTask<TValue>? result = default;

tests/bunit.web.tests/JSInterop/BunitJSInteropTest.cs

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
namespace Bunit.JSInterop
1212
{
13-
public class BunitJSInteropTest
13+
public partial class BunitJSInteropTest
1414
{
1515
private static BunitJSInterop CreateSut(JSRuntimeMode mode) => new BunitJSInterop { Mode = mode };
1616

@@ -435,5 +435,57 @@ public void Test042()
435435

436436
actual.ShouldBe(expected);
437437
}
438+
439+
[Fact(DisplayName = "Mock returns default value from IJSInProcessRuntime's invoke method in loose mode without invocation setup")]
440+
public void Test043()
441+
{
442+
var sut = CreateSut(JSRuntimeMode.Loose);
443+
444+
var result = ((IJSInProcessRuntime)sut.JSRuntime).Invoke<object>("ident", Array.Empty<object>());
445+
446+
result.ShouldBe(default);
447+
}
448+
449+
[Fact(DisplayName = "After IJSInProcessRuntime invocation a invocation should be visible from the Invocations list")]
450+
public void Test044()
451+
{
452+
var identifier = "fooFunc";
453+
var args = new[] { "bar", "baz" };
454+
var sut = CreateSut(JSRuntimeMode.Loose);
455+
456+
var _ = ((IJSInProcessRuntime)sut.JSRuntime).Invoke<object>(identifier, args);
457+
458+
var invocation = sut.Invocations[identifier].Single();
459+
invocation.Identifier.ShouldBe(identifier);
460+
invocation.Arguments.ShouldBe(args);
461+
}
462+
463+
[Fact(DisplayName = "IJSInProcessRuntime invocations receive the result set in a planned invocation")]
464+
public void Test045()
465+
{
466+
var identifier = "func";
467+
var args = new[] { "bar", "baz" };
468+
var sut = CreateSut(JSRuntimeMode.Strict);
469+
470+
var expectedResult = Guid.NewGuid();
471+
var planned = sut.Setup<Guid>(identifier, args);
472+
planned.SetResult(expectedResult);
473+
474+
var i = ((IJSInProcessRuntime)sut.JSRuntime).Invoke<Guid>(identifier, args);
475+
476+
i.ShouldBe(expectedResult);
477+
}
478+
479+
[Fact(DisplayName = "Mock throws exception when in strict mode and IJSInProcessRuntime invocation has not been setup")]
480+
public void Test046()
481+
{
482+
var sut = CreateSut(JSRuntimeMode.Strict);
483+
var identifier = "func";
484+
var args = new[] { "bar", "baz" };
485+
486+
var exception = Should.Throw<JSRuntimeUnhandledInvocationException>(() => ((IJSInProcessRuntime)sut.JSRuntime).Invoke<object>(identifier, args));
487+
exception.Invocation.Identifier.ShouldBe(identifier);
488+
exception.Invocation.Arguments.ShouldBe(args);
489+
}
438490
}
439491
}
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
#if NET5_0
2+
using System;
3+
using System.Diagnostics.CodeAnalysis;
4+
using System.Linq;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
using Bunit;
8+
using Microsoft.JSInterop;
9+
using Shouldly;
10+
using Xunit;
11+
12+
namespace Bunit.JSInterop
13+
{
14+
public partial class BunitJSInteropTest
15+
{
16+
[Fact(DisplayName = "Mock throws exception when in strict mode and IJSUnmarshalledRuntime invocation has not been setup with one argument")]
17+
public void Test047()
18+
{
19+
var sut = CreateSut(JSRuntimeMode.Strict);
20+
var identifier = "func";
21+
var args = new[] { "bar" };
22+
23+
var exception = Should.Throw<JSRuntimeUnhandledInvocationException>(() => ((IJSUnmarshalledRuntime)sut.JSRuntime).InvokeUnmarshalled<string, object>(identifier, "bar"));
24+
exception.Invocation.Identifier.ShouldBe(identifier);
25+
exception.Invocation.Arguments.ShouldBe(args);
26+
}
27+
28+
[Fact(DisplayName = "Mock throws exception when in strict mode and IJSUnmarshalledRuntime invocation has not been setup with two arguments")]
29+
public void Test048()
30+
{
31+
var sut = CreateSut(JSRuntimeMode.Strict);
32+
var identifier = "func";
33+
var args = new[] { "bar", "baz" };
34+
35+
var exception = Should.Throw<JSRuntimeUnhandledInvocationException>(() => ((IJSUnmarshalledRuntime)sut.JSRuntime).InvokeUnmarshalled<string, string, object>(identifier, "bar", "baz"));
36+
exception.Invocation.Identifier.ShouldBe(identifier);
37+
exception.Invocation.Arguments.ShouldBe(args);
38+
}
39+
40+
[Fact(DisplayName = "Mock throws exception when in strict mode and IJSUnmarshalledRuntime invocation has not been setup with three arguments")]
41+
public void Test049()
42+
{
43+
var sut = CreateSut(JSRuntimeMode.Strict);
44+
var identifier = "func";
45+
var args = new[] { "bar", "baz", "bau" };
46+
47+
var exception = Should.Throw<JSRuntimeUnhandledInvocationException>(() => ((IJSUnmarshalledRuntime)sut.JSRuntime).InvokeUnmarshalled<string, string, string, object>(identifier, "bar", "baz", "bau"));
48+
exception.Invocation.Identifier.ShouldBe(identifier);
49+
exception.Invocation.Arguments.ShouldBe(args);
50+
}
51+
52+
[Fact(DisplayName = "Mock throws exception when in strict mode and IJSUnmarshalledRuntime invocation has not been setup with zero arguments")]
53+
public void Test050()
54+
{
55+
var sut = CreateSut(JSRuntimeMode.Strict);
56+
var identifier = "func";
57+
58+
var exception = Should.Throw<JSRuntimeUnhandledInvocationException>(() => ((IJSUnmarshalledRuntime)sut.JSRuntime).InvokeUnmarshalled<object>(identifier));
59+
exception.Invocation.Identifier.ShouldBe(identifier);
60+
exception.Invocation.Arguments.ShouldBeEmpty();
61+
}
62+
63+
[Fact(DisplayName = "After IJSUnmarshalledRuntime invocation a invocation should be visible from the Invocations list when parsed one arguments.")]
64+
public void Test051()
65+
{
66+
var identifier = "fooFunc";
67+
var args = new[] { "bar"};
68+
var sut = CreateSut(JSRuntimeMode.Strict);
69+
70+
var planned = sut.Setup<Guid>("fooFunc", args);
71+
planned.SetResult(Guid.NewGuid());
72+
73+
var _ = ((IJSUnmarshalledRuntime)sut.JSRuntime).InvokeUnmarshalled<string, Guid>(identifier, "bar");
74+
75+
var invocation = sut.Invocations[identifier].Single();
76+
invocation.Identifier.ShouldBe(identifier);
77+
invocation.Arguments.ShouldBe(args);
78+
}
79+
80+
[Fact(DisplayName = "After IJSUnmarshalledRuntime invocation a invocation should be visible from the Invocations list when parsed two arguments.")]
81+
public void Test052()
82+
{
83+
var identifier = "fooFunc";
84+
var args = new[] { "bar", "baz" };
85+
var sut = CreateSut(JSRuntimeMode.Strict);
86+
87+
var planned = sut.Setup<Guid>("fooFunc", args);
88+
planned.SetResult(Guid.NewGuid());
89+
90+
var _ = ((IJSUnmarshalledRuntime)sut.JSRuntime).InvokeUnmarshalled<string, string, Guid>(identifier, "bar", "baz");
91+
92+
var invocation = sut.Invocations[identifier].Single();
93+
invocation.Identifier.ShouldBe(identifier);
94+
invocation.Arguments.ShouldBe(args);
95+
}
96+
97+
[Fact(DisplayName = "After IJSUnmarshalledRuntime invocation a invocation should be visible from the Invocations list when parsed three arguments.")]
98+
public void Test053()
99+
{
100+
var identifier = "fooFunc";
101+
var args = new[] { "bar", "baz", "boa" };
102+
var sut = CreateSut(JSRuntimeMode.Strict);
103+
104+
var planned = sut.Setup<Guid>("fooFunc", args);
105+
planned.SetResult(Guid.NewGuid());
106+
107+
var _ = ((IJSUnmarshalledRuntime)sut.JSRuntime).InvokeUnmarshalled<string, string, string, Guid>(identifier, "bar", "baz", "boa");
108+
109+
var invocation = sut.Invocations[identifier].Single();
110+
invocation.Identifier.ShouldBe(identifier);
111+
invocation.Arguments.ShouldBe(args);
112+
}
113+
114+
[Fact(DisplayName = "After IJSUnmarshalledRuntime invocation a invocation should be visible from the Invocations list when parsed zero arguments.")]
115+
public void Test054()
116+
{
117+
var identifier = "fooFunc";
118+
var args = new[] { "bar", "baz", "boa" };
119+
var sut = CreateSut(JSRuntimeMode.Strict);
120+
121+
var planned = sut.Setup<Guid>("fooFunc", args);
122+
planned.SetResult(Guid.NewGuid());
123+
124+
var _ = ((IJSUnmarshalledRuntime)sut.JSRuntime).InvokeUnmarshalled<string, string, string, Guid>(identifier, "bar", "baz", "boa");
125+
126+
var invocation = sut.Invocations[identifier].Single();
127+
invocation.Identifier.ShouldBe(identifier);
128+
invocation.Arguments.ShouldBe(args);
129+
}
130+
131+
[Fact(DisplayName = "An IJSUnmarshalledRuntime invocation should return the correct result when parsed one arguments.")]
132+
public void Test055()
133+
{
134+
var identifier = "fooFunc";
135+
var args = new[] { "bar" };
136+
var sut = CreateSut(JSRuntimeMode.Strict);
137+
138+
var expectedResult = Guid.NewGuid();
139+
var planned = sut.Setup<Guid>("fooFunc", args);
140+
planned.SetResult(expectedResult);
141+
142+
var actual = ((IJSUnmarshalledRuntime)sut.JSRuntime).InvokeUnmarshalled<string, Guid>(identifier, "bar");
143+
actual.ShouldBe(expectedResult);
144+
}
145+
146+
[Fact(DisplayName = "An IJSUnmarshalledRuntime invocation should return the correct result when parsed two arguments.")]
147+
public void Test056()
148+
{
149+
var identifier = "fooFunc";
150+
var args = new[] { "bar", "baz" };
151+
var sut = CreateSut(JSRuntimeMode.Strict);
152+
153+
var expectedResult = Guid.NewGuid();
154+
var planned = sut.Setup<Guid>("fooFunc", args);
155+
planned.SetResult(expectedResult);
156+
157+
var actual = ((IJSUnmarshalledRuntime)sut.JSRuntime).InvokeUnmarshalled<string, string, Guid>(identifier, "bar", "baz");
158+
actual.ShouldBe(expectedResult);
159+
}
160+
161+
[Fact(DisplayName = "An IJSUnmarshalledRuntime invocation should return the correct result when parsed three arguments.")]
162+
public void Test057()
163+
{
164+
var identifier = "fooFunc";
165+
var args = new[] { "bar", "baz", "bao" };
166+
var sut = CreateSut(JSRuntimeMode.Strict);
167+
168+
var expectedResult = Guid.NewGuid();
169+
var planned = sut.Setup<Guid>("fooFunc", args);
170+
planned.SetResult(expectedResult);
171+
172+
var actual = ((IJSUnmarshalledRuntime)sut.JSRuntime).InvokeUnmarshalled<string, string, string, Guid>(identifier, "bar", "baz", "bao");
173+
actual.ShouldBe(expectedResult);
174+
}
175+
176+
[Fact(DisplayName = "An IJSUnmarshalledRuntime invocation should return the correct result when parsed zero arguments.")]
177+
public void Test058()
178+
{
179+
var identifier = "fooFunc";
180+
var sut = CreateSut(JSRuntimeMode.Strict);
181+
182+
var expectedResult = Guid.NewGuid();
183+
var planned = sut.Setup<Guid>("fooFunc");
184+
planned.SetResult(expectedResult);
185+
186+
var actual = ((IJSUnmarshalledRuntime)sut.JSRuntime).InvokeUnmarshalled<Guid>(identifier);
187+
actual.ShouldBe(expectedResult);
188+
}
189+
}
190+
}
191+
#endif

0 commit comments

Comments
 (0)