File: Hosting\WebAssemblyHostBuilderTest.cs
Web Access
Project: src\src\Components\WebAssembly\WebAssembly\test\Microsoft.AspNetCore.Components.WebAssembly.Tests.csproj (Microsoft.AspNetCore.Components.WebAssembly.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.JSInterop;
using Moq;
 
namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting;
 
public class WebAssemblyHostBuilderTest
{
    [Fact]
    public void Build_AllowsConfiguringConfiguration()
    {
        // Arrange
        var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods());
 
        builder.Configuration.AddInMemoryCollection(new[]
        {
            new KeyValuePair<string, string>("key", "value"),
        });
 
        // Act
        var host = builder.Build();
 
        // Assert
        Assert.Equal("value", host.Configuration["key"]);
    }
 
    [Fact]
    public void Build_AllowsConfiguringServices()
    {
        // Arrange
        var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods());
 
        // This test also verifies that we create a scope.
        builder.Services.AddScoped<StringBuilder>();
 
        // Act
        var host = builder.Build();
 
        // Assert
        Assert.NotNull(host.Services.GetRequiredService<StringBuilder>());
    }
 
    [Fact]
    public void Build_AllowsConfiguringContainer()
    {
        // Arrange
        var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods());
 
        builder.Services.AddScoped<StringBuilder>();
        var factory = new MyFakeServiceProviderFactory();
        builder.ConfigureContainer(factory);
 
        // Act
        var host = builder.Build();
 
        // Assert
        Assert.True(factory.CreateServiceProviderCalled);
        Assert.NotNull(host.Services.GetRequiredService<StringBuilder>());
    }
 
    [Fact]
    public void Build_AllowsConfiguringContainer_WithDelegate()
    {
        // Arrange
        var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods());
 
        builder.Services.AddScoped<StringBuilder>();
 
        var factory = new MyFakeServiceProviderFactory();
        builder.ConfigureContainer(factory, builder =>
        {
            builder.ServiceCollection.AddScoped<List<string>>();
        });
 
        // Act
        var host = builder.Build();
 
        // Assert
        Assert.True(factory.CreateServiceProviderCalled);
        Assert.NotNull(host.Services.GetRequiredService<StringBuilder>());
        Assert.NotNull(host.Services.GetRequiredService<List<string>>());
    }
 
    [Fact]
    public void Build_InDevelopment_ConfiguresWithServiceProviderWithScopeValidation()
    {
        // Arrange
        var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(environment: "Development"));
 
        builder.Services.AddScoped<StringBuilder>();
        builder.Services.AddSingleton<TestServiceThatTakesStringBuilder>();
 
        // Act
        var host = builder.Build();
 
        // Assert
        Assert.NotNull(host.Services.GetRequiredService<StringBuilder>());
        Assert.Throws<InvalidOperationException>(() => host.Services.GetRequiredService<TestServiceThatTakesStringBuilder>());
    }
 
    [Fact]
    public void Build_InProduction_ConfiguresWithServiceProviderWithScopeValidation()
    {
        // Arrange
        var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods());
 
        builder.Services.AddScoped<StringBuilder>();
        builder.Services.AddSingleton<TestServiceThatTakesStringBuilder>();
 
        // Act
        var host = builder.Build();
 
        // Assert
        Assert.NotNull(host.Services.GetRequiredService<StringBuilder>());
        Assert.NotNull(host.Services.GetRequiredService<TestServiceThatTakesStringBuilder>());
    }
 
    [Fact]
    public void Builder_InDevelopment_SetsHostEnvironmentProperty()
    {
        // Arrange
        var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(environment: "Development"));
 
        // Assert
        Assert.NotNull(builder.HostEnvironment);
        Assert.True(WebAssemblyHostEnvironmentExtensions.IsDevelopment(builder.HostEnvironment));
    }
 
    [Fact]
    public void Builder_CreatesNavigationManager()
    {
        // Arrange
        var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods(environment: "Development"));
 
        // Act
        var host = builder.Build();
 
        // Assert
        var navigationManager = host.Services.GetRequiredService<NavigationManager>();
        Assert.NotNull(navigationManager);
        Assert.Equal("https://www.example.com/", navigationManager.BaseUri);
        Assert.Equal("https://www.example.com/awesome-part-that-will-be-truncated-in-tests/cool", navigationManager.Uri);
    }
 
    private class TestServiceThatTakesStringBuilder
    {
        public TestServiceThatTakesStringBuilder(StringBuilder builder) { }
    }
 
    private class MyFakeDIBuilderThing
    {
        public MyFakeDIBuilderThing(IServiceCollection serviceCollection)
        {
            ServiceCollection = serviceCollection;
        }
 
        public IServiceCollection ServiceCollection { get; }
    }
 
    private class MyFakeServiceProviderFactory : IServiceProviderFactory<MyFakeDIBuilderThing>
    {
        public bool CreateServiceProviderCalled { get; set; }
 
        public MyFakeDIBuilderThing CreateBuilder(IServiceCollection services)
        {
            return new MyFakeDIBuilderThing(services);
        }
 
        public IServiceProvider CreateServiceProvider(MyFakeDIBuilderThing containerBuilder)
        {
            // This is the best way to test the factory was actually used. The Host doesn't
            // expose the *root* service provider, only a scoped instance. So we can return
            // a different type here, but we have no way to inspect it.
            CreateServiceProviderCalled = true;
            return containerBuilder.ServiceCollection.BuildServiceProvider();
        }
    }
 
    [Fact]
    public void Build_AddsConfigurationToServices()
    {
        // Arrange
        var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods());
 
        builder.Configuration.AddInMemoryCollection(new[]
        {
                new KeyValuePair<string, string>("key", "value"),
            });
 
        // Act
        var host = builder.Build();
 
        // Assert
        var configuration = host.Services.GetRequiredService<IConfiguration>();
        Assert.Equal("value", configuration["key"]);
    }
 
    private static IReadOnlyList<Type> DefaultServiceTypes
    {
        get
        {
            return new Type[]
            {
                    typeof(IJSRuntime),
                    typeof(NavigationManager),
                    typeof(INavigationInterception),
                    typeof(ILoggerFactory),
                    typeof(ILogger<>),
                    typeof(IWebAssemblyHostEnvironment),
            };
        }
    }
 
    [Fact]
    public void Constructor_AddsDefaultServices()
    {
        // Arrange & Act
        var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods());
 
        foreach (var type in DefaultServiceTypes)
        {
            Assert.Single(builder.Services, d => d.ServiceType == type);
        }
    }
 
    [Fact]
    public void Builder_SupportsConfiguringLogging()
    {
        // Arrange
        var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods());
        var provider = new Mock<ILoggerProvider>();
 
        // Act
        builder.Logging.AddProvider(provider.Object);
        var host = builder.Build();
 
        // Assert
        var loggerProvider = host.Services.GetRequiredService<ILoggerProvider>();
        Assert.NotNull(loggerProvider);
        Assert.Equal<ILoggerProvider>(provider.Object, loggerProvider);
 
    }
}