8000 [Blazor] Add ability to filter persistent component state callbacks based on persistence reason by Copilot · Pull Request #62394 · dotnet/aspnetcore · GitHub
[go: up one dir, main page]

Skip to content

[Blazor] Add ability to filter persistent component state callbacks based on persistence reason #62394

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Add E2E tests for persistent component state filtering functionality
Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
  • Loading branch information
Copilot and javiercn committed Jun 18, 2025
commit 52e84883b4a2d024b5af6ed2777835b51137a99b
280 changes: 280 additions & 0 deletions src/Components/test/E2ETest/Tests/StatePersistenceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,286 @@ private void RenderComponentsWithPersistentStateAndValidate(
interactiveRuntime: interactiveRuntime);
}

[Theory]
[InlineData(true, typeof(InteractiveServerRenderMode), (string)null)]
[InlineData(true, typeof(InteractiveWebAssemblyRenderMode), (string)null)]
[InlineData(true, typeof(InteractiveAutoRenderMode), (string)null)]
[InlineData(false, typeof(InteractiveServerRenderMode), (string)null)]
public void CanFilterPersistentStateCallbacks(bool suppressEnhancedNavigation, Type renderMode, string streaming)
{
var mode = renderMode switch
{
var t when t == typeof(InteractiveServerRenderMode) => "server",
var t when t == typeof(InteractiveWebAssemblyRenderMode) => "wasm",
var t when t == typeof(InteractiveAutoRenderMode) => "auto",
_ => throw new ArgumentException($"Unknown render mode: {renderMode.Name}")
};

if (!suppressEnhancedNavigation)
{
// Navigate to a page without components first to test enhanced navigation filtering
Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&suppress-autostart");
if (mode == "auto")
{
BlockWebAssemblyResourceLoad();
}
Browser.Click(By.Id("call-blazor-start"));
Browser.Click(By.Id("filtering-test-link"));
}
else
{
EnhancedNavigationTestUtil.SuppressEnhancedNavigation(this, true);
if (mode == "auto")
{
BlockWebAssemblyResourceLoad();
}
}

if (mode != "auto")
{
ValidateFilteringBehavior(suppressEnhancedNavigation, mode, renderMode, streaming);
}
else
{
// For auto mode, validate both server and wasm behavior
ValidateFilteringBehavior(suppressEnhancedNavigation, mode, renderMode, streaming, interactiveRuntime: "server");

UnblockWebAssemblyResourceLoad();
Browser.Navigate().Refresh();

ValidateFilteringBehavior(suppressEnhancedNavigation, mode, renderMode, streaming, interactiveRuntime: "wasm");
}
}

[Theory]
[InlineData(true, typeof(InteractiveServerRenderMode))]
[InlineData(true, typeof(InteractiveWebAssemblyRenderMode))]
[InlineData(true, typeof(InteractiveAutoRenderMode))]
[InlineData(false, typeof(InteractiveServerRenderMode))]
public void CanFilterPersistentStateForEnhancedNavigation(bool suppressEnhancedNavigation, Type renderMode)
{
var mode = renderMode switch
{
var t when t == typeof(InteractiveServerRenderMode) => "server",
var t when t == typeof(InteractiveWebAssemblyRenderMode) => "wasm",
var t when t == typeof(InteractiveAutoRenderMode) => "auto",
_ => throw new ArgumentException($"Unknown render mode: {renderMode.Name}")
};

if (!suppressEnhancedNavigation)
{
// Navigate to a page without components first to test enhanced navigation filtering
Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&suppress-autostart");
if (mode == "auto")
{
BlockWebAssemblyResourceLoad();
}
Browser.Click(By.Id("call-blazor-start"));
// Click link that enables persistence during enhanced navigation
Browser.Click(By.Id("filtering-test-link-with-enhanced-nav"));
}
else
{
EnhancedNavigationTestUtil.SuppressEnhancedNavigation(this, true);
if (mode == "auto")
{
BlockWebAssemblyResourceLoad();
}
}

if (mode != "auto")
{
ValidateEnhancedNavigationFiltering(suppressEnhancedNavigation, mode, renderMode);
}
else
{
// For auto mode, validate both server and wasm behavior
ValidateEnhancedNavigationFiltering(suppressEnhancedNavigation, mode, renderMode, interactiveRuntime: "server");

UnblockWebAssemblyResourceLoad();
Browser.Navigate().Refresh();

ValidateEnhancedNavigationFiltering(suppressEnhancedNavigation, mode, renderMode, interactiveRuntime: "wasm");
}
}

[Theory]
[InlineData(typeof(InteractiveServerRenderMode))]
[InlineData(typeof(InteractiveWebAssemblyRenderMode))]
[InlineData(typeof(InteractiveAutoRenderMode))]
public void CanDisablePersistenceForPrerendering(Type renderMode)
{
var mode = renderMode switch
{
var t when t == typeof(InteractiveServerRenderMode) => "server",
var t when t == typeof(InteractiveWebAssemblyRenderMode) => "wasm",
var t when t == typeof(InteractiveAutoRenderMode) => "auto",
_ => throw new ArgumentException($"Unknown render mode: {renderMode.Name}")
};

// Navigate to a page without components first
Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&suppress-autostart");
if (mode == "auto")
{
BlockWebAssemblyResourceLoad();
}
Browser.Click(By.Id("call-blazor-start"));
// Click link that disables persistence during prerendering
Browser.Click(By.Id("filtering-test-link-no-prerendering"));

if (mode != "auto")
{
ValidatePrerenderingFilteringDisabled(mode, renderMode);
}
else
{
// For auto mode, validate both server and wasm behavior
ValidatePrerenderingFilteringDisabled(mode, renderMode, interactiveRuntime: "server");

UnblockWebAssemblyResourceLoad();
Browser.Navigate().Refresh();

ValidatePrerenderingFilteringDisabled(mode, renderMode, interactiveRuntime: "wasm");
}
}

private void ValidateFilteringBehavior(
bool suppressEnhancedNavigation,
string mode,
Type renderMode,
string streaming,
string interactiveRuntime = null)
{
if (suppressEnhancedNavigation)
{
Navigate($"subdir/persistent-state/filtering-test?render-mode={mode}&suppress-autostart");

// Validate server-side state before Blazor starts
AssertFilteringPageState(
mode: mode,
renderMode: renderMode.Name,
interactive: false,
interactiveRuntime: interactiveRuntime);

Browser.Click(By.Id("call-blazor-start"));
}

// Validate state after Blazor is interactive
AssertFilteringPageState(
mode: mode,
renderMode: renderMode.Name,
interactive: true,
interactiveRuntime: interactiveRuntime);
}

private void ValidateEnhancedNavigationFiltering(
bool suppressEnhancedNavigation,
string mode,
Type renderMode,
string interactiveRuntime = null)
{
if (suppressEnhancedNavigation)
{
Navigate($"subdir/persistent-state/filtering-test?render-mode={mode}&persist-enhanced-nav=true&suppress-autostart");

// Validate server-side state before Blazor starts
AssertEnhancedNavFilteringPageState(
mode: mode,
renderMode: renderMode.Name,
interactive: false,
interactiveRuntime: interactiveRuntime);

Browser.Click(By.Id("call-blazor-start"));
}

// Validate state after Blazor is interactive
AssertEnhancedNavFilteringPageState(
mode: mode,
renderMode: renderMode.Name,
interactive: true,
interactiveRuntime: interactiveRuntime);
}

private void ValidatePrerenderingFilteringDisabled(
string mode,
Type renderMode,
string interactiveRuntime = null)
{
// When prerendering persistence is disabled, components should show fresh state
AssertPrerenderingFilteringDisabledPageState(
mode: mode,
renderMode: renderMode.Name,
interactive: true,
interactiveRuntime: interactiveRuntime);
}

private void AssertFilteringPageState(
string mode,
string renderMode,
bool interactive,
string interactiveRuntime = null)
{
Browser.Equal($"Render mode: {renderMode}", () => Browser.FindElement(By.Id("render-mode")).Text);
Browser.Equal($"Interactive: {interactive}", () => Browser.FindElement(By.Id("interactive")).Text);

if (interactive)
{
interactiveRuntime = mode == "server" || mode == "wasm" ? mode : (interactiveRuntime ?? throw new InvalidOperationException("Specify interactiveRuntime for auto mode"));
Browser.Equal($"Interactive runtime: {interactiveRuntime}", () => Browser.FindElement(By.Id("interactive-runtime")).Text);

// Default behavior: persist during prerendering, not during enhanced navigation
Browser.Equal("Prerendering state found:true", () => Browser.FindElement(By.Id("prerendering-state-found")).Text);
Browser.Equal("Enhanced nav state found:false", () => Browser.FindElement(By.Id("enhanced-nav-state-found")).Text);
Browser.Equal("Circuit pause state found:false", () => Browser.FindElement(By.Id("circuit-pause-state-found")).Text);
Browser.Equal("Combined filters state found:true", () => Browser.FindElement(By.Id("combined-filters-state-found")).Text);
}
}

private void AssertEnhancedNavFilteringPageState(
string mode,
string renderMode,
bool interactive,
string interactiveRuntime = null)
{
Browser.Equal($"Render mode: {renderMode}", () => Browser.FindElement(By.Id("render-mode")).Text);
Browser.Equal($"Interactive: {interactive}", () => Browser.FindElement(By.Id("interactive")).Text);

if (interactive)
{
interactiveRuntime = mode == "server" || mode == "wasm" ? mode : (interactiveRuntime ?? throw new InvalidOperationException("Specify interactiveRuntime for auto mode"));
Browser.Equal($"Interactive runtime: {interactiveRuntime}", () => Browser.FindElement(By.Id("interactive-runtime")).Text);

// Enhanced navigation persistence enabled
Browser.Equal("Prerendering state found:true", () => Browser.FindElement(By.Id("prerendering-state-found")).Text);
Browser.Equal("Enhanced nav state found:true", () => Browser.FindElement(By.Id("enhanced-nav-state-found")).Text);
Browser.Equal("Circuit pause state found:false", () => Browser.FindElement(By.Id("circuit-pause-state-found")).Text);
Browser.Equal("Combined filters state found:true", () => Browser.FindElement(By.Id("combined-filters-state-found")).Text);
}
}

private void AssertPrerenderingFilteringDisabledPageState(
string mode,
string renderMode,
bool interactive,
string interactiveRuntime = null)
{
Browser.Equal($"Render mode: {renderMode}", () => Browser.FindElement(By.Id("render-mode")).Text);
Browser.Equal($"Interactive: {interactive}", () => Browser.FindElement(By.Id("interactive")).Text);

if (interactive)
{
interactiveRuntime = mode == "server" || mode == "wasm" ? mode : (interactiveRuntime ?? throw new InvalidOperationException("Specify interactiveRuntime for auto mode"));
Browser.Equal($"Interactive runtime: {interactiveRuntime}", () => Browser.FindElement(By.Id("interactive-runtime")).Text);

// Prerendering persistence disabled - should show fresh values
Browser.Equal("Prerendering state found:false", () => Browser.FindElement(By.Id("prerendering-state-found")).Text);
Browser.Equal("Prerendering state value:fresh-prerendering", () => Browser.FindElement(By.Id("prerendering-state-value")).Text);
Browser.Equal("Enhanced nav state found:false", () => Browser.FindElement(By.Id("enhanced-nav-state-found")).Text);
Browser.Equal("Circuit pause state found:false", () => Browser.FindElement(By.Id("circuit-pause-state-found")).Text);
Browser.Equal("Combined filters state found:false", () => Browser.FindElement(By.Id("combined-filters-state-found")).Text);
}
}

private void AssertPageState(
string mode,
string renderMode,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
@page "/persistent-state/filtering-test"
@using TestContentPackage.PersistentComponents

<h3>Filtered Persistent State Test Page</h3>

<p>
This page tests selective state persistence based on filtering criteria.
It renders components with different filter configurations to validate that state is persisted or skipped based on the persistence reason.
</p>

<p id="render-mode">Render mode: @_renderMode?.GetType()?.Name</p>
<p id="streaming-id">Streaming id:@StreamingId</p>

@if (_renderMode != null)
{
<CascadingValue Name="RunningOnServer" Value="true">
<FilteredPersistentStateComponent @rendermode="@_renderMode"
ServerState="@ServerState"
PersistOnPrerendering="@PersistOnPrerendering"
PersistOnEnhancedNav="@PersistOnEnhancedNav"
PersistOnCircuitPause="@PersistOnCircuitPause" />
</CascadingValue>
}

<a id="page-no-components-link" href=@($"persistent-state/page-no-components?render-mode={RenderMode}&streaming-id={StreamingId}")>Go to page with no components</a>

@code {
private IComponentRenderMode _renderMode;

[SupplyParameterFromQuery(Name = "render-mode")] public string RenderMode { get; set; }
[SupplyParameterFromQuery(Name = "streaming-id")] public string StreamingId { get; set; }
[SupplyParameterFromQuery(Name = "server-state")] public string ServerState { get; set; }
[SupplyParameterFromQuery(Name = "persist-prerendering")] public bool PersistOnPrerendering { get; set; } = true;
[SupplyParameterFromQuery(Name = "persist-enhanced-nav")] public bool PersistOnEnhancedNav { get; set; } = false;
[SupplyParameterFromQuery(Name = "persist-circuit-pause")] public bool PersistOnCircuitPause { get; set; } = true;

protected override void OnInitialized()
{
if (!string.IsNullOrEmpty(RenderMode))
{
switch (RenderMode)
{
case "server":
_renderMode = new InteractiveServerRenderMode(true);
break;
case "wasm":
_renderMode = new InteractiveWebAssemblyRenderMode(true);
break;
case "auto":
_renderMode = new InteractiveAutoRenderMode(true);
break;
default:
throw new ArgumentException($"Invalid render mode: {RenderMode}");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

<a id="page-with-components-link-and-state" href=@($"persistent-state/page-with-components?render-mode={RenderMode}&streaming-id={StreamingId}&server-state=other")>Go to page with components and state</a>

<a id="filtering-test-link" href=@($"persistent-state/filtering-test?render-mode={RenderMode}&streaming-id={StreamingId}")>Go to filtering test page</a>

<a id="filtering-test-link-no-prerendering" href=@($"persistent-state/filtering-test?render-mode={RenderMode}&streaming-id={StreamingId}&persist-prerendering=false")>Go to filtering test page (no prerendering)</a>

<a id="filtering-test-link-with-enhanced-nav" href=@($"persistent-state/filtering-test?render-mode={RenderMode}&streaming-id={StreamingId}&persist-enhanced-nav=true")>Go to filtering test page (with enhanced nav)</a>


@code {
[SupplyParameterFromQuery(Name = "render-mode")] public string RenderMode { get; set; }
Expand Down
Loading
Loading
0