11using System ;
2- using System . Collections . Generic ;
32using System . Linq ;
43using System . Runtime . Serialization ;
4+ using System . Text ;
55
66namespace Bunit
77{
@@ -24,24 +24,102 @@ public sealed class JSRuntimeUnhandledInvocationException : Exception
2424 /// </summary>
2525 /// <param name="invocation">The unplanned invocation.</param>
2626 public JSRuntimeUnhandledInvocationException ( JSRuntimeInvocation invocation )
27- : base ( $ "The invocation of ' { invocation . Identifier } ' { PrintArguments ( invocation . Arguments ) } was not expected." )
27+ : base ( CreateErrorMessage ( invocation ) )
2828 {
2929 Invocation = invocation ;
3030 }
3131
32- private static string PrintArguments ( IReadOnlyList < object ? > arguments )
32+ private JSRuntimeUnhandledInvocationException ( SerializationInfo serializationInfo , StreamingContext streamingContext )
33+ : base ( serializationInfo , streamingContext )
34+ {
35+ }
36+
37+ private static string CreateErrorMessage ( JSRuntimeInvocation invocation )
3338 {
34- if ( arguments . Count == 0 )
35- return "without arguments" ;
36- if ( arguments . Count == 1 )
37- return $ "with the argument [{ arguments [ 0 ] } ]";
39+ var sb = new StringBuilder ( ) ;
40+ sb . AppendLine ( "bUnit's JSInterop has not been configured to handle the call:" ) ;
41+ sb . AppendLine ( ) ;
42+
43+ if ( invocation . IsVoidResultInvocation )
44+ {
45+ sb . AppendLine ( $ " { invocation . InvocationMethodName } ({ GetArguments ( invocation ) } )") ;
46+ }
47+ else
48+ {
49+ sb . AppendLine ( $ " { invocation . InvocationMethodName } <{ GetGenericInvocationArguments ( invocation ) } >({ GetArguments ( invocation ) } )") ;
50+ }
51+
52+ sb . AppendLine ( ) ;
53+ sb . AppendLine ( "Configure bUnit's JSInterop to handle the call with following:" ) ;
54+ sb . AppendLine ( ) ;
55+
56+ if ( invocation . IsVoidResultInvocation )
57+ {
58+ sb . AppendLine ( $ " SetupVoid({ GetArguments ( invocation ) } )") ;
59+ }
60+ else
61+ {
62+ sb . AppendLine ( $ " Setup<{ GetReturnTypeName ( invocation . ResultType ) } >({ GetArguments ( invocation ) } )") ;
63+ }
64+
65+ if ( invocation . Arguments . Any ( ) )
66+ {
67+ sb . AppendLine ( "or the following, to match any arguments:" ) ;
68+ if ( invocation . IsVoidResultInvocation )
69+ sb . AppendLine ( $ " SetupVoid(\" { invocation . Identifier } \" , _ => true)") ;
70+ else
71+ sb . AppendLine ( $ " Setup<{ GetReturnTypeName ( invocation . ResultType ) } >(\" { invocation . Identifier } \" , _ => true)") ;
72+ }
3873
39- return $ "with arguments [{ string . Join ( ", " , arguments . OfType < object > ( ) . Select ( x => x . ToString ( ) ) ) } ]";
74+ sb . AppendLine ( ) ;
75+ sb . AppendLine ( "The setup methods are available on an instance of the BunitJSInterop or" ) ;
76+ sb . AppendLine ( "BunitJSModuleInterop type. The standard BunitJSInterop is available" ) ;
77+ sb . AppendLine ( "through the TestContext.JSInterop property, and a BunitJSModuleInterop" ) ;
78+ sb . AppendLine ( "instance is returned from calling SetupModule on a BunitJSInterop instance." ) ;
79+ return sb . ToString ( ) ;
4080 }
4181
42- private JSRuntimeUnhandledInvocationException ( SerializationInfo serializationInfo , StreamingContext streamingContext )
43- : base ( serializationInfo , streamingContext )
82+ private static string GetReturnTypeName ( Type resultType )
83+ => resultType switch
84+ {
85+ Type { FullName : "System.Boolean" } => "bool" ,
86+ Type { FullName : "System.Byte" } => "byte" ,
87+ Type { FullName : "System.Char" } => "char" ,
88+ Type { FullName : "System.Double" } => "double" ,
89+ Type { FullName : "System.Int16" } => "short" ,
90+ Type { FullName : "System.Int32" } => "int" ,
91+ Type { FullName : "System.Int64" } => "long" ,
92+ Type { FullName : "System.SByte" } => "sbyte" ,
93+ Type { FullName : "System.Single" } => "float" ,
94+ Type { FullName : "System.UInt16" } => "ushort" ,
95+ Type { FullName : "System.UInt32" } => "uint" ,
96+ Type { FullName : "System.UInt64" } => "ulong" ,
97+ Type x => x . Name
98+ } ;
99+
100+ private static string GetGenericInvocationArguments ( JSRuntimeInvocation invocation )
101+ {
102+ if ( invocation . InvocationMethodName . Equals ( "InvokeUnmarshalled" , StringComparison . Ordinal ) )
103+ {
104+ var genericArgs = invocation . Arguments
105+ . Select ( x => x is not null ? GetReturnTypeName ( x . GetType ( ) ) : "?" )
106+ . Append ( GetReturnTypeName ( invocation . ResultType ) ) ;
107+
108+ return string . Join ( ", " , genericArgs ) ;
109+ }
110+ else
111+ {
112+ return GetReturnTypeName ( invocation . ResultType ) ;
113+ }
114+ }
115+
116+ private static string GetArguments ( JSRuntimeInvocation invocation )
44117 {
118+ var args = invocation . Arguments
119+ . Select ( x => x is string s ? $ "\" { s } \" " : x ? . ToString ( ) ?? "null" )
120+ . Prepend ( $ "\" { invocation . Identifier } \" ") ;
121+
122+ return string . Join ( ", " , args ) ;
45123 }
46124 }
47125}
0 commit comments