From 403f025cf6f6e28e85c35905c4565ce8d9f42e99 Mon Sep 17 00:00:00 2001 From: Michael Conrad Date: Sat, 11 Jan 2020 14:12:24 -0500 Subject: [PATCH 01/10] Add default JsRuntime --- src/Mocking/JSInterop/DefaultJsRuntime.cs | 41 +++++++++++++++++++++++ src/TestServiceProvider.cs | 9 ++++- tests/TestServiceProviderTest.cs | 26 ++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 src/Mocking/JSInterop/DefaultJsRuntime.cs create mode 100644 tests/TestServiceProviderTest.cs diff --git a/src/Mocking/JSInterop/DefaultJsRuntime.cs b/src/Mocking/JSInterop/DefaultJsRuntime.cs new file mode 100644 index 000000000..4b3dfdaba --- /dev/null +++ b/src/Mocking/JSInterop/DefaultJsRuntime.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.JSInterop; + +[assembly: InternalsVisibleTo("Egil.RazorComponents.Testing.Library.Tests")] + +namespace Egil.RazorComponents.Testing.Mocking.JSInterop +{ + /// + /// This JsRuntime is used to provide users with helpful exceptions if they fail to provide a mock when required. + /// + internal class DefaultJsRuntime : IJSRuntime + { + internal const string MissingJsRuntimeMessage = "This test requires a mock JsRuntime to be supplied, because a javascript method was called. Guidance on mocking the JsRuntime is available in the wiki. See exception data for the specific method and arguments used."; + internal const string MissingJsRuntimeHelpLink = "https://github.com/egil/razor-components-testing-library/wiki/Mocking-JsRuntime"; + + public ValueTask InvokeAsync(string identifier, object[] args) + { + var ex = new Exception(MissingJsRuntimeMessage); + ex.HelpLink = MissingJsRuntimeHelpLink; + ex.Data.Add("identifier", identifier); + ex.Data.Add("args", args); + throw ex; + } + + public ValueTask InvokeAsync(string identifier, CancellationToken cancellationToken, object[] args) + { + var ex = new Exception(MissingJsRuntimeMessage); + ex.HelpLink = MissingJsRuntimeHelpLink; + ex.Data.Add("identifier", identifier); + ex.Data.Add("CancelationToken", cancellationToken); + ex.Data.Add("args", args); + throw ex; + } + } +} diff --git a/src/TestServiceProvider.cs b/src/TestServiceProvider.cs index 79f15a98d..16fd742d9 100644 --- a/src/TestServiceProvider.cs +++ b/src/TestServiceProvider.cs @@ -1,4 +1,6 @@ -using Microsoft.Extensions.DependencyInjection; +using Egil.RazorComponents.Testing.Mocking.JSInterop; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.JSInterop; using System; using System.Diagnostics.CodeAnalysis; @@ -13,6 +15,11 @@ public sealed class TestServiceProvider : IServiceProvider, IDisposable private readonly ServiceCollection _serviceCollection = new ServiceCollection(); private ServiceProvider? _serviceProvider; + public TestServiceProvider() + { + _serviceCollection.AddSingleton(); + } + /// /// Gets whether this has been initialized, and /// no longer will accept calls to the AddService's methods. diff --git a/tests/TestServiceProviderTest.cs b/tests/TestServiceProviderTest.cs new file mode 100644 index 000000000..55788b26e --- /dev/null +++ b/tests/TestServiceProviderTest.cs @@ -0,0 +1,26 @@ +using Egil.RazorComponents.Testing.EventDispatchExtensions; +using Egil.RazorComponents.Testing.Extensions; +using Egil.RazorComponents.Testing.Mocking.JSInterop; +using Egil.RazorComponents.Testing.SampleComponents; +using Egil.RazorComponents.Testing.SampleComponents.Data; +using Shouldly; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Xunit; + +namespace Egil.RazorComponents.Testing +{ + public class TestServiceProviderTest : ComponentTestFixture + { + [Fact(DisplayName = "The test service provider should register a default IJSRuntime " + + "which throws exceptions")] + public void Test001() + { + var ex = Assert.Throws(() => RenderComponent()); + Assert.Equal(DefaultJsRuntime.MissingJsRuntimeMessage, ex.InnerException?.Message); + Assert.Equal(DefaultJsRuntime.MissingJsRuntimeHelpLink, ex.InnerException?.HelpLink); + } + } +} From 653fb5a60d0f89abf409e1ce29d38aa96340b80c Mon Sep 17 00:00:00 2001 From: Michael J Conrad <32316111+Siphonophora@users.noreply.github.com> Date: Thu, 16 Jan 2020 13:31:51 -0500 Subject: [PATCH 02/10] Update src/Mocking/JSInterop/DefaultJsRuntime.cs Co-Authored-By: Egil Hansen --- src/Mocking/JSInterop/DefaultJsRuntime.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mocking/JSInterop/DefaultJsRuntime.cs b/src/Mocking/JSInterop/DefaultJsRuntime.cs index 4b3dfdaba..10285273f 100644 --- a/src/Mocking/JSInterop/DefaultJsRuntime.cs +++ b/src/Mocking/JSInterop/DefaultJsRuntime.cs @@ -16,7 +16,7 @@ namespace Egil.RazorComponents.Testing.Mocking.JSInterop /// internal class DefaultJsRuntime : IJSRuntime { - internal const string MissingJsRuntimeMessage = "This test requires a mock JsRuntime to be supplied, because a javascript method was called. Guidance on mocking the JsRuntime is available in the wiki. See exception data for the specific method and arguments used."; + internal const string MissingJsRuntimeMessage = "This test requires a IJsRuntime to be supplied, because the component under test invokes the IJsRuntime during the test. Guidance on mocking the IJsRuntime is available in the testing library's Wiki. See exception data for the specific method and arguments used."; internal const string MissingJsRuntimeHelpLink = "https://github.com/egil/razor-components-testing-library/wiki/Mocking-JsRuntime"; public ValueTask InvokeAsync(string identifier, object[] args) From 89ad90b53705851d4b7a5d6c57abc6ace7040d59 Mon Sep 17 00:00:00 2001 From: Michael Conrad Date: Mon, 20 Jan 2020 11:43:02 -0500 Subject: [PATCH 03/10] Response to review. Add custom exception and code cleanup --- src/Assembly.cs | 9 ++++ src/Mocking/JSInterop/DefaultJsRuntime.cs | 41 ---------------- .../MissingMockJsRuntimeException.cs | 47 +++++++++++++++++++ src/Mocking/JSInterop/PlaceholderJsRuntime.cs | 26 ++++++++++ src/TestServiceProvider.cs | 2 +- tests/TestServiceProviderTest.cs | 13 +++-- 6 files changed, 93 insertions(+), 45 deletions(-) create mode 100644 src/Assembly.cs delete mode 100644 src/Mocking/JSInterop/DefaultJsRuntime.cs create mode 100644 src/Mocking/JSInterop/MissingMockJsRuntimeException.cs create mode 100644 src/Mocking/JSInterop/PlaceholderJsRuntime.cs diff --git a/src/Assembly.cs b/src/Assembly.cs new file mode 100644 index 000000000..e5e11513a --- /dev/null +++ b/src/Assembly.cs @@ -0,0 +1,9 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +[assembly: InternalsVisibleTo("Egil.RazorComponents.Testing.Library.Tests")] +namespace Egil.RazorComponents.Testing { } diff --git a/src/Mocking/JSInterop/DefaultJsRuntime.cs b/src/Mocking/JSInterop/DefaultJsRuntime.cs deleted file mode 100644 index 10285273f..000000000 --- a/src/Mocking/JSInterop/DefaultJsRuntime.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.JSInterop; - -[assembly: InternalsVisibleTo("Egil.RazorComponents.Testing.Library.Tests")] - -namespace Egil.RazorComponents.Testing.Mocking.JSInterop -{ - /// - /// This JsRuntime is used to provide users with helpful exceptions if they fail to provide a mock when required. - /// - internal class DefaultJsRuntime : IJSRuntime - { - internal const string MissingJsRuntimeMessage = "This test requires a IJsRuntime to be supplied, because the component under test invokes the IJsRuntime during the test. Guidance on mocking the IJsRuntime is available in the testing library's Wiki. See exception data for the specific method and arguments used."; - internal const string MissingJsRuntimeHelpLink = "https://github.com/egil/razor-components-testing-library/wiki/Mocking-JsRuntime"; - - public ValueTask InvokeAsync(string identifier, object[] args) - { - var ex = new Exception(MissingJsRuntimeMessage); - ex.HelpLink = MissingJsRuntimeHelpLink; - ex.Data.Add("identifier", identifier); - ex.Data.Add("args", args); - throw ex; - } - - public ValueTask InvokeAsync(string identifier, CancellationToken cancellationToken, object[] args) - { - var ex = new Exception(MissingJsRuntimeMessage); - ex.HelpLink = MissingJsRuntimeHelpLink; - ex.Data.Add("identifier", identifier); - ex.Data.Add("CancelationToken", cancellationToken); - ex.Data.Add("args", args); - throw ex; - } - } -} diff --git a/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs new file mode 100644 index 000000000..8e7b2055b --- /dev/null +++ b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Diagnostics.CodeAnalysis; + +namespace Egil.RazorComponents.Testing +{ + /// + /// Exception use to indicate that a MockJsRuntime is required by a test + /// but was not provided. + /// + [SuppressMessage("Design", "CA1032:Implement standard exception constructors", Justification = "")] + public class MissingMockJsRuntimeException : Exception + { + /// + /// Identifer string used in the JSInvoke method. + /// + public string Identifier { get; } + + /// + /// Arguments passed to the JSInvoke method. + /// + public object[] Args { get; } + + /// + /// Creates a new instance of the + /// with the provided attached. + /// + /// The identifer used in the invocation. + /// The args used in the invocation, if any + public MissingMockJsRuntimeException(string identifier, object[] args) + : base($"This test requires a IJsRuntime to be supplied, because the component under test invokes the IJsRuntime during the test. The invoked method is '{identifier}' and the invocation args are stored in the Args property of this exception. Guidance on mocking the IJsRuntime is available in the testing library's Wiki.") + { + Identifier = identifier; + Args = args; + HelpLink = "https://github.com/egil/razor-components-testing-library/wiki/Mocking-JsRuntime"; + } + + + private static string PrintArguments(IReadOnlyList arguments) + { + return string.Join(", ", arguments.Select(x => x.ToString())); + } + } +} diff --git a/src/Mocking/JSInterop/PlaceholderJsRuntime.cs b/src/Mocking/JSInterop/PlaceholderJsRuntime.cs new file mode 100644 index 000000000..fc8095c32 --- /dev/null +++ b/src/Mocking/JSInterop/PlaceholderJsRuntime.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.JSInterop; + +namespace Egil.RazorComponents.Testing.Mocking.JSInterop +{ + /// + /// This JsRuntime is used to provide users with helpful exceptions if they fail to provide a mock when required. + /// + internal class PlaceholderJsRuntime : IJSRuntime + { + public ValueTask InvokeAsync(string identifier, object[] args) + { + throw new MissingMockJsRuntimeException(identifier, args); + } + + public ValueTask InvokeAsync(string identifier, CancellationToken cancellationToken, object[] args) + { + throw new MissingMockJsRuntimeException(identifier, args); + } + } +} diff --git a/src/TestServiceProvider.cs b/src/TestServiceProvider.cs index 16fd742d9..b97df3901 100644 --- a/src/TestServiceProvider.cs +++ b/src/TestServiceProvider.cs @@ -17,7 +17,7 @@ public sealed class TestServiceProvider : IServiceProvider, IDisposable public TestServiceProvider() { - _serviceCollection.AddSingleton(); + _serviceCollection.AddSingleton(); } /// diff --git a/tests/TestServiceProviderTest.cs b/tests/TestServiceProviderTest.cs index 55788b26e..cc41ad827 100644 --- a/tests/TestServiceProviderTest.cs +++ b/tests/TestServiceProviderTest.cs @@ -14,13 +14,20 @@ namespace Egil.RazorComponents.Testing { public class TestServiceProviderTest : ComponentTestFixture { - [Fact(DisplayName = "The test service provider should register a default IJSRuntime " + + [Fact(DisplayName = "The test service provider should register a placeholder IJSRuntime " + "which throws exceptions")] public void Test001() { var ex = Assert.Throws(() => RenderComponent()); - Assert.Equal(DefaultJsRuntime.MissingJsRuntimeMessage, ex.InnerException?.Message); - Assert.Equal(DefaultJsRuntime.MissingJsRuntimeHelpLink, ex.InnerException?.HelpLink); + Assert.True(ex?.InnerException is MissingMockJsRuntimeException); + } + + [Fact(DisplayName = "The placeholder IJSRuntime is overriden by a supplied mock and does not throw")] + public void Test002() + { + Services.AddMockJsRuntime(); + + RenderComponent(); } } } From ca2af8adf29bdb8751b6b9c86489b6ce45ef73ee Mon Sep 17 00:00:00 2001 From: Michael Conrad Date: Tue, 21 Jan 2020 21:47:53 -0500 Subject: [PATCH 04/10] Remove unneded method --- src/Mocking/JSInterop/MissingMockJsRuntimeException.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs index 8e7b2055b..f85b43eed 100644 --- a/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs +++ b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs @@ -37,11 +37,5 @@ public MissingMockJsRuntimeException(string identifier, object[] args) Args = args; HelpLink = "https://github.com/egil/razor-components-testing-library/wiki/Mocking-JsRuntime"; } - - - private static string PrintArguments(IReadOnlyList arguments) - { - return string.Join(", ", arguments.Select(x => x.ToString())); - } } } From f784b3579be6b28a49fec34797728878f58ea33f Mon Sep 17 00:00:00 2001 From: Michael J Conrad <32316111+Siphonophora@users.noreply.github.com> Date: Tue, 21 Jan 2020 21:51:44 -0500 Subject: [PATCH 05/10] Update src/Mocking/JSInterop/MissingMockJsRuntimeException.cs Co-Authored-By: Egil Hansen --- src/Mocking/JSInterop/MissingMockJsRuntimeException.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs index f85b43eed..64bf177e6 100644 --- a/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs +++ b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs @@ -22,7 +22,7 @@ public class MissingMockJsRuntimeException : Exception /// /// Arguments passed to the JSInvoke method. /// - public object[] Args { get; } + public IReadOnlyList Arguments { get; } /// /// Creates a new instance of the From 2b6f92aa535a0faa225c80151bee54994e6a66f3 Mon Sep 17 00:00:00 2001 From: Michael J Conrad <32316111+Siphonophora@users.noreply.github.com> Date: Tue, 21 Jan 2020 21:51:54 -0500 Subject: [PATCH 06/10] Update src/Mocking/JSInterop/MissingMockJsRuntimeException.cs Co-Authored-By: Egil Hansen --- src/Mocking/JSInterop/MissingMockJsRuntimeException.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs index 64bf177e6..21c03888f 100644 --- a/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs +++ b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs @@ -29,7 +29,7 @@ public class MissingMockJsRuntimeException : Exception /// with the provided attached. /// /// The identifer used in the invocation. - /// The args used in the invocation, if any + /// The args used in the invocation, if any public MissingMockJsRuntimeException(string identifier, object[] args) : base($"This test requires a IJsRuntime to be supplied, because the component under test invokes the IJsRuntime during the test. The invoked method is '{identifier}' and the invocation args are stored in the Args property of this exception. Guidance on mocking the IJsRuntime is available in the testing library's Wiki.") { From 0584252ff63945beeb789aa4f2124da402731aa9 Mon Sep 17 00:00:00 2001 From: Michael J Conrad <32316111+Siphonophora@users.noreply.github.com> Date: Tue, 21 Jan 2020 21:52:04 -0500 Subject: [PATCH 07/10] Update src/Mocking/JSInterop/MissingMockJsRuntimeException.cs Co-Authored-By: Egil Hansen --- src/Mocking/JSInterop/MissingMockJsRuntimeException.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs index 21c03888f..e6c718a79 100644 --- a/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs +++ b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs @@ -34,7 +34,7 @@ public MissingMockJsRuntimeException(string identifier, object[] args) : base($"This test requires a IJsRuntime to be supplied, because the component under test invokes the IJsRuntime during the test. The invoked method is '{identifier}' and the invocation args are stored in the Args property of this exception. Guidance on mocking the IJsRuntime is available in the testing library's Wiki.") { Identifier = identifier; - Args = args; + Arguments = arguments; HelpLink = "https://github.com/egil/razor-components-testing-library/wiki/Mocking-JsRuntime"; } } From 6f8d735cef00f187f673b1f7c4fb1b58c7caaf92 Mon Sep 17 00:00:00 2001 From: Michael J Conrad <32316111+Siphonophora@users.noreply.github.com> Date: Tue, 21 Jan 2020 21:52:39 -0500 Subject: [PATCH 08/10] Update src/Mocking/JSInterop/MissingMockJsRuntimeException.cs Co-Authored-By: Egil Hansen --- src/Mocking/JSInterop/MissingMockJsRuntimeException.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs index e6c718a79..765946c96 100644 --- a/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs +++ b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs @@ -31,7 +31,7 @@ public class MissingMockJsRuntimeException : Exception /// The identifer used in the invocation. /// The args used in the invocation, if any public MissingMockJsRuntimeException(string identifier, object[] args) - : base($"This test requires a IJsRuntime to be supplied, because the component under test invokes the IJsRuntime during the test. The invoked method is '{identifier}' and the invocation args are stored in the Args property of this exception. Guidance on mocking the IJsRuntime is available in the testing library's Wiki.") + : base($"This test requires a IJsRuntime to be supplied, because the component under test invokes the IJsRuntime during the test. The invoked method is '{identifier}' and the invocation arguments are stored in the {nameof(Arguments)} property of this exception. Guidance on mocking the IJsRuntime is available in the testing library's Wiki.") { Identifier = identifier; Arguments = arguments; From 6bd0a535dc34ca955f0a7ba336b2ddf237da8aa6 Mon Sep 17 00:00:00 2001 From: Michael J Conrad <32316111+Siphonophora@users.noreply.github.com> Date: Tue, 21 Jan 2020 21:52:50 -0500 Subject: [PATCH 09/10] Update src/Mocking/JSInterop/MissingMockJsRuntimeException.cs Co-Authored-By: Egil Hansen --- src/Mocking/JSInterop/MissingMockJsRuntimeException.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs index 765946c96..619337ff7 100644 --- a/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs +++ b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs @@ -30,7 +30,7 @@ public class MissingMockJsRuntimeException : Exception /// /// The identifer used in the invocation. /// The args used in the invocation, if any - public MissingMockJsRuntimeException(string identifier, object[] args) + public MissingMockJsRuntimeException(string identifier, object[] arguments) : base($"This test requires a IJsRuntime to be supplied, because the component under test invokes the IJsRuntime during the test. The invoked method is '{identifier}' and the invocation arguments are stored in the {nameof(Arguments)} property of this exception. Guidance on mocking the IJsRuntime is available in the testing library's Wiki.") { Identifier = identifier; From 4e7ea27b264ec4ffc737f2f4fdc727b8778c6adf Mon Sep 17 00:00:00 2001 From: Michael J Conrad <32316111+Siphonophora@users.noreply.github.com> Date: Tue, 21 Jan 2020 21:53:02 -0500 Subject: [PATCH 10/10] Update src/Mocking/JSInterop/MissingMockJsRuntimeException.cs Co-Authored-By: Egil Hansen --- src/Mocking/JSInterop/MissingMockJsRuntimeException.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs index 619337ff7..ef56da290 100644 --- a/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs +++ b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs @@ -26,7 +26,7 @@ public class MissingMockJsRuntimeException : Exception /// /// Creates a new instance of the - /// with the provided attached. + /// with the arguments used in the invocation. /// /// The identifer used in the invocation. /// The args used in the invocation, if any