diff --git a/CHANGELOG.md b/CHANGELOG.md index be560c88f..d122dc667 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,8 @@ List of added functionality in this release. List of fixes in this release. +- Fixed JSInterop error message when trying to import an unconfigured module (#398). + - Fixed issue where a registered fall-back service provider was not made available to resolve service dependencies of components under test. Thanks to [@dady8889](https://github.com/dady8889) for the reporting the issue. ## [1.1.5] - 2021-04-30 diff --git a/src/bunit.web/JSInterop/JSRuntimeUnhandledInvocationException.cs b/src/bunit.web/JSInterop/JSRuntimeUnhandledInvocationException.cs index 1c296cc01..652976baa 100644 --- a/src/bunit.web/JSInterop/JSRuntimeUnhandledInvocationException.cs +++ b/src/bunit.web/JSInterop/JSRuntimeUnhandledInvocationException.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Runtime.Serialization; using System.Text; +using Microsoft.JSInterop; namespace Bunit { @@ -53,22 +54,29 @@ private static string CreateErrorMessage(JSRuntimeInvocation invocation) sb.AppendLine("Configure bUnit's JSInterop to handle the call with following:"); sb.AppendLine(); - if (invocation.IsVoidResultInvocation) + if (IsImportModuleInvocation(invocation)) { - sb.AppendLine($" SetupVoid({GetArguments(invocation)})"); + sb.AppendLine($" SetupModule({GetArguments(invocation, includeIdentifier: false)})"); } else { - sb.AppendLine($" Setup<{GetReturnTypeName(invocation.ResultType)}>({GetArguments(invocation)})"); - } - - if (invocation.Arguments.Any()) - { - sb.AppendLine("or the following, to match any arguments:"); if (invocation.IsVoidResultInvocation) - sb.AppendLine($" SetupVoid(\"{invocation.Identifier}\", _ => true)"); + { + sb.AppendLine($" SetupVoid({GetArguments(invocation)})"); + } else - sb.AppendLine($" Setup<{GetReturnTypeName(invocation.ResultType)}>(\"{invocation.Identifier}\", _ => true)"); + { + sb.AppendLine($" Setup<{GetReturnTypeName(invocation.ResultType)}>({GetArguments(invocation)})"); + } + + if (invocation.Arguments.Any()) + { + sb.AppendLine("or the following, to match any arguments:"); + if (invocation.IsVoidResultInvocation) + sb.AppendLine($" SetupVoid(\"{invocation.Identifier}\", _ => true)"); + else + sb.AppendLine($" Setup<{GetReturnTypeName(invocation.ResultType)}>(\"{invocation.Identifier}\", _ => true)"); + } } sb.AppendLine(); @@ -113,11 +121,27 @@ private static string GetGenericInvocationArguments(JSRuntimeInvocation invocati } } - private static string GetArguments(JSRuntimeInvocation invocation) +#if NET5_0_OR_GREATER + private static bool IsImportModuleInvocation(JSRuntimeInvocation invocation) + { + const string DefaultImportIdentifier = "import"; + return string.Equals(invocation.Identifier, DefaultImportIdentifier, StringComparison.Ordinal) + && typeof(IJSObjectReference).IsAssignableFrom(invocation.ResultType); + } +#else + [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "S1172:Unused method parameters should be removed", Justification = "Method to allow compatibility with netstandard2.1")] + private static bool IsImportModuleInvocation(JSRuntimeInvocation _) + => false; +#endif + + private static string GetArguments(JSRuntimeInvocation invocation, bool includeIdentifier = true) { var args = invocation.Arguments - .Select(x => x is string s ? $"\"{s}\"" : x?.ToString() ?? "null") - .Prepend($"\"{invocation.Identifier}\""); + .Select(x => x is string s ? $"\"{s}\"" : x?.ToString() ?? "null"); + if (includeIdentifier) + { + args = args.Prepend($"\"{invocation.Identifier}\""); + } return string.Join(", ", args); } diff --git a/tests/bunit.web.tests/JSInterop/JSRuntimeUnhandledInvocationExceptionTest.cs b/tests/bunit.web.tests/JSInterop/JSRuntimeUnhandledInvocationExceptionTest.cs index e990f313d..4bbbedea8 100644 --- a/tests/bunit.web.tests/JSInterop/JSRuntimeUnhandledInvocationExceptionTest.cs +++ b/tests/bunit.web.tests/JSInterop/JSRuntimeUnhandledInvocationExceptionTest.cs @@ -1,13 +1,14 @@ using System; using System.Diagnostics.CodeAnalysis; using AutoFixture.Xunit2; +using Microsoft.JSInterop; using Xunit; namespace Bunit.JSInterop { [SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:Parameter should not span multiple lines", Justification = "Makes error message easier to read.")] [SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "Dummy types are visible for testing purposes.")] - public class JSRuntimeUnhandledInvocationExceptionTest + public partial class JSRuntimeUnhandledInvocationExceptionTest { private const string CodeIdent = " "; diff --git a/tests/bunit.web.tests/JSInterop/JSRuntimeUnhandledInvocationExceptionTest.net5.cs b/tests/bunit.web.tests/JSInterop/JSRuntimeUnhandledInvocationExceptionTest.net5.cs new file mode 100644 index 000000000..ee9ded9be --- /dev/null +++ b/tests/bunit.web.tests/JSInterop/JSRuntimeUnhandledInvocationExceptionTest.net5.cs @@ -0,0 +1,29 @@ +#if NET5_0_OR_GREATER +using System; +using System.Diagnostics.CodeAnalysis; +using AutoFixture.Xunit2; +using Microsoft.JSInterop; +using Xunit; + +namespace Bunit.JSInterop +{ + public partial class JSRuntimeUnhandledInvocationExceptionTest + { + [Theory(DisplayName = "Message prints correctly when trying to import an unconfigured module")] + [AutoData] + public void Test036(string moduleName) + { + var identifier = "import"; + var returnType = typeof(IJSObjectReference); + var invocationMethodName = "InvokeAsync"; + var exectedErrorMessage = CreateExpectedErrorMessage( + $"{CodeIdent}{invocationMethodName}<{returnType.Name}>(\"{identifier}\", \"{moduleName}\")", + $"{CodeIdent}SetupModule(\"{moduleName}\")"); + + var sut = new JSRuntimeUnhandledInvocationException(new JSRuntimeInvocation(identifier, new object?[] { moduleName }, returnType, invocationMethodName)); + + Assert.Equal(exectedErrorMessage, sut.Message); + } + } +} +#endif \ No newline at end of file