diff --git a/src/Assembly.cs b/src/Assembly.cs index 92a57b785..1dcfc7065 100644 --- a/src/Assembly.cs +++ b/src/Assembly.cs @@ -1 +1 @@ -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Egil.RazorComponents.Testing.Library.Tests")] \ No newline at end of file +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Egil.RazorComponents.Testing.Library.Tests")] diff --git a/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs new file mode 100644 index 000000000..ef56da290 --- /dev/null +++ b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs @@ -0,0 +1,41 @@ +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 IReadOnlyList Arguments { get; } + + /// + /// Creates a new instance of the + /// with the arguments used in the invocation. + /// + /// The identifer used in the invocation. + /// The args used in the invocation, if any + 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; + Arguments = arguments; + HelpLink = "https://github.com/egil/razor-components-testing-library/wiki/Mocking-JsRuntime"; + } + } +} 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 79f15a98d..b97df3901 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..cc41ad827 --- /dev/null +++ b/tests/TestServiceProviderTest.cs @@ -0,0 +1,33 @@ +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 placeholder IJSRuntime " + + "which throws exceptions")] + public void Test001() + { + var ex = Assert.Throws(() => RenderComponent()); + 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(); + } + } +}