From 651a5392cb94ab6947dd7d2d1eaa36dccc810ba4 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Wed, 2 Jul 2025 10:06:21 +0200 Subject: [PATCH 1/3] Make sure switch is in the correct position + add test with async operation before navigation. --- .../ServerRenderingTests/NoInteractivityTest.cs | 17 ++++++++++++++--- .../RedirectionNotFound-SSR-streaming.razor | 2 +- .../NotFound/RedirectionNotFound-SSR.razor | 7 ++++++- .../NotFound/RedirectionNotFoundComponent.razor | 4 ++-- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs b/src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs index bbeb1ad4d9bb..3de7fdc739b6 100644 --- a/src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs +++ b/src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs @@ -83,18 +83,28 @@ public void NavigatesWithoutInteractivityByRequestRedirection(bool controlFlowBy [Theory] [InlineData(true)] [InlineData(false)] - public void ProgrammaticNavigationToNotExistingPathReExecutesTo404(bool streaming) + public void ProgrammaticNavigationToNotExistingPath_ReExecutesTo404(bool streaming) { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", isEnabled: true); string streamingPath = streaming ? "-streaming" : ""; Navigate($"{ServerPathBase}/reexecution/redirection-not-found-ssr{streamingPath}?navigate-programmatically=true"); AssertReExecutionPageRendered(); } + [Fact] + public void ProgrammaticNavigationToNotExistingPath_AfterAsyncOperation_ReExecutesTo404() + { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", isEnabled: true); + Navigate($"{ServerPathBase}/reexecution/redirection-not-found-ssr?doAsync=true&navigate-programmatically=true"); + AssertReExecutionPageRendered(); + } + [Theory] [InlineData(true)] [InlineData(false)] - public void LinkNavigationToNotExistingPathReExecutesTo404(bool streaming) + public void LinkNavigationToNotExistingPath_ReExecutesTo404(bool streaming) { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", isEnabled: true); string streamingPath = streaming ? "-streaming" : ""; Navigate($"{ServerPathBase}/reexecution/redirection-not-found-ssr{streamingPath}"); Browser.Click(By.Id("link-to-not-existing-page")); @@ -104,8 +114,9 @@ public void LinkNavigationToNotExistingPathReExecutesTo404(bool streaming) [Theory] [InlineData(true)] [InlineData(false)] - public void BrowserNavigationToNotExistingPathReExecutesTo404(bool streaming) + public void BrowserNavigationToNotExistingPath_ReExecutesTo404(bool streaming) { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", isEnabled: true); // non-existing path has to have re-execution middleware set up // so it has to have "reexecution" prefix. Otherwise middleware mapping // will not be activated, see configuration in Startup diff --git a/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/NotFound/RedirectionNotFound-SSR-streaming.razor b/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/NotFound/RedirectionNotFound-SSR-streaming.razor index 74374799ce04..f9d0b9da9b5a 100644 --- a/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/NotFound/RedirectionNotFound-SSR-streaming.razor +++ b/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/NotFound/RedirectionNotFound-SSR-streaming.razor @@ -2,4 +2,4 @@ @page "/reexecution/redirection-not-found-ssr-streaming" @attribute [StreamRendering(true)] - + diff --git a/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/NotFound/RedirectionNotFound-SSR.razor b/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/NotFound/RedirectionNotFound-SSR.razor index 80a579a2e456..d64425d233d9 100644 --- a/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/NotFound/RedirectionNotFound-SSR.razor +++ b/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/NotFound/RedirectionNotFound-SSR.razor @@ -2,4 +2,9 @@ @page "/reexecution/redirection-not-found-ssr" @attribute [StreamRendering(false)] - \ No newline at end of file + + +@code{ + [SupplyParameterFromQuery(Name = "doAsync")] + public bool DoAsync { get; set; } = false; +} diff --git a/src/Components/test/testassets/Components.WasmMinimal/Pages/NotFound/RedirectionNotFoundComponent.razor b/src/Components/test/testassets/Components.WasmMinimal/Pages/NotFound/RedirectionNotFoundComponent.razor index 68df9c6e0c8c..46d8496af1cb 100644 --- a/src/Components/test/testassets/Components.WasmMinimal/Pages/NotFound/RedirectionNotFoundComponent.razor +++ b/src/Components/test/testassets/Components.WasmMinimal/Pages/NotFound/RedirectionNotFoundComponent.razor @@ -16,7 +16,7 @@ public bool? NavigateProgrammatically { get; set; } [Parameter] - public bool StartStreaming { get; set; } = false; + public bool DoAsyncOperationBeforeRedirection { get; set; } = false; [Parameter] public bool WaitForInteractivity { get; set; } = false; @@ -25,7 +25,7 @@ protected override async Task OnInitializedAsync() { - if (StartStreaming) + if (DoAsyncOperationBeforeRedirection) { await Task.Yield(); } From 77b89a99778608034e2c7919c3f99b8401864034 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Wed, 2 Jul 2025 10:15:41 +0200 Subject: [PATCH 2/3] Test SSR redirection with both values of switch. --- .../ServerRenderingTests/RedirectionTest.cs | 126 +++++++++++++----- 1 file changed, 94 insertions(+), 32 deletions(-) diff --git a/src/Components/test/E2ETest/ServerRenderingTests/RedirectionTest.cs b/src/Components/test/E2ETest/ServerRenderingTests/RedirectionTest.cs index f3b70bc87509..dcdebfca3ce4 100644 --- a/src/Components/test/E2ETest/ServerRenderingTests/RedirectionTest.cs +++ b/src/Components/test/E2ETest/ServerRenderingTests/RedirectionTest.cs @@ -32,9 +32,13 @@ public override async Task InitializeAsync() Browser.Equal("Redirections", () => _originalH1Element.Text); } - [Fact] - public void RedirectStreamingGetToInternal() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RedirectStreamingGetToInternal(bool disableThrowNavigationException) { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", disableThrowNavigationException); + Browser.Exists(By.LinkText("Streaming GET with internal redirection")).Click(); AssertElementRemoved(_originalH1Element); Browser.Equal("Scroll to hash", () => Browser.Exists(By.TagName("h1")).Text); @@ -47,16 +51,23 @@ public void RedirectStreamingGetToInternal() Assert.EndsWith("/subdir/redirect", Browser.Url); } - [Fact] - public void RedirectStreamingGetToExternal() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RedirectStreamingGetToExternal(bool disableThrowNavigationException) { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", disableThrowNavigationException); Browser.Exists(By.LinkText("Streaming GET with external redirection")).Click(); Browser.Contains("microsoft.com", () => Browser.Url); } - [Fact] - public void RedirectStreamingPostToInternal() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RedirectStreamingPostToInternal(bool disableThrowNavigationException) { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", disableThrowNavigationException); + Browser.Exists(By.CssSelector("#form-streaming-internal button")).Click(); AssertElementRemoved(_originalH1Element); Browser.Equal("Scroll to hash", () => Browser.Exists(By.TagName("h1")).Text); @@ -69,16 +80,23 @@ public void RedirectStreamingPostToInternal() Assert.EndsWith("/subdir/redirect", Browser.Url); } - [Fact] - public void RedirectStreamingPostToExternal() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RedirectStreamingPostToExternal(bool disableThrowNavigationException) { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", disableThrowNavigationException); Browser.Exists(By.CssSelector("#form-streaming-external button")).Click(); Browser.Contains("microsoft.com", () => Browser.Url); } - [Fact] - public void RedirectEnhancedGetToInternal() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RedirectEnhancedGetToInternal(bool disableThrowNavigationException) { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", disableThrowNavigationException); + // Note that for enhanced nav we can't preserve the hash part of the URL, as it // gets discarded when the browser follows a 'fetch' redirection. This is not solvable // unless we are willing to make the server return extra information so that the @@ -95,16 +113,23 @@ public void RedirectEnhancedGetToInternal() Assert.EndsWith("/subdir/redirect", Browser.Url); } - [Fact] - public void RedirectEnhancedGetToExternal() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RedirectEnhancedGetToExternal(bool disableThrowNavigationException) { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", disableThrowNavigationException); Browser.Exists(By.LinkText("Enhanced GET with external redirection")).Click(); Browser.Contains("microsoft.com", () => Browser.Url); } - [Fact] - public void RedirectEnhancedPostToInternal() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RedirectEnhancedPostToInternal(bool disableThrowNavigationException) { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", disableThrowNavigationException); + // See above for why enhanced nav doesn't support preserving the hash Browser.Exists(By.CssSelector("#form-enhanced-internal button")).Click(); Browser.Equal("Scroll to hash", () => _originalH1Element.Text); @@ -116,16 +141,23 @@ public void RedirectEnhancedPostToInternal() Assert.EndsWith("/subdir/redirect", Browser.Url); } - [Fact] - public void RedirectEnhancedPostToExternal() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RedirectEnhancedPostToExternal(bool disableThrowNavigationException) { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", disableThrowNavigationException); Browser.Exists(By.CssSelector("#form-enhanced-external button")).Click(); Browser.Contains("microsoft.com", () => Browser.Url); } - [Fact] - public void RedirectStreamingEnhancedGetToInternal() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RedirectStreamingEnhancedGetToInternal(bool disableThrowNavigationException) { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", disableThrowNavigationException); + // See above for why enhanced nav doesn't support preserving the hash Browser.Exists(By.LinkText("Streaming enhanced GET with internal redirection")).Click(); Browser.Equal("Scroll to hash", () => _originalH1Element.Text); @@ -137,16 +169,25 @@ public void RedirectStreamingEnhancedGetToInternal() Assert.EndsWith("/subdir/redirect", Browser.Url); } - [Fact] - public void RedirectStreamingEnhancedGetToExternal() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RedirectStreamingEnhancedGetToExternal(bool disableThrowNavigationException) { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", disableThrowNavigationException); + Browser.Exists(By.LinkText("Streaming enhanced GET with external redirection")).Click(); Browser.Contains("microsoft.com", () => Browser.Url); } + - [Fact] - public void RedirectStreamingEnhancedPostToInternal() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RedirectStreamingEnhancedPostToInternal(bool disableThrowNavigationException) { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", disableThrowNavigationException); + // See above for why enhanced nav doesn't support preserving the hash Browser.Exists(By.CssSelector("#form-streaming-enhanced-internal button")).Click(); Browser.Equal("Scroll to hash", () => _originalH1Element.Text); @@ -158,16 +199,24 @@ public void RedirectStreamingEnhancedPostToInternal() Assert.EndsWith("/subdir/redirect", Browser.Url); } - [Fact] - public void RedirectStreamingEnhancedPostToExternal() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RedirectStreamingEnhancedPostToExternal(bool disableThrowNavigationException) { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", disableThrowNavigationException); + Browser.Exists(By.CssSelector("#form-streaming-enhanced-external button")).Click(); Browser.Contains("microsoft.com", () => Browser.Url); } - [Fact] - public void RedirectEnhancedNonBlazorGetToInternal() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RedirectEnhancedNonBlazorGetToInternal(bool disableThrowNavigationException) { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", disableThrowNavigationException); + // See above for why enhanced nav doesn't support preserving the hash Browser.Exists(By.LinkText("Enhanced GET to non-Blazor endpoint with internal redirection")).Click(); Browser.Equal("Scroll to hash", () => _originalH1Element.Text); @@ -179,16 +228,24 @@ public void RedirectEnhancedNonBlazorGetToInternal() Assert.EndsWith("/subdir/redirect", Browser.Url); } - [Fact] - public void RedirectEnhancedNonBlazorGetToExternal() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RedirectEnhancedNonBlazorGetToExternal(bool disableThrowNavigationException) { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", disableThrowNavigationException); + Browser.Exists(By.LinkText("Enhanced GET to non-Blazor endpoint with external redirection")).Click(); Browser.Contains("microsoft.com", () => Browser.Url); } - [Fact] - public void RedirectEnhancedNonBlazorPostToInternal() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RedirectEnhancedNonBlazorPostToInternal(bool disableThrowNavigationException) { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", disableThrowNavigationException); + // See above for why enhanced nav doesn't support preserving the hash Browser.Exists(By.CssSelector("#form-nonblazor-enhanced-internal button")).Click(); Browser.Equal("Scroll to hash", () => _originalH1Element.Text); @@ -205,9 +262,14 @@ public void RedirectEnhancedNonBlazorPostToInternal() // response to something like a 200 that the 'fetch' is allowed to read (embedding the // destination URL). - [Fact] - public void RedirectEnhancedGetToInternalWithErrorBoundary() + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RedirectEnhancedGetToInternalWithErrorBoundary(bool disableThrowNavigationException) { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", disableThrowNavigationException); + // This test verifies that redirection works even if an ErrorBoundary wraps // a component throwing a NavigationException. From 99f9cb81e20034768528b1328bc131d79d4a7ee8 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Wed, 2 Jul 2025 12:56:39 +0200 Subject: [PATCH 3/3] Update RedirectionTest.cs Remove multiple blank lines. --- .../test/E2ETest/ServerRenderingTests/RedirectionTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Components/test/E2ETest/ServerRenderingTests/RedirectionTest.cs b/src/Components/test/E2ETest/ServerRenderingTests/RedirectionTest.cs index dcdebfca3ce4..4d9830c09033 100644 --- a/src/Components/test/E2ETest/ServerRenderingTests/RedirectionTest.cs +++ b/src/Components/test/E2ETest/ServerRenderingTests/RedirectionTest.cs @@ -262,7 +262,6 @@ public void RedirectEnhancedNonBlazorPostToInternal(bool disableThrowNavigationE // response to something like a 200 that the 'fetch' is allowed to read (embedding the // destination URL). - [Theory] [InlineData(true)] [InlineData(false)]