Skip to content

Commit 76f886f

Browse files
committed
JSPlannedInvocation now can have its result set at any time, and requests will get the result when they arrive, if one is set. A new result can also be provided at any time, but it will only affect requests arriving after it has been set, not the previous. Closes #78
1 parent a2a3e6e commit 76f886f

3 files changed

Lines changed: 118 additions & 95 deletions

File tree

src/bunit.web.tests/Mocking/JSInterop/MockJsRuntimeInvokeHandlerTest.cs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Linq;
33
using System.Threading;
44
using System.Threading.Tasks;
@@ -55,16 +55,35 @@ public async Task Test003()
5555
exception.Invocation.Arguments.ShouldBe(args);
5656
}
5757

58-
[Fact(DisplayName = "Invocations receives before a planned invocation " +
59-
"has result set receives the same result")]
60-
public async Task Test005()
58+
[Fact(DisplayName = "All invocations received AFTER a planned invocation " +
59+
"has a result set, receives the same result")]
60+
public async Task Test005x()
6161
{
6262
var identifier = "func";
6363
var expectedResult = Guid.NewGuid();
6464
var sut = new MockJsRuntimeInvokeHandler(JsRuntimeMockMode.Strict);
65+
var jsRuntime = sut.ToJsRuntime();
6566
var plannedInvoke = sut.Setup<Guid>(identifier);
6667

68+
plannedInvoke.SetResult(expectedResult);
69+
70+
var i1 = jsRuntime.InvokeAsync<Guid>(identifier);
71+
var i2 = jsRuntime.InvokeAsync<Guid>(identifier);
72+
73+
(await i1).ShouldBe(expectedResult);
74+
(await i2).ShouldBe(expectedResult);
75+
}
76+
77+
[Fact(DisplayName = "All invocations received BEFORE a planned invocation " +
78+
"has a result set, receives the same result")]
79+
public async Task Test005()
80+
{
81+
var identifier = "func";
82+
var expectedResult = Guid.NewGuid();
83+
var sut = new MockJsRuntimeInvokeHandler(JsRuntimeMockMode.Strict);
6784
var jsRuntime = sut.ToJsRuntime();
85+
var plannedInvoke = sut.Setup<Guid>(identifier);
86+
6887
var i1 = jsRuntime.InvokeAsync<Guid>(identifier);
6988
var i2 = jsRuntime.InvokeAsync<Guid>(identifier);
7089

@@ -74,29 +93,27 @@ public async Task Test005()
7493
(await i2).ShouldBe(expectedResult);
7594
}
7695

77-
[Fact(DisplayName = "Invocations receives after a planned invocation " +
78-
"has result set does not receive the same result as " +
79-
"the invocations before the result was set the first time")]
80-
public async Task Test006()
96+
[Fact(DisplayName = "Invocations receive the latest result set in a planned invocation")]
97+
public async Task Test006x()
8198
{
8299
var identifier = "func";
83100
var sut = new MockJsRuntimeInvokeHandler(JsRuntimeMockMode.Strict);
84101
var plannedInvoke = sut.Setup<Guid>(identifier);
85102
var jsRuntime = sut.ToJsRuntime();
86103

87104
var expectedResult1 = Guid.NewGuid();
88-
var i1 = jsRuntime.InvokeAsync<Guid>(identifier);
89105
plannedInvoke.SetResult(expectedResult1);
106+
var i1 = jsRuntime.InvokeAsync<Guid>(identifier);
90107

91108
var expectedResult2 = Guid.NewGuid();
92-
var i2 = jsRuntime.InvokeAsync<Guid>(identifier);
93109
plannedInvoke.SetResult(expectedResult2);
110+
var i2 = jsRuntime.InvokeAsync<Guid>(identifier);
94111

95112
(await i1).ShouldBe(expectedResult1);
96113
(await i2).ShouldBe(expectedResult2);
97114
}
98115

99-
[Fact(DisplayName = "A planned invocation can be cancelled for any waiting received invocations.")]
116+
[Fact(DisplayName = "A planned invocation can be canceled for any waiting received invocations")]
100117
public void Test007()
101118
{
102119
var identifier = "func";
@@ -109,7 +126,7 @@ public void Test007()
109126
invocation.IsCanceled.ShouldBeTrue();
110127
}
111128

112-
[Fact(DisplayName = "A planned invocation can throw an exception for any waiting received invocations.")]
129+
[Fact(DisplayName = "A planned invocation can throw an exception for any waiting received invocations")]
113130
public async Task Test008()
114131
{
115132
var identifier = "func";
Lines changed: 1 addition & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Threading.Tasks;
43

54
namespace Bunit.Mocking.JSInterop
65
{
@@ -36,87 +35,6 @@ internal JsRuntimePlannedInvocation(string identifier, Func<IReadOnlyList<object
3635
/// Sets the <typeparamref name="TResult"/> result that invocations will receive.
3736
/// </summary>
3837
/// <param name="result"></param>
39-
public void SetResult(TResult result)
40-
{
41-
base.SetResultBase(result);
42-
}
43-
}
44-
45-
/// <summary>
46-
/// Represents a planned invocation of a JavaScript function with specific arguments.
47-
/// </summary>
48-
/// <typeparam name="TResult"></typeparam>
49-
public abstract class JsRuntimePlannedInvocationBase<TResult>
50-
{
51-
private readonly List<JsRuntimeInvocation> _invocations;
52-
53-
private Func<IReadOnlyList<object>, bool> InvocationMatcher { get; }
54-
55-
private TaskCompletionSource<TResult> _completionSource;
56-
57-
/// <summary>
58-
/// The expected identifier for the function to invoke.
59-
/// </summary>
60-
public string Identifier { get; }
61-
62-
/// <summary>
63-
/// Gets the invocations that this <see cref="JsRuntimePlannedInvocation{TResult}"/> has matched with.
64-
/// </summary>
65-
public IReadOnlyList<JsRuntimeInvocation> Invocations => _invocations.AsReadOnly();
66-
67-
/// <summary>
68-
/// Creates an instance of a <see cref="JsRuntimePlannedInvocationBase{TResult}"/>.
69-
/// </summary>
70-
protected JsRuntimePlannedInvocationBase(string identifier, Func<IReadOnlyList<object>, bool> matcher)
71-
{
72-
Identifier = identifier;
73-
_invocations = new List<JsRuntimeInvocation>();
74-
InvocationMatcher = matcher;
75-
_completionSource = new TaskCompletionSource<TResult>();
76-
}
77-
78-
/// <summary>
79-
/// Sets the <typeparamref name="TResult"/> result that invocations will receive.
80-
/// </summary>
81-
/// <param name="result"></param>
82-
protected void SetResultBase(TResult result)
83-
{
84-
_completionSource.SetResult(result);
85-
}
86-
87-
/// <summary>
88-
/// Sets the <typeparamref name="TException"/> exception that invocations will receive.
89-
/// </summary>
90-
/// <param name="exception"></param>
91-
public void SetException<TException>(TException exception)
92-
where TException : Exception
93-
{
94-
_completionSource.SetException(exception);
95-
}
96-
97-
/// <summary>
98-
/// Marks the <see cref="Task{TResult}"/> that invocations will receive as canceled.
99-
/// </summary>
100-
public void SetCanceled()
101-
{
102-
_completionSource.SetCanceled();
103-
}
104-
105-
internal Task<TResult> RegisterInvocation(JsRuntimeInvocation invocation)
106-
{
107-
// TODO: https://github.com/egil/bunit/issues/78
108-
if (_completionSource.Task.IsCompleted)
109-
_completionSource = new TaskCompletionSource<TResult>();
110-
111-
_invocations.Add(invocation);
112-
113-
return _completionSource.Task;
114-
}
115-
116-
internal bool Matches(JsRuntimeInvocation invocation)
117-
{
118-
return Identifier.Equals(invocation.Identifier, StringComparison.Ordinal)
119-
&& InvocationMatcher(invocation.Arguments);
120-
}
38+
public void SetResult(TResult result) => base.SetResultBase(result);
12139
}
12240
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
5+
namespace Bunit.Mocking.JSInterop
6+
{
7+
/// <summary>
8+
/// Represents a planned invocation of a JavaScript function with specific arguments.
9+
/// </summary>
10+
/// <typeparam name="TResult"></typeparam>
11+
public abstract class JsRuntimePlannedInvocationBase<TResult>
12+
{
13+
private readonly List<JsRuntimeInvocation> _invocations;
14+
15+
private Func<IReadOnlyList<object>, bool> InvocationMatcher { get; }
16+
17+
private TaskCompletionSource<TResult> _completionSource;
18+
19+
/// <summary>
20+
/// The expected identifier for the function to invoke.
21+
/// </summary>
22+
public string Identifier { get; }
23+
24+
/// <summary>
25+
/// Gets the invocations that this <see cref="JsRuntimePlannedInvocation{TResult}"/> has matched with.
26+
/// </summary>
27+
public IReadOnlyList<JsRuntimeInvocation> Invocations => _invocations.AsReadOnly();
28+
29+
/// <summary>
30+
/// Creates an instance of a <see cref="JsRuntimePlannedInvocationBase{TResult}"/>.
31+
/// </summary>
32+
protected JsRuntimePlannedInvocationBase(string identifier, Func<IReadOnlyList<object>, bool> matcher)
33+
{
34+
Identifier = identifier;
35+
_invocations = new List<JsRuntimeInvocation>();
36+
InvocationMatcher = matcher;
37+
_completionSource = new TaskCompletionSource<TResult>();
38+
}
39+
40+
/// <summary>
41+
/// Sets the <typeparamref name="TResult"/> result that invocations will receive.
42+
/// </summary>
43+
/// <param name="result"></param>
44+
protected void SetResultBase(TResult result)
45+
{
46+
if (_completionSource.Task.IsCompleted)
47+
_completionSource = new TaskCompletionSource<TResult>();
48+
49+
_completionSource.SetResult(result);
50+
}
51+
52+
/// <summary>
53+
/// Sets the <typeparamref name="TException"/> exception that invocations will receive.
54+
/// </summary>
55+
/// <param name="exception"></param>
56+
public void SetException<TException>(TException exception)
57+
where TException : Exception
58+
{
59+
if (_completionSource.Task.IsCompleted)
60+
_completionSource = new TaskCompletionSource<TResult>();
61+
62+
_completionSource.SetException(exception);
63+
}
64+
65+
/// <summary>
66+
/// Marks the <see cref="Task{TResult}"/> that invocations will receive as canceled.
67+
/// </summary>
68+
public void SetCanceled()
69+
{
70+
if (_completionSource.Task.IsCompleted)
71+
_completionSource = new TaskCompletionSource<TResult>();
72+
73+
_completionSource.SetCanceled();
74+
}
75+
76+
internal Task<TResult> RegisterInvocation(JsRuntimeInvocation invocation)
77+
{
78+
_invocations.Add(invocation);
79+
return _completionSource.Task;
80+
}
81+
82+
internal bool Matches(JsRuntimeInvocation invocation)
83+
{
84+
return Identifier.Equals(invocation.Identifier, StringComparison.Ordinal)
85+
&& InvocationMatcher(invocation.Arguments);
86+
}
87+
}
88+
}

0 commit comments

Comments
 (0)