Skip to content

Commit cf9bec7

Browse files
committed
More E2E tests
1 parent 7703eea commit cf9bec7

File tree

10 files changed

+164
-31
lines changed

10 files changed

+164
-31
lines changed

src/Components/Web.JS/dist/Release/blazor.web.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Components/Web.JS/src/Boot.Server.Common.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ export function startCircuit(): Promise<boolean> {
9898

9999
if (circuit.isDisposedOrDisposing()) {
100100
// If the current circuit is no longer available, create a new one.
101+
appState = discoverServerPersistedState(document) || '';
101102
circuit = new CircuitManager(circuit.getRootComponentManager(), appState, options, logger);
102103
}
103104

src/Components/Web.JS/src/Services/WebRootComponentManager.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,10 @@ export class WebRootComponentManager implements DescriptorHandler, RootComponent
357357
// updates.
358358
} else {
359359
this.unregisterComponent(component);
360+
if (component.assignedRendererId !== undefined && component.interactiveComponentId !== undefined) {
361+
const renderer = getRendererer(component.assignedRendererId);
362+
renderer?.disposeComponent(component.interactiveComponentId);
363+
}
360364

361365
if (component.interactiveComponentId !== undefined) {
362366
// We have an interactive component for this marker, so we'll remove it.

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

Lines changed: 127 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ namespace Microsoft.AspNetCore.Components.E2ETests.Tests;
1717
// For Blazor Server and Webassembly, check SaveStateTest.cs
1818
public class StatePersistenceTest : ServerTestBase<BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<App>>>
1919
{
20+
static int _nextStreamingIdContext;
21+
2022
public StatePersistenceTest(
2123
BrowserFixture browserFixture,
2224
BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<App>> serverFixture,
@@ -25,9 +27,18 @@ public StatePersistenceTest(
2527
{
2628
}
2729

30+
// Separate contexts to ensure that caches and other state don't interfere across tests.
2831
public override Task InitializeAsync()
29-
=> InitializeAsync(BrowserFixture.StreamingContext);
32+
=> InitializeAsync(BrowserFixture.StreamingContext + _nextStreamingIdContext++);
3033

34+
// Validates that we can use persisted state across server, webasembly, and auto modes, with and without
35+
// streaming rendering.
36+
// For streaming rendering, we validate that the state is captured and restored after streaming completes.
37+
// For enhanced navigation we validate that the state is captured at the time components are rendered for
38+
// the first time on the page.
39+
// For auto mode, we validate that the state is captured and restored for both server and wasm runtimes.
40+
// In each case, we validate that the state is available until the initial set of components first render reaches quiescence. Similar to how it works for Server and WebAssembly.
41+
// For server we validate that the state is provided every time a circuit is initialized.
3142
[Theory]
3243
[InlineData(true, typeof(InteractiveServerRenderMode), (string)null)]
3344
[InlineData(true, typeof(InteractiveServerRenderMode), "ServerStreaming")]
@@ -41,7 +52,7 @@ public override Task InitializeAsync()
4152
[InlineData(false, typeof(InteractiveWebAssemblyRenderMode), "WebAssemblyStreaming")]
4253
[InlineData(false, typeof(InteractiveAutoRenderMode), (string)null)]
4354
[InlineData(false, typeof(InteractiveAutoRenderMode), "AutoStreaming")]
44-
public void CanRenderComponentWithPersistedState(bool supressEnhancedNavigation, Type renderMode, string streaming)
55+
public void CanRenderComponentWithPersistedState(bool suppressEnhancedNavigation, Type renderMode, string streaming)
4556
{
4657
var mode = renderMode switch
4758
{
@@ -51,73 +62,167 @@ public void CanRenderComponentWithPersistedState(bool supressEnhancedNavigation,
5162
_ => throw new ArgumentException($"Unknown render mode: {renderMode.Name}")
5263
};
5364

54-
if (!supressEnhancedNavigation)
65+
if (!suppressEnhancedNavigation)
5566
{
5667
// Navigate to a page without components first to make sure that we exercise rendering components
5768
// with enhanced navigation on.
58-
Navigate($"subdir/persistent-state/page-no-components?render-mode={renderMode}&streaming-id={streaming}");
69+
if (streaming == null)
70+
{
71+
Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&suppress-autostart");
72+
}
73+
else
74+
{
75+
Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&streaming-id={streaming}&suppress-autostart");
76+
}
77+
if (mode == "auto")
78+
{
79+
BlockWebAssemblyResourceLoad();
80+
}
81+
Browser.Click(By.Id("call-blazor-start"));
5982
Browser.Click(By.Id("page-with-components-link"));
6083
}
84+
else
85+
{
86+
SuppressEnhancedNavigation(true);
87+
}
88+
89+
if (mode != "auto")
90+
{
91+
RenderComponentsWithPersistentStateAndValidate(suppressEnhancedNavigation, mode, renderMode, streaming);
92+
}
93+
else
94+
{
95+
if (suppressEnhancedNavigation)
96+
{
97+
BlockWebAssemblyResourceLoad();
98+
}
99+
// For auto mode, validate that the state is persisted for both runtimes and is able
100+
// to be loaded on server and wasm.
101+
RenderComponentsWithPersistentStateAndValidate(suppressEnhancedNavigation, mode, renderMode, streaming, interactiveRuntime: "server");
102+
103+
UnblockWebAssemblyResourceLoad();
104+
Browser.Navigate().Refresh();
61105

62-
RenderComponentsWithPersistentStateAndValidate(mode, renderMode, streaming, supressEnhancedNavigation);
106+
RenderComponentsWithPersistentStateAndValidate(suppressEnhancedNavigation, mode, renderMode, streaming, interactiveRuntime: "wasm");
107+
}
63108
}
64109

65-
private void RenderComponentsWithPersistentStateAndValidate(string mode, Type renderMode, string streaming, bool suppressEnhancedNavigation)
110+
[Theory]
111+
[InlineData((string)null)]
112+
[InlineData("ServerStreaming")]
113+
public async Task StateIsProvidedEveryTimeACircuitGetsCreated(string streaming)
66114
{
67-
SuppressEnhancedNavigation(suppressEnhancedNavigation);
115+
var mode = "server";
68116
if (streaming == null)
69117
{
70-
Navigate($"subdir/persistent-state/page-with-components?render-mode={mode}&suppress-autostart");
118+
Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}");
71119
}
72120
else
73121
{
74-
Navigate($"subdir/persistent-state/page-with-components?render-mode={mode}&streaming-id={streaming}&suppress-autostart");
122+
Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&streaming-id={streaming}");
75123
}
76-
AssertPageState(
77-
renderMode: renderMode.Name,
78-
interactive: false,
79-
stateFound: true,
80-
stateValue: "restored",
81-
streamingId: streaming,
82-
streamingCompleted: false);
124+
Browser.Click(By.Id("page-with-components-link"));
83125

84-
Browser.Click(By.Id("call-blazor-start"));
126+
RenderComponentsWithPersistentStateAndValidate(suppresEnhancedNavigation: false, mode, typeof(InteractiveServerRenderMode), streaming);
127+
Browser.Click(By.Id("page-no-components-link"));
128+
// Ensure that the circuit is gone.
129+
await Task.Delay(1000);
130+
Browser.Click(By.Id("page-with-components-link-and-state"));
131+
RenderComponentsWithPersistentStateAndValidate(suppresEnhancedNavigation: false, mode, typeof(InteractiveServerRenderMode), streaming, stateValue: "other");
132+
}
133+
134+
private void BlockWebAssemblyResourceLoad()
135+
{
136+
((IJavaScriptExecutor)Browser).ExecuteScript("sessionStorage.setItem('block-load-boot-resource', 'true')");
137+
138+
// Clear caches so that we can block the resource load
139+
((IJavaScriptExecutor)Browser).ExecuteScript("caches.keys().then(keys => keys.forEach(key => caches.delete(key)))");
140+
}
141+
142+
private void UnblockWebAssemblyResourceLoad()
143+
{
144+
((IJavaScriptExecutor)Browser).ExecuteScript("window.unblockLoadBootResource()");
145+
}
146+
147+
private void RenderComponentsWithPersistentStateAndValidate(
148+
bool suppresEnhancedNavigation,
149+
string mode,
150+
Type renderMode,
151+
string streaming,
152+
string interactiveRuntime = null,
153+
string stateValue = null)
154+
{
155+
stateValue ??= "restored";
156+
// No need to navigate if we are using enhanced navigation, the tests will have already navigated to the page via a link.
157+
if (suppresEnhancedNavigation)
158+
{
159+
// In this case we suppress auto start to check some server side state before we boot Blazor.
160+
if (streaming == null)
161+
{
162+
Navigate($"subdir/persistent-state/page-with-components?render-mode={mode}&suppress-autostart");
163+
}
164+
else
165+
{
166+
Navigate($"subdir/persistent-state/page-with-components?render-mode={mode}&streaming-id={streaming}&suppress-autostart");
167+
}
168+
169+
AssertPageState(
170+
mode: mode,
171+
renderMode: renderMode.Name,
172+
interactive: false,
173+
stateFound: true,
174+
stateValue: stateValue,
175+
streamingId: streaming,
176+
streamingCompleted: false,
177+
interactiveRuntime: interactiveRuntime);
178+
179+
Browser.Click(By.Id("call-blazor-start"));
180+
}
85181

86182
AssertPageState(
183+
mode: mode,
87184
renderMode: renderMode.Name,
88185
interactive: streaming == null,
89186
stateFound: true,
90-
stateValue: "restored",
187+
stateValue: stateValue,
91188
streamingId: streaming,
92-
streamingCompleted: false);
189+
streamingCompleted: false,
190+
interactiveRuntime: interactiveRuntime);
93191

94192
if (streaming != null)
95193
{
96194
Browser.Click(By.Id("end-streaming"));
97195
}
98196

99197
AssertPageState(
198+
mode: mode,
100199
renderMode: renderMode.Name,
101200
interactive: true,
102201
stateFound: true,
103-
stateValue: "restored",
202+
stateValue: stateValue,
104203
streamingId: streaming,
105-
streamingCompleted: true);
204+
streamingCompleted: true,
205+
interactiveRuntime: interactiveRuntime);
106206
}
107207

108208
private void AssertPageState(
209+
string mode,
109210
string renderMode,
110211
bool interactive,
111212
bool stateFound,
112213
string stateValue,
113214
string streamingId = null,
114-
bool streamingCompleted = false)
215+
bool streamingCompleted = false,
216+
string interactiveRuntime = null)
115217
{
116218
Browser.Equal($"Render mode: {renderMode}", () => Browser.FindElement(By.Id("render-mode")).Text);
117219
Browser.Equal($"Streaming id:{streamingId}", () => Browser.FindElement(By.Id("streaming-id")).Text);
118220
Browser.Equal($"Interactive: {interactive}", () => Browser.FindElement(By.Id("interactive")).Text);
119221
if (streamingId == null || streamingCompleted)
120222
{
223+
interactiveRuntime = !interactive ? "none" : mode == "server" || mode == "wasm" ? mode : (interactiveRuntime ?? throw new InvalidOperationException("Specify interactiveRuntime for auto mode"));
224+
225+
Browser.Equal($"Interactive runtime: {interactiveRuntime}", () => Browser.FindElement(By.Id("interactive-runtime")).Text);
121226
Browser.Equal($"State found:{stateFound}", () => Browser.FindElement(By.Id("state-found")).Text);
122227
Browser.Equal($"State value:{stateValue}", () => Browser.FindElement(By.Id("state-value")).Text);
123228
}

src/Components/test/testassets/Components.TestServer/Properties/launchSettings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
// time you restart the test server
1212
"TESTSERVER_USE_DETERMINISTIC_PORTS": "true"
1313
},
14-
"inspectUri": "{wsProtocol}://{url.hostname}:5005/_framework/debug/ws-proxy?browser={browserInspectUri}",
14+
//"inspectUri": "{wsProtocol}://{url.hostname}:5005/_framework/debug/ws-proxy?browser={browserInspectUri}",
1515
"applicationUrl": "http://localhost:5000"
1616
}
1717
}

src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/PersistentState/PageWithComponents.razor

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
<CascadingValue Name="RunningOnServer" Value="true">
1717
@if (!string.IsNullOrEmpty(StreamingId))
1818
{
19-
<StreamingComponentWithPersistentState @rendermode="@_renderMode" StreamingId="@StreamingId" />
19+
<StreamingComponentWithPersistentState @rendermode="@_renderMode" StreamingId="@StreamingId" ServerState="@ServerState" />
2020
}
2121
else
2222
{
23-
<NonStreamingComponentWithPersistentState @rendermode="@_renderMode" />
23+
<NonStreamingComponentWithPersistentState @rendermode="@_renderMode" ServerState="@ServerState" />
2424
}
2525
</CascadingValue>
2626
}
@@ -29,6 +29,9 @@
2929
<a id="end-streaming" href="@($"persistent-state/end-streaming?streaming-id={StreamingId}")" target="_blank">End streaming</a>
3030
}
3131

32+
<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>
33+
34+
3235
@code {
3336

3437
private IComponentRenderMode _renderMode;
@@ -37,6 +40,8 @@
3740

3841
[SupplyParameterFromQuery(Name = "streaming-id")] public string StreamingId { get; set; }
3942

43+
[SupplyParameterFromQuery(Name = "server-state")] public string ServerState { get; set; }
44+
4045
protected override async void OnInitialized()
4146
{
4247
if (!string.IsNullOrEmpty(RenderMode))

src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/PersistentState/PageWithoutComponents.razor

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

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

7+
<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</a>
8+
9+
710
@code {
811
[SupplyParameterFromQuery(Name = "render-mode")] public string RenderMode { get; set; }
912

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,20 @@
33
<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 succeded 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>
44

55
<p id="interactive">Interactive: @(!RunningOnServer)</p>
6+
<p id="interactive-runtime">Interactive runtime: @_interactiveRuntime</p>
67
<p id="state-found">State found:@_stateFound</p>
78
<p id="state-value">State value:@_stateValue</p>
89

910
@code {
1011

1112
private bool _stateFound;
1213
private string _stateValue;
14+
private string _interactiveRuntime;
1315

1416
[Inject] public PersistentComponentState PersistentComponentState { get; set; }
1517

1618
[CascadingParameter(Name = nameof(RunningOnServer))] public bool RunningOnServer { get; set; }
19+
[Parameter] public string ServerState { get; set; }
1720

1821
protected override void OnInitialized()
1922
{
@@ -28,14 +31,18 @@
2831

2932
if (RunningOnServer)
3033
{
34+
_interactiveRuntime = "none";
3135
_stateFound = true;
32-
_stateValue = "restored";
36+
_stateValue = ServerState ?? "restored";
37+
}else
38+
{
39+
_interactiveRuntime = OperatingSystem.IsBrowser() ? "wasm" : "server";
3340
}
3441
}
3542

3643
Task PersistState()
3744
{
38-
PersistentComponentState.PersistAsJson("NonStreamingComponentWithPersistentState", "restored");
45+
PersistentComponentState.PersistAsJson("NonStreamingComponentWithPersistentState", _stateValue);
3946
return Task.CompletedTask;
4047
}
4148
}

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
}
1212
else
1313
{
14+
<p id="interactive-runtime">Interactive runtime: @_interactiveRuntime</p>
1415
<p id="state-found">State found:@_stateFound</p>
1516
<p id="state-value">State value:@_stateValue</p>
1617
}
@@ -20,13 +21,16 @@ else
2021
private bool _streaming;
2122
private bool _stateFound;
2223
private string _stateValue;
24+
private string _interactiveRuntime;
2325

2426
[Inject] public PersistentComponentState PersistentComponentState { get; set; }
2527

2628
[Inject] public AsyncOperationService StreamingManager { get; set; }
2729

2830
[Parameter] public string StreamingId { get; set; }
2931

32+
[Parameter] public string ServerState { get; set; }
33+
3034
[CascadingParameter(Name = nameof(RunningOnServer))] public bool RunningOnServer { get; set; }
3135

3236
protected override async Task OnInitializedAsync()
@@ -39,9 +43,13 @@ else
3943

4044
if (RunningOnServer)
4145
{
46+
_interactiveRuntime = "none";
4247
_streaming = true;
4348
await StreamingManager.Start(StreamingId);
4449
_streaming = false;
50+
}else
51+
{
52+
_interactiveRuntime = OperatingSystem.IsBrowser() ? "wasm" : "server";
4553
}
4654

4755
// We do this to ensure that the state remains accessible during the entire first render
@@ -57,13 +65,13 @@ else
5765
if (RunningOnServer)
5866
{
5967
_stateFound = true;
60-
_stateValue = "restored";
68+
_stateValue = ServerState ?? "restored";
6169
}
6270
}
6371

6472
Task PersistState()
6573
{
66-
PersistentComponentState.PersistAsJson("NonStreamingComponentWithPersistentState", "restored");
74+
PersistentComponentState.PersistAsJson("NonStreamingComponentWithPersistentState", _stateValue);
6775
return Task.CompletedTask;
6876
}
6977
}

0 commit comments

Comments
 (0)