Skip to content

Commit 60fb1e5

Browse files
committed
Fix E2E tests
1 parent 0559d39 commit 60fb1e5

16 files changed

+558
-66
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
using Xunit;
1717
using Xunit.Abstractions;
1818

19-
namespace Microsoft.AspNetCore.Components.E2ETests.ServerExecutionTests
19+
namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
2020
{
2121
public class CircuitGracefulTerminationTests : BasicTestAppTestBase, IDisposable
2222
{

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ void AssertState(string username)
5555

5656
private void PerformReconnection()
5757
{
58-
((IJavaScriptExecutor)Browser).ExecuteScript("Blazor._internal.forceCloseConnection()");
58+
((IJavaScriptExecutor)Browser).ExecuteScript($"fetch('/WebSockets/Interrupt?WebSockets.Identifier={SessionIdentifier}')");
5959

6060
// Wait until the reconnection dialog has been shown but is now hidden
6161
new WebDriverWait(Browser, TimeSpan.FromSeconds(10))
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using BasicTestApp;
5+
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
6+
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
7+
using Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests;
8+
using Microsoft.AspNetCore.E2ETesting;
9+
using OpenQA.Selenium;
10+
using OpenQA.Selenium.Support.UI;
11+
using Xunit;
12+
using Xunit.Abstractions;
13+
14+
namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
15+
{
16+
public class ServerReconnectionTest : BasicTestAppTestBase
17+
{
18+
public ServerReconnectionTest(
19+
BrowserFixture browserFixture,
20+
ToggleExecutionModeServerFixture<Program> serverFixture,
21+
ITestOutputHelper output)
22+
: base(browserFixture, serverFixture.WithServerExecution(), output)
23+
{
24+
}
25+
26+
public List<(Extensions.Logging.LogLevel level, string eventIdName)> Messages { get; private set; }
27+
28+
public string SessionIdentifier { get; set; } = Guid.NewGuid().ToString();
29+
30+
protected override void InitializeAsyncCore()
31+
{
32+
Navigate(ServerPathBase, noReload: false);
33+
34+
Browser.Manage().Cookies.DeleteCookieNamed("WebSockets.Identifier");
35+
Browser.Manage().Cookies.AddCookie(new Cookie("WebSockets.Identifier", SessionIdentifier));
36+
Browser.Navigate().Refresh();
37+
}
38+
39+
[Fact]
40+
public void ReconnectUI()
41+
{
42+
MountTestComponent<ReconnectComponent>();
43+
Browser.Equal("0", () => Browser.FindElement(By.Id("counter-count")).Text);
44+
45+
var counterButton = Browser.FindElement(By.Id("counter-click"));
46+
for (int i = 0; i < 10; i++)
47+
{
48+
counterButton.Click();
49+
}
50+
51+
Disconnect();
52+
53+
// We should see the 'reconnecting' UI appear
54+
var reconnectionDialog = WaitUntilReconnectionDialogExists();
55+
Browser.True(() => reconnectionDialog.GetCssValue("display") == "block");
56+
57+
// Then it should disappear
58+
new WebDriverWait(Browser, TimeSpan.FromSeconds(10))
59+
.Until(driver => reconnectionDialog.GetCssValue("display") == "none");
60+
61+
counterButton = Browser.FindElement(By.Id("counter-click"));
62+
for (int i = 0; i < 10; i++)
63+
{
64+
counterButton.Click();
65+
}
66+
67+
Browser.Equal("20", () => Browser.FindElement(By.Id("counter-count")).Text);
68+
}
69+
70+
[Fact]
71+
public void RendersContinueAfterReconnect()
72+
{
73+
MountTestComponent<ReconnectTicker>();
74+
75+
Browser.FindElement(By.LinkText("Ticker")).Click();
76+
var selector = By.ClassName("tick-value");
77+
var element = Browser.FindElement(selector);
78+
79+
var initialValue = element.Text;
80+
81+
Disconnect();
82+
83+
// We should see the 'reconnecting' UI appear
84+
var reconnectionDialog = WaitUntilReconnectionDialogExists();
85+
Browser.True(() => reconnectionDialog.GetCssValue("display") == "block");
86+
87+
// Then it should disappear
88+
new WebDriverWait(Browser, TimeSpan.FromSeconds(10))
89+
.Until(driver => reconnectionDialog.GetCssValue("display") == "none");
90+
91+
// We should receive a render that occurred while disconnected
92+
var currentValue = element.Text;
93+
Assert.NotEqual(initialValue, currentValue);
94+
95+
// Verify it continues to tick
96+
new WebDriverWait(Browser, TimeSpan.FromSeconds(10)).Until(
97+
_ => element.Text != currentValue);
98+
}
99+
100+
private void Disconnect()
101+
{
102+
var javascript = (IJavaScriptExecutor)Browser;
103+
javascript.ExecuteScript($"fetch('/WebSockets/Interrupt?WebSockets.Identifier={SessionIdentifier}')");
104+
}
105+
106+
private IWebElement WaitUntilReconnectionDialogExists()
107+
{
108+
IWebElement reconnectionDialog = null;
109+
new WebDriverWait(Browser, TimeSpan.FromSeconds(10))
110+
.Until(driver => (reconnectionDialog = driver.FindElement(By.Id("components-reconnect-modal"))) != null);
111+
return reconnectionDialog;
112+
}
113+
}
114+
}

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

Lines changed: 2 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public ServerSideAppTest(
2828

2929
public DateTime LastLogTimeStamp { get; set; } = DateTime.MinValue;
3030

31+
public string SessionIdentifier { get; } = Guid.NewGuid().ToString();
32+
3133
public override async Task InitializeAsync()
3234
{
3335
await base.InitializeAsync();
@@ -138,52 +140,6 @@ public void HasFetchDataPage()
138140
}
139141
}
140142

141-
[Fact]
142-
public void ReconnectUI()
143-
{
144-
Browser.FindElement(By.LinkText("Counter")).Click();
145-
146-
var javascript = (IJavaScriptExecutor)Browser;
147-
javascript.ExecuteScript("Blazor._internal.forceCloseConnection()");
148-
149-
// We should see the 'reconnecting' UI appear
150-
var reconnectionDialog = WaitUntilReconnectionDialogExists();
151-
Browser.True(() => reconnectionDialog.GetCssValue("display") == "block");
152-
153-
// Then it should disappear
154-
new WebDriverWait(Browser, TimeSpan.FromSeconds(10))
155-
.Until(driver => reconnectionDialog.GetCssValue("display") == "none");
156-
}
157-
158-
[Fact]
159-
public void RendersContinueAfterReconnect()
160-
{
161-
Browser.FindElement(By.LinkText("Ticker")).Click();
162-
var selector = By.ClassName("tick-value");
163-
var element = Browser.FindElement(selector);
164-
165-
var initialValue = element.Text;
166-
167-
var javascript = (IJavaScriptExecutor)Browser;
168-
javascript.ExecuteScript("Blazor._internal.forceCloseConnection()");
169-
170-
// We should see the 'reconnecting' UI appear
171-
var reconnectionDialog = WaitUntilReconnectionDialogExists();
172-
Browser.True(() => reconnectionDialog.GetCssValue("display") == "block");
173-
174-
// Then it should disappear
175-
new WebDriverWait(Browser, TimeSpan.FromSeconds(10))
176-
.Until(driver => reconnectionDialog.GetCssValue("display") == "none");
177-
178-
// We should receive a render that occurred while disconnected
179-
var currentValue = element.Text;
180-
Assert.NotEqual(initialValue, currentValue);
181-
182-
// Verify it continues to tick
183-
new WebDriverWait(Browser, TimeSpan.FromSeconds(10)).Until(
184-
_ => element.Text != currentValue);
185-
}
186-
187143
// Since we've removed stateful prerendering, the name which is passed in
188144
// during prerendering cannot be retained. The first interactive render
189145
// will remove it.
@@ -204,13 +160,5 @@ public void ErrorsStopTheRenderingProcess()
204160
Browser.True(() => Browser.Manage().Logs.GetLog(LogType.Browser)
205161
.Any(l => l.Level == LogLevel.Info && l.Message.Contains("Connection disconnected.")));
206162
}
207-
208-
private IWebElement WaitUntilReconnectionDialogExists()
209-
{
210-
IWebElement reconnectionDialog = null;
211-
new WebDriverWait(Browser, TimeSpan.FromSeconds(10))
212-
.Until(driver => (reconnectionDialog = driver.FindElement(By.Id("components-reconnect-modal"))) != null);
213-
return reconnectionDialog;
214-
}
215163
}
216164
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using BasicTestApp;
56
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
67
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
@@ -22,6 +23,8 @@ public class AuthTest : BasicTestAppTestBase
2223
protected const string PageRequiringPolicy = "Page requiring policy";
2324
protected const string PageRequiringRole = "Page requiring role";
2425

26+
public string SessionIdentifier { get; } = Guid.NewGuid().ToString();
27+
2528
public AuthTest(
2629
BrowserFixture browserFixture,
2730
ToggleExecutionModeServerFixture<Program> serverFixture,
@@ -188,6 +191,11 @@ public void Router_RequireRole_NotAuthorized()
188191
protected IWebElement MountAndNavigateToAuthTest(string authLinkText)
189192
{
190193
Navigate(ServerPathBase);
194+
195+
Browser.Manage().Cookies.DeleteCookieNamed("WebSockets.Identifier");
196+
Browser.Manage().Cookies.AddCookie(new Cookie("WebSockets.Identifier", SessionIdentifier));
197+
Browser.Navigate().Refresh();
198+
191199
var appElement = MountTestComponent<BasicTestApp.AuthTest.AuthRouter>();
192200
WaitUntilExists(By.Id("auth-links"));
193201
appElement.FindElement(By.LinkText(authLinkText)).Click();

src/Components/test/testassets/BasicTestApp/Index.razor

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353
<option value="BasicTestApp.ParentChildComponent">Parent component with child</option>
5454
<option value="BasicTestApp.PropertiesChangedHandlerParent">Parent component that changes parameters on child</option>
5555
<option value="BasicTestApp.RazorTemplates">Razor Templates</option>
56+
<option value="BasicTestApp.ReconnectComponent">Reconnect component</option>
57+
<option value="BasicTestApp.ReconnectTicker">Reconnect ticker</option>
5658
<option value="BasicTestApp.RedTextComponent">Red text</option>
5759
<option value="BasicTestApp.ReliabilityComponent">Server reliability component</option>
5860
<option value="BasicTestApp.RenderFragmentToggler">Render fragment renderer</option>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<h1>Reconnect component</h1>
2+
3+
<p>This component demonstrates reconnection capabilities for blazor server-side. We click this button a few times,
4+
we force a disconnection and we make sure we are able to successfully reconnect and continue interacting with
5+
the application.</p>
6+
7+
<p>Current count: <span id="counter-count">@currentCount</span></p>
8+
<p><button id="counter-click" @onclick="@IncrementCount">Click me</button></p>
9+
10+
@code {
11+
int currentCount = 0;
12+
13+
void IncrementCount() => currentCount++;
14+
}

src/Components/test/testassets/ComponentsApp.App/Pages/Ticker.razor renamed to src/Components/test/testassets/BasicTestApp/ReconnectTicker.razor

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1-
@page "/ticker"
2-
@implements IDisposable
1+
@implements System.IDisposable
32
@using System.Threading
43

4+
<h1>Reconnect component</h1>
5+
6+
<p>
7+
This component demonstrates reconnection capabilities for blazor server-side. The ticker starts ticking for a,
8+
few seconds. Then the application reconnects and the renders resume updating the ticker value. (The renders happened
9+
while the circuit was disconnected).
10+
</p>
11+
512
<h1>Ticker</h1>
613
<p class="tick-value">@tick</p>
714

src/Components/test/testassets/ComponentsApp.App/Shared/NavMenu.razor

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,6 @@
2222
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
2323
</NavLink>
2424
</li>
25-
<li class="nav-item px-3">
26-
<NavLink class="nav-link" href="ticker">
27-
<span class="oi oi-list-rich" aria-hidden="true"></span> Ticker
28-
</NavLink>
29-
</li>
3025
<li class="nav-item px-3">
3126
<NavLink class="nav-link" href="greeter">
3227
<span class="oi oi-list-rich" aria-hidden="true"></span> Greeter

src/Components/test/testassets/ComponentsApp.Server/ComponentsApp.Server.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk.Web">
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
44
<TargetFramework>netcoreapp3.0</TargetFramework>

src/Components/test/testassets/ComponentsApp.Server/Startup.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using Microsoft.Extensions.DependencyInjection;
88
using Microsoft.Extensions.Hosting;
99

10-
1110
namespace ComponentsApp.Server
1211
{
1312
public class Startup
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
using System;
2+
using System.IO;
3+
using System.Net.WebSockets;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
7+
namespace Components.TestServer.Infrastructure
8+
{
9+
public class InterruptibleWebSocket : WebSocket
10+
{
11+
private TaskCompletionSource<WebSocketReceiveResult> _disabledReceiveTask =
12+
new TaskCompletionSource<WebSocketReceiveResult>();
13+
14+
public InterruptibleWebSocket(WebSocket socket, string identifier)
15+
{
16+
Socket = socket;
17+
Identifier = identifier;
18+
}
19+
20+
public WebSocket Socket { get; }
21+
public string Identifier { get; }
22+
23+
public override WebSocketCloseStatus? CloseStatus => Socket.CloseStatus;
24+
25+
public override string CloseStatusDescription => Socket.CloseStatusDescription;
26+
27+
public override WebSocketState State => Socket.State;
28+
29+
public override string SubProtocol => Socket.SubProtocol;
30+
31+
public override void Abort()
32+
{
33+
Socket.Abort();
34+
}
35+
36+
public void Disable()
37+
{
38+
_disabledReceiveTask.TrySetException(new IOException($"Socket {Identifier} got disabled by the test."));
39+
}
40+
41+
public override Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken)
42+
{
43+
return Socket.CloseAsync(closeStatus, statusDescription, cancellationToken);
44+
}
45+
46+
public override Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken)
47+
{
48+
return Socket.CloseAsync(closeStatus, statusDescription, cancellationToken);
49+
}
50+
51+
public override void Dispose()
52+
{
53+
Socket.Dispose();
54+
}
55+
56+
public override async Task<WebSocketReceiveResult> ReceiveAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken)
57+
{
58+
if (_disabledReceiveTask.Task.IsCompleted)
59+
{
60+
return await _disabledReceiveTask.Task;
61+
}
62+
else
63+
{
64+
return await await Task.WhenAny(
65+
_disabledReceiveTask.Task,
66+
Socket.ReceiveAsync(buffer, cancellationToken));
67+
}
68+
}
69+
70+
public override Task SendAsync(ArraySegment<byte> buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken)
71+
{
72+
return Socket.SendAsync(buffer, messageType, endOfMessage, cancellationToken);
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)