55using System . Threading . Tasks ;
66using Microsoft . JSInterop ;
77
8- namespace Bunit . TestDoubles
8+ namespace Bunit
99{
1010 /// <summary>
11- /// Represents an invoke handler for a mock of a <see cref="IJSRuntime"/> .
11+ /// Represents an bUnit's implementation of Blazor's JSInterop .
1212 /// </summary>
13- public class MockJSRuntimeInvokeHandler
13+ public class BunitJSInterop
1414 {
1515 private readonly Dictionary < string , List < JSRuntimeInvocation > > _invocations = new Dictionary < string , List < JSRuntimeInvocation > > ( ) ;
16- private readonly Dictionary < string , List < object > > _plannedInvocations = new Dictionary < string , List < object > > ( ) ;
17- private readonly Dictionary < Type , object > _catchAllInvocations = new Dictionary < Type , object > ( ) ;
16+ private readonly Dictionary < string , List < object > > _handlers = new Dictionary < string , List < object > > ( ) ;
17+ private readonly Dictionary < Type , object > _catchAllHandlers = new Dictionary < Type , object > ( ) ;
1818
1919 /// <summary>
2020 /// Gets a dictionary of all <see cref="List{JSRuntimeInvocation}"/> this mock has observed.
2121 /// </summary>
2222 public IReadOnlyDictionary < string , List < JSRuntimeInvocation > > Invocations => _invocations ;
2323
2424 /// <summary>
25- /// Gets whether the mock is running in <see cref="JSRuntimeMockMode .Loose"/> or
26- /// <see cref="JSRuntimeMockMode .Strict"/>.
25+ /// Gets or sets whether the mock is running in <see cref="JSRuntimeMode .Loose"/> or
26+ /// <see cref="JSRuntimeMode .Strict"/>.
2727 /// </summary>
28- public JSRuntimeMockMode Mode { get ; }
28+ public JSRuntimeMode Mode { get ; set ; }
2929
3030 /// <summary>
31- /// Creates a <see cref="MockJSRuntimeInvokeHandler "/>.
31+ /// Gets the mocked <see cref="IJSRuntime "/> instance .
3232 /// </summary>
33- /// <param name="mode">The <see cref="JSRuntimeMockMode"/> the handler should use.</param>
34- public MockJSRuntimeInvokeHandler ( JSRuntimeMockMode mode = JSRuntimeMockMode . Loose )
35- {
36- Mode = mode ;
37- }
33+ /// <returns></returns>
34+ public IJSRuntime JSRuntime { get ; }
3835
3936 /// <summary>
40- /// Gets the mocked <see cref="IJSRuntime "/> instance .
37+ /// Creates a <see cref="BunitJSInterop "/>.
4138 /// </summary>
42- /// <returns></returns>
43- public IJSRuntime ToJSRuntime ( )
39+ public BunitJSInterop ( )
4440 {
45- return new MockJSRuntime ( this ) ;
41+ Mode = JSRuntimeMode . Strict ;
42+ JSRuntime = new BUnitJSRuntime ( this ) ;
4643 }
4744
4845 /// <summary>
49- /// Configure a catch all JSInterop invocation for a specific return type.
46+ /// Configure a catch all JSInterop invocation handler for a specific return type.
47+ /// This will match only on the <typeparamref name="TResult"/>, and any arguments passed to
48+ /// <see cref="IJSRuntime.InvokeAsync{TValue}(string, object[])"/>.
5049 /// </summary>
51- /// <typeparam name="TResult">The result type of the invocation</typeparam>
52- /// <returns>A <see cref="JSRuntimeCatchAllPlannedInvocation {TResult}"/>.</returns>
53- public JSRuntimeCatchAllPlannedInvocation < TResult > Setup < TResult > ( )
50+ /// <typeparam name="TResult">The result type of the invocation. </typeparam>
51+ /// <returns>A <see cref="JSRuntimeCatchAllInvocationHandler {TResult}"/>.</returns>
52+ public JSRuntimeCatchAllInvocationHandler < TResult > Setup < TResult > ( )
5453 {
55- var result = new JSRuntimeCatchAllPlannedInvocation < TResult > ( ) ;
54+ var result = new JSRuntimeCatchAllInvocationHandler < TResult > ( ) ;
5655
57- _catchAllInvocations [ typeof ( TResult ) ] = result ;
56+ _catchAllHandlers [ typeof ( TResult ) ] = result ;
5857
5958 return result ;
6059 }
6160
6261 /// <summary>
63- /// Configure a planned JSInterop invocation with the <paramref name="identifier"/> and arguments
62+ /// Configure a JSInterop invocation handler with the <paramref name="identifier"/> and arguments
6463 /// passing the <paramref name="argumentsMatcher"/> test.
6564 /// </summary>
66- /// <typeparam name="TResult">The result type of the invocation</typeparam>
67- /// <param name="identifier">The identifier to setup a response for</param>
65+ /// <typeparam name="TResult">The result type of the invocation. </typeparam>
66+ /// <param name="identifier">The identifier to setup a response for. </param>
6867 /// <param name="argumentsMatcher">A matcher that is passed arguments received in invocations to <paramref name="identifier"/>. If it returns true the invocation is matched.</param>
69- /// <returns>A <see cref="JSRuntimePlannedInvocation {TResult}"/>.</returns>
70- public JSRuntimePlannedInvocation < TResult > Setup < TResult > ( string identifier , Func < IReadOnlyList < object ? > , bool > argumentsMatcher )
68+ /// <returns>A <see cref="JSRuntimeInvocationHandler {TResult}"/>.</returns>
69+ public JSRuntimeInvocationHandler < TResult > Setup < TResult > ( string identifier , Func < IReadOnlyList < object ? > , bool > argumentsMatcher )
7170 {
72- var result = new JSRuntimePlannedInvocation < TResult > ( identifier , argumentsMatcher ) ;
71+ var result = new JSRuntimeInvocationHandler < TResult > ( identifier , argumentsMatcher ) ;
7372
74- AddPlannedInvocation ( result ) ;
73+ AddHandler ( result ) ;
7574
7675 return result ;
7776 }
7877
7978 /// <summary>
80- /// Configure a planned JSInterop invocation with the <paramref name="identifier"/> and <paramref name="arguments"/>.
79+ /// Configure a JSInterop invocation handler with the <paramref name="identifier"/> and <paramref name="arguments"/>.
8180 /// </summary>
8281 /// <typeparam name="TResult"></typeparam>
83- /// <param name="identifier">The identifier to setup a response for</param>
82+ /// <param name="identifier">The identifier to setup a response for. </param>
8483 /// <param name="arguments">The arguments that an invocation to <paramref name="identifier"/> should match.</param>
85- /// <returns>A <see cref="JSRuntimePlannedInvocation {TResult}"/>.</returns>
86- public JSRuntimePlannedInvocation < TResult > Setup < TResult > ( string identifier , params object [ ] arguments )
84+ /// <returns>A <see cref="JSRuntimeInvocationHandler {TResult}"/>.</returns>
85+ public JSRuntimeInvocationHandler < TResult > Setup < TResult > ( string identifier , params object [ ] arguments )
8786 {
8887 return Setup < TResult > ( identifier , args => args . SequenceEqual ( arguments ) ) ;
8988 }
9089
9190 /// <summary>
92- /// Configure a planned JSInterop invocation with the <paramref name="identifier"/> and arguments
91+ /// Configure a JSInterop invocation handler with the <paramref name="identifier"/> and arguments
9392 /// passing the <paramref name="argumentsMatcher"/> test, that should not receive any result.
9493 /// </summary>
95- /// <param name="identifier">The identifier to setup a response for</param>
94+ /// <param name="identifier">The identifier to setup a response for. </param>
9695 /// <param name="argumentsMatcher">A matcher that is passed arguments received in invocations to <paramref name="identifier"/>. If it returns true the invocation is matched.</param>
97- /// <returns>A <see cref="JSRuntimePlannedInvocation "/>.</returns>
98- public JSRuntimePlannedInvocation SetupVoid ( string identifier , Func < IReadOnlyList < object ? > , bool > argumentsMatcher )
96+ /// <returns>A <see cref="JSRuntimeInvocationHandler "/>.</returns>
97+ public JSRuntimeInvocationHandler SetupVoid ( string identifier , Func < IReadOnlyList < object ? > , bool > argumentsMatcher )
9998 {
100- var result = new JSRuntimePlannedInvocation ( identifier , argumentsMatcher ) ;
99+ var result = new JSRuntimeInvocationHandler ( identifier , argumentsMatcher ) ;
101100
102- AddPlannedInvocation ( result ) ;
101+ AddHandler ( result ) ;
103102
104103 return result ;
105104 }
106105
107106 /// <summary>
108- /// Configure a planned JSInterop invocation with the <paramref name="identifier"/>
107+ /// Configure a JSInterop invocation handler with the <paramref name="identifier"/>
109108 /// and <paramref name="arguments"/>, that should not receive any result.
110109 /// </summary>
111- /// <param name="identifier">The identifier to setup a response for</param>
110+ /// <param name="identifier">The identifier to setup a response for. </param>
112111 /// <param name="arguments">The arguments that an invocation to <paramref name="identifier"/> should match.</param>
113- /// <returns>A <see cref="JSRuntimePlannedInvocation "/>.</returns>
114- public JSRuntimePlannedInvocation SetupVoid ( string identifier , params object [ ] arguments )
112+ /// <returns>A <see cref="JSRuntimeInvocationHandler "/>.</returns>
113+ public JSRuntimeInvocationHandler SetupVoid ( string identifier , params object [ ] arguments )
115114 {
116115 return SetupVoid ( identifier , args => args . SequenceEqual ( arguments ) ) ;
117116 }
118117
119118 /// <summary>
120- /// Configure a catch all JSInterop invocation, that should not receive any result.
119+ /// Configure a catch all JSInterop invocation handler , that should not receive any result.
121120 /// </summary>
122- /// <returns>A <see cref="JSRuntimeCatchAllPlannedInvocation "/>.</returns>
123- public JSRuntimeCatchAllPlannedInvocation SetupVoid ( )
121+ /// <returns>A <see cref="JSRuntimeCatchAllInvocationHandler "/>.</returns>
122+ public JSRuntimeCatchAllInvocationHandler SetupVoid ( )
124123 {
125- var result = new JSRuntimeCatchAllPlannedInvocation ( ) ;
126-
127- _catchAllInvocations [ typeof ( object ) ] = result ;
128-
124+ var result = new JSRuntimeCatchAllInvocationHandler ( ) ;
125+ _catchAllHandlers [ typeof ( object ) ] = result ;
129126 return result ;
130127 }
131128
132- private void AddPlannedInvocation < TResult > ( JSRuntimePlannedInvocation < TResult > planned )
129+ private void AddHandler < TResult > ( JSRuntimeInvocationHandler < TResult > handler )
133130 {
134- if ( ! _plannedInvocations . ContainsKey ( planned . Identifier ) )
131+ if ( ! _handlers . ContainsKey ( handler . Identifier ) )
135132 {
136- _plannedInvocations . Add ( planned . Identifier , new List < object > ( ) ) ;
133+ _handlers . Add ( handler . Identifier , new List < object > ( ) ) ;
137134 }
138- _plannedInvocations [ planned . Identifier ] . Add ( planned ) ;
135+ _handlers [ handler . Identifier ] . Add ( handler ) ;
139136 }
140137
141- private void AddInvocation ( JSRuntimeInvocation invocation )
138+ private void RegisterInvocation ( JSRuntimeInvocation invocation )
142139 {
143140 if ( ! _invocations . ContainsKey ( invocation . Identifier ) )
144141 {
@@ -147,13 +144,13 @@ private void AddInvocation(JSRuntimeInvocation invocation)
147144 _invocations [ invocation . Identifier ] . Add ( invocation ) ;
148145 }
149146
150- private class MockJSRuntime : IJSRuntime
147+ private class BUnitJSRuntime : IJSRuntime
151148 {
152- private readonly MockJSRuntimeInvokeHandler _handlers ;
149+ private readonly BunitJSInterop _jsInterop ;
153150
154- public MockJSRuntime ( MockJSRuntimeInvokeHandler mockJSRuntimeInvokeHandler )
151+ public BUnitJSRuntime ( BunitJSInterop bunitJsInterop )
155152 {
156- _handlers = mockJSRuntimeInvokeHandler ;
153+ _jsInterop = bunitJsInterop ;
157154 }
158155
159156 public ValueTask < TValue > InvokeAsync < TValue > ( string identifier , object ? [ ] ? args )
@@ -162,7 +159,7 @@ public ValueTask<TValue> InvokeAsync<TValue>(string identifier, object?[]? args)
162159 public ValueTask < TValue > InvokeAsync < TValue > ( string identifier , CancellationToken cancellationToken , object ? [ ] ? args )
163160 {
164161 var invocation = new JSRuntimeInvocation ( identifier , cancellationToken , args ) ;
165- _handlers . AddInvocation ( invocation ) ;
162+ _jsInterop . RegisterInvocation ( invocation ) ;
166163
167164 return TryHandlePlannedInvocation < TValue > ( identifier , invocation )
168165 ?? new ValueTask < TValue > ( default ( TValue ) ! ) ;
@@ -171,9 +168,9 @@ public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToke
171168 private ValueTask < TValue > ? TryHandlePlannedInvocation < TValue > ( string identifier , JSRuntimeInvocation invocation )
172169 {
173170 ValueTask < TValue > ? result = default ;
174- if ( _handlers . _plannedInvocations . TryGetValue ( identifier , out var plannedInvocations ) )
171+ if ( _jsInterop . _handlers . TryGetValue ( identifier , out var plannedInvocations ) )
175172 {
176- var planned = plannedInvocations . OfType < JSRuntimePlannedInvocationBase < TValue > > ( )
173+ var planned = plannedInvocations . OfType < JSRuntimeInvocationHandlerBase < TValue > > ( )
177174 . SingleOrDefault ( x => x . Matches ( invocation ) ) ;
178175
179176 if ( planned is not null )
@@ -183,20 +180,20 @@ public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToke
183180 }
184181 }
185182
186- if ( _handlers . _catchAllInvocations . TryGetValue ( typeof ( TValue ) , out var catchAllInvocation ) )
183+ if ( _jsInterop . _catchAllHandlers . TryGetValue ( typeof ( TValue ) , out var catchAllInvocation ) )
187184 {
188- var planned = catchAllInvocation as JSRuntimePlannedInvocationBase < TValue > ;
185+ var planned = catchAllInvocation as JSRuntimeInvocationHandlerBase < TValue > ;
189186
190187 if ( planned is not null )
191188 {
192- var task = ( ( JSRuntimePlannedInvocationBase < TValue > ) catchAllInvocation ) . RegisterInvocation ( invocation ) ;
189+ var task = ( ( JSRuntimeInvocationHandlerBase < TValue > ) catchAllInvocation ) . RegisterInvocation ( invocation ) ;
193190 return new ValueTask < TValue > ( task ) ;
194191 }
195192 }
196193
197- if ( result is null && _handlers . Mode == JSRuntimeMockMode . Strict )
194+ if ( result is null && _jsInterop . Mode == JSRuntimeMode . Strict )
198195 {
199- throw new UnplannedJSInvocationException ( invocation ) ;
196+ throw new JSRuntimeUnhandledInvocationException ( invocation ) ;
200197 }
201198
202199 return result ;
0 commit comments