11using System ;
22using System . Collections . Generic ;
3+ using System . ComponentModel ;
34using System . Linq ;
45using System . Threading ;
56using System . Threading . Tasks ;
@@ -14,7 +15,6 @@ public class BunitJSInterop
1415 {
1516 private readonly Dictionary < string , List < JSRuntimeInvocation > > _invocations = new Dictionary < string , List < JSRuntimeInvocation > > ( ) ;
1617 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.
@@ -48,12 +48,12 @@ public BunitJSInterop()
4848 /// <see cref="IJSRuntime.InvokeAsync{TValue}(string, object[])"/>.
4949 /// </summary>
5050 /// <typeparam name="TResult">The result type of the invocation.</typeparam>
51- /// <returns>A <see cref="JSRuntimeCatchAllInvocationHandler {TResult}"/>.</returns>
52- public JSRuntimeCatchAllInvocationHandler < TResult > Setup < TResult > ( )
51+ /// <returns>A <see cref="JSRuntimeInvocationHandler {TResult}"/>.</returns>
52+ public JSRuntimeInvocationHandler < TResult > Setup < TResult > ( )
5353 {
54- var result = new JSRuntimeCatchAllInvocationHandler < TResult > ( ) ;
54+ var result = new JSRuntimeInvocationHandler < TResult > ( JSRuntimeInvocationHandler < object > . CatchAllIdentifier , _ => true ) ;
5555
56- _catchAllHandlers [ typeof ( TResult ) ] = result ;
56+ AddHandler ( result ) ;
5757
5858 return result ;
5959 }
@@ -118,14 +118,33 @@ public JSRuntimeInvocationHandler SetupVoid(string identifier, params object[] a
118118 /// <summary>
119119 /// Configure a catch all JSInterop invocation handler, that should not receive any result.
120120 /// </summary>
121- /// <returns>A <see cref="JSRuntimeCatchAllInvocationHandler "/>.</returns>
122- public JSRuntimeCatchAllInvocationHandler SetupVoid ( )
121+ /// <returns>A <see cref="JSRuntimeInvocationHandler "/>.</returns>
122+ public JSRuntimeInvocationHandler SetupVoid ( )
123123 {
124- var result = new JSRuntimeCatchAllInvocationHandler ( ) ;
125- _catchAllHandlers [ typeof ( object ) ] = result ;
124+ var result = new JSRuntimeInvocationHandler ( JSRuntimeInvocationHandler < object > . CatchAllIdentifier , _ => true ) ;
125+
126+ AddHandler ( result ) ;
127+
126128 return result ;
127129 }
128130
131+ /// <summary>
132+ /// Looks through the registered handlers and returns the latest registered that can handle
133+ /// the provided <paramref name="identifier"/> and <paramref name="args"/>, and that
134+ /// will return <typeparamref name="TResult"/>.
135+ /// </summary>
136+ /// <returns>Returns the <see cref="JSRuntimeInvocationHandler{TResult}"/> or null if no one is found.</returns>
137+ public JSRuntimeInvocationHandler < TResult > ? TryGetInvokeHandler < TResult > ( string identifier , object ? [ ] ? args = null )
138+ => TryGetHandlerFor < TResult > ( new JSRuntimeInvocation ( identifier , default , args ) ) ;
139+
140+ /// <summary>
141+ /// Looks through the registered handlers and returns the latest registered that can handle
142+ /// the provided <paramref name="identifier"/> and <paramref name="args"/>, and that returns a "void" result.
143+ /// </summary>
144+ /// <returns>Returns the <see cref="JSRuntimeInvocationHandler"/> or null if no one is found.</returns>
145+ public JSRuntimeInvocationHandler ? TryGetInvokeVoidHandler ( string identifier , object ? [ ] ? args = null )
146+ => TryGetHandlerFor < object > ( new JSRuntimeInvocation ( identifier , default , args ) , x => x . IsVoidResultHandler ) as JSRuntimeInvocationHandler ;
147+
129148 private void AddHandler < TResult > ( JSRuntimeInvocationHandler < TResult > handler )
130149 {
131150 if ( ! _handlers . ContainsKey ( handler . Identifier ) )
@@ -144,6 +163,23 @@ private void RegisterInvocation(JSRuntimeInvocation invocation)
144163 _invocations [ invocation . Identifier ] . Add ( invocation ) ;
145164 }
146165
166+ private JSRuntimeInvocationHandler < TResult > ? TryGetHandlerFor < TResult > ( JSRuntimeInvocation invocation , Predicate < JSRuntimeInvocationHandler < TResult > > ? handlerPredicate = null )
167+ {
168+ handlerPredicate ??= _ => true ;
169+ JSRuntimeInvocationHandler < TResult > ? result = default ;
170+ if ( _handlers . TryGetValue ( invocation . Identifier , out var plannedInvocations ) )
171+ {
172+ result = plannedInvocations . OfType < JSRuntimeInvocationHandler < TResult > > ( )
173+ . Where ( x => handlerPredicate ( x ) && x . Matches ( invocation ) ) . LastOrDefault ( ) ;
174+ }
175+
176+ if ( result is null && _handlers . TryGetValue ( JSRuntimeInvocationHandler < TResult > . CatchAllIdentifier , out var catchAllHandlers ) )
177+ {
178+ result = catchAllHandlers . OfType < JSRuntimeInvocationHandler < TResult > > ( ) . Where ( x => handlerPredicate ( x ) ) . LastOrDefault ( ) ;
179+ }
180+ return result ;
181+ }
182+
147183 private class BUnitJSRuntime : IJSRuntime
148184 {
149185 private readonly BunitJSInterop _jsInterop ;
@@ -161,37 +197,19 @@ public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToke
161197 var invocation = new JSRuntimeInvocation ( identifier , cancellationToken , args ) ;
162198 _jsInterop . RegisterInvocation ( invocation ) ;
163199
164- return TryHandlePlannedInvocation < TValue > ( identifier , invocation )
165- ?? new ValueTask < TValue > ( default ( TValue ) ! ) ;
200+ return TryHandlePlannedInvocation < TValue > ( invocation ) ?? new ValueTask < TValue > ( default ( TValue ) ! ) ;
166201 }
167202
168- private ValueTask < TValue > ? TryHandlePlannedInvocation < TValue > ( string identifier , JSRuntimeInvocation invocation )
203+ private ValueTask < TValue > ? TryHandlePlannedInvocation < TValue > ( JSRuntimeInvocation invocation )
169204 {
170205 ValueTask < TValue > ? result = default ;
171- if ( _jsInterop . _handlers . TryGetValue ( identifier , out var plannedInvocations ) )
172- {
173- var handler = plannedInvocations . OfType < JSRuntimeInvocationHandlerBase < TValue > > ( )
174- . Where ( x => x . Matches ( invocation ) ) . LastOrDefault ( ) ;
175-
176- if ( handler is not null )
177- {
178- var task = handler . RegisterInvocation ( invocation ) ;
179- return new ValueTask < TValue > ( task ) ;
180- }
181- }
182206
183- if ( _jsInterop . _catchAllHandlers . TryGetValue ( typeof ( TValue ) , out var catchAllInvocation ) )
207+ if ( _jsInterop . TryGetHandlerFor < TValue > ( invocation ) is JSRuntimeInvocationHandler < TValue > handler )
184208 {
185- var planned = catchAllInvocation as JSRuntimeInvocationHandlerBase < TValue > ;
186-
187- if ( planned is not null )
188- {
189- var task = ( ( JSRuntimeInvocationHandlerBase < TValue > ) catchAllInvocation ) . RegisterInvocation ( invocation ) ;
190- return new ValueTask < TValue > ( task ) ;
191- }
209+ var task = handler . RegisterInvocation ( invocation ) ;
210+ result = new ValueTask < TValue > ( task ) ;
192211 }
193-
194- if ( result is null && _jsInterop . Mode == JSRuntimeMode . Strict )
212+ else if ( _jsInterop . Mode == JSRuntimeMode . Strict )
195213 {
196214 throw new JSRuntimeUnhandledInvocationException ( invocation ) ;
197215 }
0 commit comments