diff --git a/src/Components/Components/src/Routing/RegexConstraintSupport.cs b/src/Components/Components/src/Routing/RegexConstraintSupport.cs index 47210593b779..ad63c2ceeb3c 100644 --- a/src/Components/Components/src/Routing/RegexConstraintSupport.cs +++ b/src/Components/Components/src/Routing/RegexConstraintSupport.cs @@ -10,6 +10,6 @@ internal static class RegexConstraintSupport // until the context switch flows to the runtime. // This value gets updated by the linker when the app is trimmed, so the code will always be removed from // webassembly unless the switch is enabled. - public static bool IsEnabled => + public static bool IsEnabled { get; } = AppContext.TryGetSwitch("Microsoft.AspNetCore.Components.Routing.RegexConstraintSupport", out var enabled) && enabled; } diff --git a/src/Components/Components/test/Microsoft.AspNetCore.Components.Tests.csproj b/src/Components/Components/test/Microsoft.AspNetCore.Components.Tests.csproj index 25a12fa2bdb9..6a5a2cc2d042 100644 --- a/src/Components/Components/test/Microsoft.AspNetCore.Components.Tests.csproj +++ b/src/Components/Components/test/Microsoft.AspNetCore.Components.Tests.csproj @@ -15,6 +15,7 @@ + diff --git a/src/Components/Components/test/RendererTest.cs b/src/Components/Components/test/RendererTest.cs index b672dcaf867a..8d9fa1b04c24 100644 --- a/src/Components/Components/test/RendererTest.cs +++ b/src/Components/Components/test/RendererTest.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Components.RenderTree; using Microsoft.AspNetCore.Components.Test.Helpers; using Microsoft.AspNetCore.InternalTesting; +using Microsoft.DotNet.RemoteExecutor; using Microsoft.Extensions.Logging.Abstractions; namespace Microsoft.AspNetCore.Components.Test; @@ -4981,13 +4982,18 @@ public async Task DisposeAsyncCallsComponentDisposeAsyncOnSyncContext() Assert.True(wasOnSyncContext); } - [Fact] - public async Task NoHotReloadListenersAreRegistered_WhenHotReloadIsDisabled() + [ConditionalFact] + [RemoteExecutionSupported] + public void NoHotReloadListenersAreRegistered_WhenHotReloadIsDisabled() { - // Arrange - try + var options = new RemoteInvokeOptions(); + options.RuntimeConfigurationOptions.Add("System.Reflection.Metadata.MetadataUpdater.IsSupported", "false"); + + using var remoteHandle = RemoteExecutor.Invoke(static async () => { + // Set the switch before any code triggers HotReloadManager type initialization. AppContext.SetSwitch("System.Reflection.Metadata.MetadataUpdater.IsSupported", false); + await using var renderer = new TestRenderer(); var hotReloadManager = new HotReloadManager(); renderer.HotReloadManager = hotReloadManager; @@ -4998,17 +5004,12 @@ public async Task NoHotReloadListenersAreRegistered_WhenHotReloadIsDisabled() builder.CloseElement(); }); - // Act var componentId = renderer.AssignRootComponentId(component); component.TriggerRender(); Assert.False(hotReloadManager.IsSubscribedTo); await renderer.DisposeAsync(); - } - finally - { - AppContext.SetSwitch("System.Reflection.Metadata.MetadataUpdater.IsSupported", true); - } + }, options); } [Fact] diff --git a/src/Components/Endpoints/src/DependencyInjection/HttpNavigationManager.cs b/src/Components/Endpoints/src/DependencyInjection/HttpNavigationManager.cs index 5c2d42fedd24..ff6310f8f8a3 100644 --- a/src/Components/Endpoints/src/DependencyInjection/HttpNavigationManager.cs +++ b/src/Components/Endpoints/src/DependencyInjection/HttpNavigationManager.cs @@ -10,10 +10,12 @@ internal sealed class HttpNavigationManager : NavigationManager, IHostEnvironmen { private const string _disableThrowNavigationException = "Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException"; - [FeatureSwitchDefinition(_disableThrowNavigationException)] - private static bool _throwNavigationException => + private static readonly bool s_throwNavigationException = !AppContext.TryGetSwitch(_disableThrowNavigationException, out var switchValue) || !switchValue; + [FeatureSwitchDefinition(_disableThrowNavigationException)] + private static bool _throwNavigationException => s_throwNavigationException; + private Func? _onNavigateTo; void IHostEnvironmentNavigationManager.Initialize(string baseUri, string uri) => Initialize(baseUri, uri); diff --git a/src/Components/Endpoints/test/EndpointHtmlRendererTest.cs b/src/Components/Endpoints/test/EndpointHtmlRendererTest.cs index 6b5bc359d49d..9b120cc48bbe 100644 --- a/src/Components/Endpoints/test/EndpointHtmlRendererTest.cs +++ b/src/Components/Endpoints/test/EndpointHtmlRendererTest.cs @@ -22,6 +22,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.InternalTesting; +using Microsoft.DotNet.RemoteExecutor; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.FileProviders; @@ -982,62 +983,72 @@ public async Task Rendering_ComponentWithJsInteropThrows() exception.Message); } - [Theory] + [ConditionalTheory] + [RemoteExecutionSupported] [InlineData(true)] [InlineData(false)] - public async Task UriHelperRedirect_ThrowsInvalidOperationException_WhenResponseHasAlreadyStarted(bool allowException) + public void UriHelperRedirect_ThrowsInvalidOperationException_WhenResponseHasAlreadyStarted(bool allowException) { - AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", isEnabled: !allowException); - // Arrange - var ctx = new DefaultHttpContext(); - ctx.Request.Scheme = "http"; - ctx.Request.Host = new HostString("localhost"); - ctx.Request.PathBase = "/base"; - ctx.Request.Path = "/path"; - ctx.Request.QueryString = new QueryString("?query=value"); - ctx.Response.Body = new MemoryStream(); - var responseMock = new Mock(); - responseMock.Setup(r => r.HasStarted).Returns(true); - ctx.Features.Set(responseMock.Object); - var httpContext = GetHttpContext(ctx); - string redirectUri = "http://localhost/redirect"; + var options = new RemoteInvokeOptions(); + options.RuntimeConfigurationOptions.Add( + "Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", + (!allowException).ToString().ToLowerInvariant()); - // Act - if (allowException) + using var remoteHandle = RemoteExecutor.Invoke(static async (allowExceptionStr) => { - var exception = await Assert.ThrowsAsync(async () => await renderer.PrerenderComponentAsync( - httpContext, - typeof(RedirectComponent), - null, - ParameterView.FromDictionary(new Dictionary - { - { "RedirectUri", redirectUri } - }))); - - Assert.Equal("A navigation command was attempted during prerendering after the server already started sending the response. " + - "Navigation commands can not be issued during server-side prerendering after the response from the server has started. Applications must buffer the" + - "response and avoid using features like FlushAsync() before all components on the page have been rendered to prevent failed navigation commands.", - exception.Message); - } - else - { - await renderer.PrerenderComponentAsync( - httpContext, - typeof(RedirectComponent), - null, - ParameterView.FromDictionary(new Dictionary - { - { "RedirectUri", redirectUri } - })); - // read the custom element from the response body - httpContext.Response.Body.Position = 0; - var reader = new StreamReader(httpContext.Response.Body); - var output = await reader.ReadToEndAsync(); - - // Assert that the output contains expected navigation instructions. - var pattern = "^