Skip to content

Commit 60fcdf5

Browse files
Copilotjaviercn
andcommitted
Implement E2E tests for scenario-based persistent component state filtering
Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
1 parent 59e2436 commit 60fcdf5

File tree

6 files changed

+217
-2
lines changed

6 files changed

+217
-2
lines changed

src/Components/test/E2ETest/ServerExecutionTests/ServerResumeTests.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,53 @@ private void TriggerClientPauseAndInteract(IJavaScriptExecutor javascript)
188188

189189
Browser.Exists(By.Id("increment-persistent-counter-count")).Click();
190190
}
191+
192+
[Fact]
193+
public void NonPersistedStateIsNotRestoredAfterDisconnection()
194+
{
195+
// Verify initial state during/after SSR - NonPersistedCounter should be 5
196+
Browser.Equal("5", () => Browser.Exists(By.Id("non-persisted-counter")).Text);
197+
198+
// Wait for interactivity - the value should still be 5
199+
Browser.Exists(By.Id("render-mode-interactive"));
200+
Browser.Equal("5", () => Browser.Exists(By.Id("non-persisted-counter")).Text);
201+
202+
// Increment the non-persisted counter to 6 to show it works during interactive session
203+
Browser.Exists(By.Id("increment-non-persisted-counter")).Click();
204+
Browser.Equal("6", () => Browser.Exists(By.Id("non-persisted-counter")).Text);
205+
206+
// Also increment the persistent counter to show the contrast
207+
Browser.Exists(By.Id("increment-persistent-counter-count")).Click();
208+
Browser.Equal("1", () => Browser.Exists(By.Id("persistent-counter-count")).Text);
209+
210+
// Force disconnection and reconnection
211+
var javascript = (IJavaScriptExecutor)Browser;
212+
javascript.ExecuteScript("window.replaceReconnectCallback()");
213+
TriggerReconnectAndInteract(javascript);
214+
215+
// After reconnection:
216+
// - Persistent counter should be 2 (was 1, incremented by TriggerReconnectAndInteract)
217+
Browser.Equal("2", () => Browser.Exists(By.Id("persistent-counter-count")).Text);
218+
219+
// - Non-persisted counter should be 0 (default value) because RestoreStateOnPrerendering
220+
// prevented it from being restored after disconnection
221+
Browser.Equal("0", () => Browser.Exists(By.Id("non-persisted-counter")).Text);
222+
223+
// Verify the non-persisted counter can still be incremented in the new session
224+
Browser.Exists(By.Id("increment-non-persisted-counter")).Click();
225+
Browser.Equal("1", () => Browser.Exists(By.Id("non-persisted-counter")).Text);
226+
227+
// Test repeatability - trigger another disconnection cycle
228+
javascript.ExecuteScript("resetReconnect()");
229+
TriggerReconnectAndInteract(javascript);
230+
231+
// After second reconnection:
232+
// - Persistent counter should be 3
233+
Browser.Equal("3", () => Browser.Exists(By.Id("persistent-counter-count")).Text);
234+
235+
// - Non-persisted counter should be 0 again (reset to default)
236+
Browser.Equal("0", () => Browser.Exists(By.Id("non-persisted-counter")).Text);
237+
}
191238
}
192239

193240
public class CustomUIServerResumeTests : ServerResumeTests

src/Components/test/E2ETest/ServerRenderingTests/InteractivityTest.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,8 @@ public void CanPersistPrerenderedStateDeclaratively_Server()
10601060

10611061
Browser.Equal("restored", () => Browser.FindElement(By.Id("server")).Text);
10621062
Browser.Equal("Server", () => Browser.FindElement(By.Id("render-mode-server")).Text);
1063+
Browser.Equal("restored-prerendering-enabled", () => Browser.FindElement(By.Id("prerendering-enabled-server")).Text);
1064+
Browser.Equal("restored-prerendering-disabled", () => Browser.FindElement(By.Id("prerendering-disabled-server")).Text);
10631065
}
10641066

10651067
[Fact]

src/Components/test/E2ETest/Tests/StatePersistenceTest.cs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,4 +236,117 @@ private void AssertPageState(
236236
Browser.Equal("Streaming: True", () => Browser.FindElement(By.Id("streaming")).Text);
237237
}
238238
}
239+
240+
[Theory]
241+
[InlineData(typeof(InteractiveServerRenderMode), (string)null)]
242+
[InlineData(typeof(InteractiveServerRenderMode), "ServerStreaming")]
243+
[InlineData(typeof(InteractiveWebAssemblyRenderMode), (string)null)]
244+
[InlineData(typeof(InteractiveWebAssemblyRenderMode), "WebAssemblyStreaming")]
245+
[InlineData(typeof(InteractiveAutoRenderMode), (string)null)]
246+
[InlineData(typeof(InteractiveAutoRenderMode), "AutoStreaming")]
247+
public void ComponentWithUpdateStateOnEnhancedNavigationReceivesStateUpdates(Type renderMode, string streaming)
248+
{
249+
var mode = renderMode switch
250+
{
251+
var t when t == typeof(InteractiveServerRenderMode) => "server",
252+
var t when t == typeof(InteractiveWebAssemblyRenderMode) => "wasm",
253+
var t when t == typeof(InteractiveAutoRenderMode) => "auto",
254+
_ => throw new ArgumentException($"Unknown render mode: {renderMode.Name}")
255+
};
256+
257+
// Step 1: Navigate to page without components first to establish initial state
258+
if (streaming == null)
259+
{
260+
Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&suppress-autostart");
261+
}
262+
else
263+
{
264+
Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&streaming-id={streaming}&suppress-autostart");
265+
}
266+
267+
if (mode == "auto")
268+
{
269+
BlockWebAssemblyResourceLoad();
270+
}
271+
272+
Browser.Click(By.Id("call-blazor-start"));
273+
Browser.Click(By.Id("page-with-components-link"));
274+
275+
// Step 2: Validate initial state - no enhanced nav state should be found
276+
ValidateEnhancedNavState(
277+
mode: mode,
278+
renderMode: renderMode.Name,
279+
interactive: streaming == null,
280+
enhancedNavStateFound: false,
281+
enhancedNavStateValue: "no-enhanced-nav-state",
282+
streamingId: streaming,
283+
streamingCompleted: false);
284+
285+
if (streaming != null)
286+
{
287+
Browser.Click(By.Id("end-streaming"));
288+
ValidateEnhancedNavState(
289+
mode: mode,
290+
renderMode: renderMode.Name,
291+
interactive: true,
292+
enhancedNavStateFound: false,
293+
enhancedNavStateValue: "no-enhanced-nav-state",
294+
streamingId: streaming,
295+
streamingCompleted: true);
296+
}
297+
298+
// Step 3: Navigate back to page without components (this persists state)
299+
Browser.Click(By.Id("page-no-components-link"));
300+
301+
// Step 4: Navigate back to page with components via enhanced navigation
302+
// This should trigger [UpdateStateOnEnhancedNavigation] and update the state
303+
Browser.Click(By.Id("page-with-components-link"));
304+
305+
// Step 5: Validate that enhanced navigation state was updated
306+
ValidateEnhancedNavState(
307+
mode: mode,
308+
renderMode: renderMode.Name,
309+
interactive: streaming == null,
310+
enhancedNavStateFound: true,
311+
enhancedNavStateValue: "enhanced-nav-updated",
312+
streamingId: streaming,
313+
streamingCompleted: streaming == null);
314+
315+
if (streaming != null)
316+
{
317+
Browser.Click(By.Id("end-streaming"));
318+
ValidateEnhancedNavState(
319+
mode: mode,
320+
renderMode: renderMode.Name,
321+
interactive: true,
322+
enhancedNavStateFound: true,
323+
enhancedNavStateValue: "enhanced-nav-updated",
324+
streamingId: streaming,
325+
streamingCompleted: true);
326+
}
327+
}
328+
329+
private void ValidateEnhancedNavState(
330+
string mode,
331+
string renderMode,
332+
bool interactive,
333+
bool enhancedNavStateFound,
334+
string enhancedNavStateValue,
335+
string streamingId = null,
336+
bool streamingCompleted = false)
337+
{
338+
Browser.Equal($"Render mode: {renderMode}", () => Browser.FindElement(By.Id("render-mode")).Text);
339+
Browser.Equal($"Streaming id:{streamingId}", () => Browser.FindElement(By.Id("streaming-id")).Text);
340+
Browser.Equal($"Interactive: {interactive}", () => Browser.FindElement(By.Id("interactive")).Text);
341+
342+
if (streamingId == null || streamingCompleted)
343+
{
344+
Browser.Equal($"Enhanced nav state found:{enhancedNavStateFound}", () => Browser.FindElement(By.Id("enhanced-nav-state-found")).Text);
345+
Browser.Equal($"Enhanced nav state value:{enhancedNavStateValue}", () => Browser.FindElement(By.Id("enhanced-nav-state-value")).Text);
346+
}
347+
else
348+
{
349+
Browser.Equal("Streaming: True", () => Browser.FindElement(By.Id("streaming")).Text);
350+
}
351+
}
239352
}

src/Components/test/testassets/TestContentPackage/DeclarativePersistStateComponent.razor

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
<p>Application state is <span id="@KeyName">@Value</span></p>
1+
@using Microsoft.AspNetCore.Components.Web
2+
<p>Application state is <span id="@KeyName">@Value</span></p>
23
<p>Render mode: <span id="render-mode-@KeyName">@_renderMode</span></p>
4+
<p>Prerendering enabled state: <span id="prerendering-enabled-@KeyName">@PrerenderingEnabledValue</span></p>
5+
<p>Prerendering disabled state: <span id="prerendering-disabled-@KeyName">@PrerenderingDisabledValue</span></p>
36

47
@code {
58
[Parameter, EditorRequired]
@@ -11,11 +14,21 @@
1114
[SupplyParameterFromPersistentComponentState]
1215
public string Value { get; set; }
1316

17+
[SupplyParameterFromPersistentComponentState]
18+
[RestoreStateOnPrerendering]
19+
public string PrerenderingEnabledValue { get; set; }
20+
21+
[SupplyParameterFromPersistentComponentState]
22+
[UpdateStateOnEnhancedNavigation]
23+
public string PrerenderingDisabledValue { get; set; }
24+
1425
private string _renderMode = "SSR";
1526

1627
protected override void OnInitialized()
1728
{
1829
Value ??= !RendererInfo.IsInteractive ? InitialValue : "not restored";
30+
PrerenderingEnabledValue ??= !RendererInfo.IsInteractive ? $"{InitialValue}-prerendering-enabled" : "not restored";
31+
PrerenderingDisabledValue ??= !RendererInfo.IsInteractive ? $"{InitialValue}-prerendering-disabled" : "not restored";
1932
_renderMode = OperatingSystem.IsBrowser() ? "WebAssembly" : "Server";
2033
}
2134
}

src/Components/test/testassets/TestContentPackage/PersistentComponents/NonStreamingComponentWithPersistentState.razor

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,33 @@
1-
<p>Non streaming component with persistent state</p>
1+
@using Microsoft.AspNetCore.Components.Web
2+
<p>Non streaming component with persistent state</p>
23

34
<p>This component demonstrates state persistence in the absence of streaming rendering. When the component renders it will try to restore the state and if present display that it succeeded in doing so and the restored value. If the state is not present, it will indicate it didn't find it and display a "fresh" value.</p>
45

56
<p id="interactive">Interactive: @(!RunningOnServer)</p>
67
<p id="interactive-runtime">Interactive runtime: @_interactiveRuntime</p>
78
<p id="state-found">State found:@_stateFound</p>
89
<p id="state-value">State value:@_stateValue</p>
10+
<p id="enhanced-nav-state-found">Enhanced nav state found:@_enhancedNavStateFound</p>
11+
<p id="enhanced-nav-state-value">Enhanced nav state value:@_enhancedNavState</p>
912

1013
@code {
1114

1215
private bool _stateFound;
1316
private string _stateValue;
1417
private string _interactiveRuntime;
18+
private bool _enhancedNavStateFound;
19+
private string _enhancedNavState;
1520

1621
[Inject] public PersistentComponentState PersistentComponentState { get; set; }
1722

1823
[CascadingParameter(Name = nameof(RunningOnServer))] public bool RunningOnServer { get; set; }
1924
[Parameter] public string ServerState { get; set; }
2025

26+
[Parameter]
27+
[SupplyParameterFromPersistentComponentState]
28+
[UpdateStateOnEnhancedNavigation]
29+
public string EnhancedNavState { get; set; }
30+
2131
protected override void OnInitialized()
2232
{
2333
PersistentComponentState.RegisterOnPersisting(PersistState);
@@ -39,11 +49,23 @@
3949
{
4050
_interactiveRuntime = OperatingSystem.IsBrowser() ? "wasm" : "server";
4151
}
52+
53+
// Track enhanced navigation state updates
54+
_enhancedNavState = EnhancedNavState ?? "no-enhanced-nav-state";
55+
_enhancedNavStateFound = !string.IsNullOrEmpty(EnhancedNavState);
56+
}
57+
58+
protected override void OnParametersSet()
59+
{
60+
// This will be called during enhanced navigation when [UpdateStateOnEnhancedNavigation] triggers
61+
_enhancedNavState = EnhancedNavState ?? "no-enhanced-nav-state";
62+
_enhancedNavStateFound = !string.IsNullOrEmpty(EnhancedNavState);
4263
}
4364

4465
Task PersistState()
4566
{
4667
PersistentComponentState.PersistAsJson("NonStreamingComponentWithPersistentState", _stateValue);
68+
PersistentComponentState.PersistAsJson("EnhancedNavState", "enhanced-nav-updated");
4769
return Task.CompletedTask;
4870
}
4971
}

src/Components/test/testassets/TestContentPackage/PersistentCounter.razor

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@using Microsoft.JSInterop
2+
@using Microsoft.AspNetCore.Components.Web
23
@inject IJSRuntime JSRuntime
34

45
<!-- The render changes when the component is resumed because it runs in a separate circuit -->
@@ -9,13 +10,19 @@
910
<p>Current render GUID: <span id="persistent-counter-render">@Guid.NewGuid().ToString()</span></p>
1011

1112
<p>Current count: <span id="persistent-counter-count">@State.Count</span></p>
13+
<p>Non-persisted counter: <span id="non-persisted-counter">@NonPersistedCounter</span></p>
1214

1315
<button id="increment-persistent-counter-count" @onclick="IncrementCount">Click me</button>
16+
<button id="increment-non-persisted-counter" @onclick="IncrementNonPersistedCount">Increment non-persisted</button>
1417

1518
@code {
1619

1720
[SupplyParameterFromPersistentComponentState] public CounterState State { get; set; }
1821

22+
[SupplyParameterFromPersistentComponentState]
23+
[RestoreStateOnPrerendering]
24+
public int NonPersistedCounter { get; set; }
25+
1926
public class CounterState
2027
{
2128
public int Count { get; set; } = 0;
@@ -25,10 +32,21 @@
2532
{
2633
// State is preserved across disconnections
2734
State ??= new CounterState();
35+
36+
// Initialize non-persisted counter to 5 during SSR (before interactivity)
37+
if (!RendererInfo.IsInteractive)
38+
{
39+
NonPersistedCounter = 5;
40+
}
2841
}
2942

3043
private void IncrementCount()
3144
{
3245
State.Count = State.Count + 1;
3346
}
47+
48+
private void IncrementNonPersistedCount()
49+
{
50+
NonPersistedCounter = NonPersistedCounter + 1;
51+
}
3452
}

0 commit comments

Comments
 (0)