-
Notifications
You must be signed in to change notification settings - Fork 10.3k
[Blazor][Fixes #12788] Fixes blazor reconnection tests #12813
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using BasicTestApp; | ||
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; | ||
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; | ||
using Microsoft.AspNetCore.E2ETesting; | ||
using OpenQA.Selenium; | ||
using OpenQA.Selenium.Support.UI; | ||
using Xunit; | ||
using Xunit.Abstractions; | ||
|
||
namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests | ||
{ | ||
public class ServerReconnectionTest : BasicTestAppTestBase | ||
{ | ||
public ServerReconnectionTest( | ||
BrowserFixture browserFixture, | ||
ToggleExecutionModeServerFixture<Program> serverFixture, | ||
ITestOutputHelper output) | ||
: base(browserFixture, serverFixture.WithServerExecution(), output) | ||
{ | ||
} | ||
|
||
public string SessionIdentifier { get; set; } = Guid.NewGuid().ToString(); | ||
|
||
protected override void InitializeAsyncCore() | ||
{ | ||
Navigate(ServerPathBase, noReload: false); | ||
|
||
Browser.Manage().Cookies.DeleteCookieNamed("WebSockets.Identifier"); | ||
Browser.Manage().Cookies.AddCookie(new Cookie("WebSockets.Identifier", SessionIdentifier)); | ||
Browser.Navigate().Refresh(); | ||
} | ||
|
||
[Fact] | ||
public void ReconnectUI() | ||
{ | ||
MountTestComponent<ReconnectComponent>(); | ||
Browser.Equal("0", () => Browser.FindElement(By.Id("counter-count")).Text); | ||
|
||
var counterButton = Browser.FindElement(By.Id("counter-click")); | ||
for (int i = 0; i < 10; i++) | ||
{ | ||
counterButton.Click(); | ||
} | ||
|
||
Disconnect(); | ||
|
||
// We should see the 'reconnecting' UI appear | ||
Browser.True( | ||
() => Browser.FindElement(By.Id("components-reconnect-modal"))?.GetCssValue("display") == "block", | ||
TimeSpan.FromSeconds(10)); | ||
|
||
// Then it should disappear | ||
Browser.True(() => Browser.FindElement(By.Id("components-reconnect-modal"))?.GetCssValue("display") == "none", | ||
TimeSpan.FromSeconds(10)); | ||
|
||
counterButton = Browser.FindElement(By.Id("counter-click")); | ||
for (int i = 0; i < 10; i++) | ||
{ | ||
counterButton.Click(); | ||
} | ||
|
||
Browser.Equal("20", () => Browser.FindElement(By.Id("counter-count")).Text); | ||
} | ||
|
||
[Fact] | ||
public void RendersContinueAfterReconnect() | ||
{ | ||
MountTestComponent<ReconnectTicker>(); | ||
|
||
var selector = By.ClassName("tick-value"); | ||
var element = Browser.FindElement(selector); | ||
|
||
var initialValue = element.Text; | ||
|
||
Disconnect(); | ||
|
||
// We should see the 'reconnecting' UI appear | ||
Browser.True( | ||
() => Browser.FindElement(By.Id("components-reconnect-modal"))?.GetCssValue("display") == "block", | ||
TimeSpan.FromSeconds(10)); | ||
|
||
// Then it should disappear | ||
Browser.True(() => Browser.FindElement(By.Id("components-reconnect-modal"))?.GetCssValue("display") == "none", | ||
TimeSpan.FromSeconds(10)); | ||
|
||
// We should receive a render that occurred while disconnected | ||
var currentValue = element.Text; | ||
Assert.NotEqual(initialValue, currentValue); | ||
|
||
// Verify it continues to tick | ||
new WebDriverWait(Browser, TimeSpan.FromSeconds(10)).Until( | ||
_ => element.Text != currentValue); | ||
} | ||
javiercn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
private void Disconnect() | ||
{ | ||
var javascript = (IJavaScriptExecutor)Browser; | ||
Browser.ExecuteAsyncScript($"fetch('/WebSockets/Interrupt?WebSockets.Identifier={SessionIdentifier}').then(r => window['WebSockets.{SessionIdentifier}'] = r.ok)"); | ||
Browser.HasJavaScriptValue(true, $"window['WebSockets.{SessionIdentifier}']", (r) => r != null); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using BasicTestApp; | ||
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; | ||
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; | ||
|
@@ -22,6 +23,8 @@ public class AuthTest : BasicTestAppTestBase | |
protected const string PageRequiringPolicy = "Page requiring policy"; | ||
protected const string PageRequiringRole = "Page requiring role"; | ||
|
||
public string SessionIdentifier { get; } = Guid.NewGuid().ToString(); | ||
|
||
public AuthTest( | ||
BrowserFixture browserFixture, | ||
ToggleExecutionModeServerFixture<Program> serverFixture, | ||
|
@@ -188,6 +191,11 @@ public void Router_RequireRole_NotAuthorized() | |
protected IWebElement MountAndNavigateToAuthTest(string authLinkText) | ||
{ | ||
Navigate(ServerPathBase); | ||
|
||
Browser.Manage().Cookies.DeleteCookieNamed("WebSockets.Identifier"); | ||
Browser.Manage().Cookies.AddCookie(new Cookie("WebSockets.Identifier", SessionIdentifier)); | ||
Browser.Navigate().Refresh(); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a drawback to this running for the client-side tests? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These test don't run on the client, when they do, we can simply key this off to whether or not we are running server-side. |
||
var appElement = MountTestComponent<BasicTestApp.AuthTest.AuthRouter>(); | ||
WaitUntilExists(By.Id("auth-links")); | ||
appElement.FindElement(By.LinkText(authLinkText)).Click(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<h1>Reconnect component</h1> | ||
|
||
<p>This component demonstrates reconnection capabilities for blazor server-side. We click this button a few times, | ||
we force a disconnection and we make sure we are able to successfully reconnect and continue interacting with | ||
the application.</p> | ||
|
||
<p>Current count: <span id="counter-count">@currentCount</span></p> | ||
<p><button id="counter-click" @onclick="@IncrementCount">Click me</button></p> | ||
|
||
@code { | ||
int currentCount = 0; | ||
|
||
void IncrementCount() => currentCount++; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
using System; | ||
using System.Collections.Concurrent; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.AspNetCore.Http.Features; | ||
|
||
namespace Components.TestServer | ||
{ | ||
public class InterruptibleSocketMiddleware | ||
{ | ||
public InterruptibleSocketMiddleware( | ||
RequestDelegate next, | ||
InterruptibleWebSocketOptions options) | ||
{ | ||
Next = next; | ||
Options = options; | ||
} | ||
|
||
public RequestDelegate Next { get; } | ||
public ConcurrentDictionary<string, InterruptibleWebSocket> Registry => Options.Registry; | ||
public InterruptibleWebSocketOptions Options { get; } | ||
|
||
public async Task Invoke(HttpContext context) | ||
{ | ||
var socketsFeature = context.Features.Get<IHttpWebSocketFeature>(); | ||
if (context.Request.Path.Equals(Options.InterruptPath) && context.Request.Query.TryGetValue(Options.WebSocketIdParameterName,out var currentIdentifier)) | ||
{ | ||
if (Registry.TryGetValue(currentIdentifier, out var webSocket)) | ||
{ | ||
webSocket.Disable(); | ||
return; | ||
} | ||
else | ||
{ | ||
context.Response.StatusCode = 400; | ||
return; | ||
} | ||
} | ||
|
||
if (context.Request.Path.Equals(Options.WebSocketPath, StringComparison.OrdinalIgnoreCase) && | ||
context.Request.Cookies.TryGetValue(Options.WebSocketIdParameterName, out var identifier)) | ||
{ | ||
context.Features.Set<IHttpWebSocketFeature>(new InterruptibleWebSocketFeature(socketsFeature, identifier, Registry)); | ||
} | ||
|
||
await Next(context); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to just replace this with navigation + reload? (line 34)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean line 32? I don't see why
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean line 30. Do we need to navigate to the root and then trigger a refresh?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the server-side case yes because the connection already got established by the time we set the cookies.
These tests don't run client-side.