/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Components/Endpoints/test/RazorComponentEndpointDataSourceTest.cs
353 строки
14 KB
Ondřej Roztočil
Fix hot reload for Blazor route changes (#63972)
09 окт 2025, 21:25
Не верифицирован
09 окт 2025, 21:25
982cae2
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Reflection; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Components.Discovery; using Microsoft.AspNetCore.Components.Endpoints.Infrastructure; using Microsoft.AspNetCore.Components.Endpoints.Tests; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Testing; namespace Microsoft.AspNetCore.Components.Endpoints; public class RazorComponentEndpointDataSourceTest { [Fact] public void RegistersEndpoints() { var endpointDataSource = CreateDataSource<App>(); endpointDataSource.ComponentApplicationBuilderActions.Add(builder => { var assembly = typeof(App).Assembly; IRazorComponentApplication.GetBuilderForAssembly(builder, assembly); }); var endpoints = endpointDataSource.Endpoints; Assert.Equal(3, endpoints.Count); } [Fact] public void NoDiscoveredModesDefaultsToStatic() { var services = CreateServices(typeof(ServerEndpointProvider)); var endpointDataSource = CreateDataSource<App>(services); endpointDataSource.ComponentApplicationBuilderActions.Add(builder => { builder.AddLibrary(new AssemblyComponentLibraryDescriptor( "TestAssembly", Array.Empty<PageComponentBuilder>(), Array.Empty<ComponentBuilder>())); }); var endpoints = endpointDataSource.Endpoints; Assert.Single(endpoints); } // renderModes, providers, components, expectedEndpoints public static TheoryData<IComponentRenderMode[], Type[], Type[], string[]> ConfiguredAndDiscoveredRenderModes => new() { // Auto component sets up server and wasm when available { Array.Empty<IComponentRenderMode>(), new [] { typeof(ServerEndpointProvider), typeof(WebassemblyEndpointProvider) }, new [] { typeof(AutoComponent) }, new [] { "/server", "/webassembly" } }, // Auto component adds webassembly when available { new [] { RenderMode.InteractiveServer }, new [] { typeof(ServerEndpointProvider), typeof(WebassemblyEndpointProvider) }, new [] { typeof(AutoComponent) }, new [] { "/server", "/webassembly" } }, // Auto component adds server when available { new [] { RenderMode.InteractiveWebAssembly }, new [] { typeof(ServerEndpointProvider), typeof(WebassemblyEndpointProvider) }, new [] { typeof(AutoComponent) }, new [] { "/server", "/webassembly" } }, // Auto component does nothing because modes are explicitly configured { new IComponentRenderMode [] { RenderMode.InteractiveServer, RenderMode.InteractiveWebAssembly }, new [] { typeof(ServerEndpointProvider), typeof(WebassemblyEndpointProvider) }, new [] { typeof(AutoComponent) }, new [] { "/server", "/webassembly" } }, // Webassembly component wires up webassembly endpoints { Array.Empty<IComponentRenderMode>(), new [] { typeof(ServerEndpointProvider), typeof(WebassemblyEndpointProvider) }, new [] { typeof(WebAssemblyComponent) }, new [] { "/webassembly" } }, // Webassembly component wires up webassembly endpoints in addition to server endpoints // as they were explicitly configured. { new [] { RenderMode.InteractiveServer }, new [] { typeof(ServerEndpointProvider), typeof(WebassemblyEndpointProvider) }, new [] { typeof(WebAssemblyComponent) }, new [] { "/server", "/webassembly" } }, // Webassembly component does nothing as webassembly is already configured explicitly. { new [] { RenderMode.InteractiveWebAssembly }, new [] { typeof(ServerEndpointProvider), typeof(WebassemblyEndpointProvider) }, new [] { typeof(WebAssemblyComponent) }, new [] { "/webassembly" } }, // Server and webassembly endpoints are added as they were explicitly configured. { new IComponentRenderMode [] { RenderMode.InteractiveServer, RenderMode.InteractiveWebAssembly }, new [] { typeof(ServerEndpointProvider), typeof(WebassemblyEndpointProvider) }, new [] { typeof(WebAssemblyComponent) }, new [] { "/server", "/webassembly" } }, // Server component wires up server components { Array.Empty<IComponentRenderMode>(), new [] { typeof(ServerEndpointProvider), typeof(WebassemblyEndpointProvider) }, new [] { typeof(ServerComponent) }, new [] { "/server" } }, // Server component does nothing as server is already configured. { new [] { RenderMode.InteractiveServer }, new [] { typeof(ServerEndpointProvider), typeof(WebassemblyEndpointProvider) }, new [] { typeof(ServerComponent) }, new [] { "/server" } }, // Server component wires up server endpoints in addition to webassembly endpoints // that were explicitly configured. { new [] { RenderMode.InteractiveWebAssembly }, new [] { typeof(ServerEndpointProvider), typeof(WebassemblyEndpointProvider) }, new [] { typeof(ServerComponent) }, new [] { "/server", "/webassembly" } }, // Server component does nothing as server and webassembly endpoints were explicitly configured. { new IComponentRenderMode [] { RenderMode.InteractiveServer, RenderMode.InteractiveWebAssembly }, new [] { typeof(ServerEndpointProvider), typeof(WebassemblyEndpointProvider) }, new [] { typeof(ServerComponent) }, new [] { "/server", "/webassembly" } }, }; // renderModes, providers, components, expectedEndpoints public static TheoryData<IComponentRenderMode[], Type[], Type[]> SetRenderModesFailures => new() { // Missing server and webassembly, auto requires both. { Array.Empty<IComponentRenderMode>(), Array.Empty<Type>(), new [] { typeof(AutoComponent) } }, // Missing webassembly, auto requires it. { Array.Empty<IComponentRenderMode>(), new [] { typeof(ServerEndpointProvider) }, new [] { typeof(AutoComponent) } }, // Missing server, auto requires it. { Array.Empty<IComponentRenderMode>(), new [] { typeof(WebassemblyEndpointProvider) }, new [] { typeof(AutoComponent) } }, // Missing server. { Array.Empty<IComponentRenderMode>(), Array.Empty<Type>(), new [] { typeof(ServerComponent) } }, // Missing server. { Array.Empty<IComponentRenderMode>(), new [] { typeof(WebassemblyEndpointProvider) }, new [] { typeof(ServerComponent) } }, // Server explicitly configured and missing { new IComponentRenderMode[] { RenderMode.InteractiveServer }, new [] { typeof(WebassemblyEndpointProvider) }, Array.Empty<Type>() }, // Missing webassembly { Array.Empty<IComponentRenderMode>(), Array.Empty<Type>(), new [] { typeof(WebAssemblyComponent) } }, // Missing webassembly { Array.Empty<IComponentRenderMode>(), new [] { typeof(ServerEndpointProvider) }, new [] { typeof(WebAssemblyComponent) } }, // Webassembly explicitly configured and missing { new IComponentRenderMode[] { RenderMode.InteractiveWebAssembly }, new [] { typeof(ServerEndpointProvider) }, Array.Empty<Type>() }, }; private IServiceProvider CreateServices(params Type[] types) { var services = new ServiceCollection(); foreach (var type in types) { services.TryAddEnumerable(ServiceDescriptor.Singleton(typeof(RenderModeEndpointProvider), type)); } services.AddSingleton<IWebHostEnvironment, TestWebHostEnvironment>(); services.AddLogging(); return services.BuildServiceProvider(); } private RazorComponentEndpointDataSource<TComponent> CreateDataSource<TComponent>( IServiceProvider services = null, IComponentRenderMode[] renderModes = null) { var result = new RazorComponentEndpointDataSource<TComponent>( services?.GetService<IEnumerable<RenderModeEndpointProvider>>() ?? Enumerable.Empty<RenderModeEndpointProvider>(), new TestEndpointRouteBuilder(services ?? CreateServices()), new RazorComponentEndpointFactory(), new HotReloadService() { MetadataUpdateSupported = true }); if (renderModes != null) { foreach (var mode in renderModes) { result.Options.ConfiguredRenderModes.Add(mode); } } return result; } [TestRenderMode<InteractiveServerRenderMode>] private class ServerComponent : ComponentBase { } [TestRenderMode<InteractiveAutoRenderMode>] private class AutoComponent : ComponentBase { } [TestRenderMode<InteractiveWebAssemblyRenderMode>] private class WebAssemblyComponent : ComponentBase { } private class ServerEndpointProvider : RenderModeEndpointProvider { public override IEnumerable<RouteEndpointBuilder> GetEndpointBuilders(IComponentRenderMode renderMode, IApplicationBuilder applicationBuilder) { yield return new RouteEndpointBuilder( (context) => Task.CompletedTask, RoutePatternFactory.Parse("/server"), 0); } public override bool Supports(IComponentRenderMode renderMode) => renderMode is InteractiveServerRenderMode or InteractiveAutoRenderMode; } private class WebassemblyEndpointProvider : RenderModeEndpointProvider { public override IEnumerable<RouteEndpointBuilder> GetEndpointBuilders(IComponentRenderMode renderMode, IApplicationBuilder applicationBuilder) { yield return new RouteEndpointBuilder( (context) => Task.CompletedTask, RoutePatternFactory.Parse("/webassembly"), 0); } public override bool Supports(IComponentRenderMode renderMode) => renderMode is InteractiveWebAssemblyRenderMode or InteractiveAutoRenderMode; } private class TestWebHostEnvironment : IWebHostEnvironment { public string ApplicationName { get; set; } = "TestApplication"; public string EnvironmentName { get; set; } = "TestEnvironment"; public string WebRootPath { get; set; } = ""; public IFileProvider WebRootFileProvider { get => ContentRootFileProvider; set { } } public string ContentRootPath { get; set; } = Directory.GetCurrentDirectory(); public IFileProvider ContentRootFileProvider { get; set; } = CreateTestFileProvider(); private static TestFileProvider CreateTestFileProvider() { var provider = new TestFileProvider(); provider.AddFile("site.css", "body { color: red; }"); return provider; } } private class TestEndpointRouteBuilder : IEndpointRouteBuilder { private IServiceProvider _serviceProvider; private List<EndpointDataSource> _dataSources = new(); public TestEndpointRouteBuilder(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider; public IServiceProvider ServiceProvider => _serviceProvider; public ICollection<EndpointDataSource> DataSources => _dataSources; public IApplicationBuilder CreateApplicationBuilder() => new ApplicationBuilder(_serviceProvider); } } public class App : IComponent { public void Attach(RenderHandle renderHandle) { throw new NotImplementedException(); } public Task SetParametersAsync(ParameterView parameters) { throw new NotImplementedException(); } } [Route("/")] public class Index : IComponent { public void Attach(RenderHandle renderHandle) { throw new NotImplementedException(); } public Task SetParametersAsync(ParameterView parameters) { throw new NotImplementedException(); } } [Route("/counter")] public class Counter : IComponent { public void Attach(RenderHandle renderHandle) { throw new NotImplementedException(); } public Task SetParametersAsync(ParameterView parameters) { throw new NotImplementedException(); } }