Skip to content

Commit 5b347d2

Browse files
committed
Add tests for custom UI and update templates
1 parent ab49cac commit 5b347d2

File tree

8 files changed

+313
-6
lines changed

8 files changed

+313
-6
lines changed

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

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414

1515
namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests;
1616

17-
public class ServerResumeTestsTest : ServerTestBase<BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<Root>>>
17+
public class ServerResumeTests : ServerTestBase<BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<Root>>>
1818
{
19-
public ServerResumeTestsTest(
19+
public ServerResumeTests(
2020
BrowserFixture browserFixture,
2121
BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<Root>> serverFixture,
2222
ITestOutputHelper output)
@@ -27,10 +27,12 @@ public ServerResumeTestsTest(
2727

2828
protected override void InitializeAsyncCore()
2929
{
30-
Navigate("/subdir/persistent-state/disconnection");
30+
Navigate(TestUrl);
3131
Browser.Exists(By.Id("render-mode-interactive"));
3232
}
3333

34+
public string TestUrl { get; set; } = "/subdir/persistent-state/disconnection";
35+
3436
[Fact]
3537
public void CanResumeCircuitAfterDisconnection()
3638
{
@@ -179,3 +181,15 @@ private void TriggerClientPauseAndInteract(IJavaScriptExecutor javascript)
179181
Browser.Exists(By.Id("increment-persistent-counter-count")).Click();
180182
}
181183
}
184+
185+
public class CustomUIServerResumeTests : ServerResumeTests
186+
{
187+
public CustomUIServerResumeTests(
188+
BrowserFixture browserFixture,
189+
BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<Root>> serverFixture,
190+
ITestOutputHelper output)
191+
: base(browserFixture, serverFixture, output)
192+
{
193+
TestUrl = "/subdir/persistent-state/disconnection?use-custom-ui=true";
194+
}
195+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<script type="module" src="@Assets["RazorComponents/Components/ReconnectModal.razor.js"]"></script>
2+
3+
<dialog id="components-reconnect-modal" data-nosnippet>
4+
<div class="components-reconnect-container">
5+
<div class="components-rejoining-animation" aria-hidden="true">
6+
<div></div>
7+
<div></div>
8+
</div>
9+
<p class="components-reconnect-first-attempt-visible">
10+
Rejoining the server...
11+
</p>
12+
<p class="components-reconnect-repeated-attempt-visible">
13+
Rejoin failed... trying again in <span id="components-seconds-to-next-attempt"></span> seconds.
14+
</p>
15+
<p class="components-reconnect-failed-visible">
16+
Failed to rejoin.<br />Please retry or reload the page.
17+
</p>
18+
<button id="components-reconnect-button" class="components-reconnect-failed-visible">
19+
Retry
20+
</button>
21+
<p class="components-pause-visible">
22+
The session has been paused by the server.
23+
</p>
24+
<button id="components-resume-button" class="components-pause-visible">
25+
Resume
26+
</button>
27+
<p class="components-resume-failed-visible">
28+
Failed to resume the session.<br />Please reload the page.
29+
</p>
30+
</div>
31+
</dialog>
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/* Comment */
2+
3+
.components-reconnect-first-attempt-visible,
4+
.components-reconnect-repeated-attempt-visible,
5+
.components-reconnect-failed-visible,
6+
.components-pause-visible,
7+
.components-resume-failed-visible,
8+
.components-rejoining-animation {
9+
display: none;
10+
}
11+
12+
#components-reconnect-modal.components-reconnect-show .components-reconnect-first-attempt-visible,
13+
#components-reconnect-modal.components-reconnect-show .components-rejoining-animation,
14+
#components-reconnect-modal.components-reconnect-paused .components-pause-visible,
15+
#components-reconnect-modal.components-reconnect-resume-failed .components-resume-failed-visible,
16+
#components-reconnect-modal.components-reconnect-retrying,
17+
#components-reconnect-modal.components-reconnect-retrying .components-reconnect-repeated-attempt-visible,
18+
#components-reconnect-modal.components-reconnect-retrying .components-rejoining-animation,
19+
#components-reconnect-modal.components-reconnect-failed,
20+
#components-reconnect-modal.components-reconnect-failed .components-reconnect-failed-visible {
21+
display: block;
22+
}
23+
24+
25+
#components-reconnect-modal {
26+
background-color: white;
27+
width: 20rem;
28+
margin: 20vh auto;
29+
padding: 2rem;
30+
border: 0;
31+
border-radius: 0.5rem;
32+
box-shadow: 0 3px 6px 2px rgba(0, 0, 0, 0.3);
33+
opacity: 0;
34+
transition: display 0.5s allow-discrete, overlay 0.5s allow-discrete;
35+
animation: components-reconnect-modal-fadeOutOpacity 0.5s both;
36+
&[open]
37+
38+
{
39+
animation: components-reconnect-modal-slideUp 1.5s cubic-bezier(.05, .89, .25, 1.02) 0.3s, components-reconnect-modal-fadeInOpacity 0.5s ease-in-out 0.3s;
40+
animation-fill-mode: both;
41+
}
42+
43+
}
44+
45+
#components-reconnect-modal::backdrop {
46+
background-color: rgba(0, 0, 0, 0.4);
47+
animation: components-reconnect-modal-fadeInOpacity 0.5s ease-in-out;
48+
opacity: 1;
49+
}
50+
51+
@keyframes components-reconnect-modal-slideUp {
52+
0% {
53+
transform: translateY(30px) scale(0.95);
54+
}
55+
56+
100% {
57+
transform: translateY(0);
58+
}
59+
}
60+
61+
@keyframes components-reconnect-modal-fadeInOpacity {
62+
0% {
63+
opacity: 0;
64+
}
65+
66+
100% {
67+
opacity: 1;
68+
}
69+
}
70+
71+
@keyframes components-reconnect-modal-fadeOutOpacity {
72+
0% {
73+
opacity: 1;
74+
}
75+
76+
100% {
77+
opacity: 0;
78+
}
79+
}
80+
81+
.components-reconnect-container {
82+
display: flex;
83+
flex-direction: column;
84+
align-items: center;
85+
gap: 1rem;
86+
}
87+
88+
#components-reconnect-modal p {
89+
margin: 0;
90+
text-align: center;
91+
}
92+
93+
#components-reconnect-modal button {
94+
border: 0;
95+
background-color: #6b9ed2;
96+
color: white;
97+
padding: 4px 24px;
98+
border-radius: 4px;
99+
}
100+
101+
#components-reconnect-modal button:hover {
102+
background-color: #3b6ea2;
103+
}
104+
105+
#components-reconnect-modal button:active {
106+
background-color: #6b9ed2;
107+
}
108+
109+
.components-rejoining-animation {
110+
position: relative;
111+
width: 80px;
112+
height: 80px;
113+
}
114+
115+
.components-rejoining-animation div {
116+
position: absolute;
117+
border: 3px solid #0087ff;
118+
opacity: 1;
119+
border-radius: 50%;
120+
animation: components-rejoining-animation 1.5s cubic-bezier(0, 0.2, 0.8, 1) infinite;
121+
}
122+
123+
.components-rejoining-animation div:nth-child(2) {
124+
animation-delay: -0.5s;
125+
}
126+
127+
@keyframes components-rejoining-animation {
128+
0% {
129+
top: 40px;
130+
left: 40px;
131+
width: 0;
132+
height: 0;
133+
opacity: 0;
134+
}
135+
136+
4.9% {
137+
top: 40px;
138+
left: 40px;
139+
width: 0;
140+
height: 0;
141+
opacity: 0;
142+
}
143+
144+
5% {
145+
top: 40px;
146+
left: 40px;
147+
width: 0;
148+
height: 0;
149+
opacity: 1;
150+
}
151+
152+
100% {
153+
top: 0px;
154+
left: 0px;
155+
width: 80px;
156+
height: 80px;
157+
opacity: 0;
158+
}
159+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Set up event handlers
2+
const reconnectModal = document.getElementById("components-reconnect-modal");
3+
reconnectModal.addEventListener("components-reconnect-state-changed", handleReconnectStateChanged);
4+
5+
const retryButton = document.getElementById("components-reconnect-button");
6+
retryButton.addEventListener("click", retry);
7+
8+
const resumeButton = document.getElementById("components-resume-button");
9+
resumeButton.addEventListener("click", resume);
10+
11+
function handleReconnectStateChanged(event) {
12+
if (event.detail.state === "show") {
13+
reconnectModal.showModal();
14+
} else if (event.detail.state === "hide") {
15+
reconnectModal.close();
16+
} else if (event.detail.state === "failed") {
17+
document.addEventListener("visibilitychange", retryWhenDocumentBecomesVisible);
18+
} else if (event.detail.state === "rejected") {
19+
location.reload();
20+
}
21+
}
22+
23+
async function retry() {
24+
document.removeEventListener("visibilitychange", retryWhenDocumentBecomesVisible);
25+
26+
try {
27+
// Reconnect will asynchronously return:
28+
// - true to mean success
29+
// - false to mean we reached the server, but it rejected the connection (e.g., unknown circuit ID)
30+
// - exception to mean we didn't reach the server (this can be sync or async)
31+
const successful = await Blazor.reconnect();
32+
if (!successful) {
33+
// We have been able to reach the server, but the circuit is no longer available.
34+
// We'll reload the page so the user can continue using the app as quickly as possible.
35+
const resumeSuccessful = await Blazor.resume();
36+
if (!resumeSuccessful) {
37+
location.reload();
38+
} else {
39+
reconnectModal.close();
40+
}
41+
}
42+
} catch (err) {
43+
// We got an exception, server is currently unavailable
44+
document.addEventListener("visibilitychange", retryWhenDocumentBecomesVisible);
45+
}
46+
}
47+
48+
async function resume() {
49+
try {
50+
const successful = await Blazor.resume();
51+
if (!successful) {
52+
location.reload();
53+
}
54+
} catch {
55+
location.reload();
56+
}
57+
}
58+
59+
async function retryWhenDocumentBecomesVisible() {
60+
if (document.visibilityState === "visible") {
61+
await retry();
62+
}
63+
}

src/Components/test/testassets/Components.TestServer/RazorComponents/Root.razor

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<head>
77
<meta charset="utf-8" />
88
<base href="/subdir/" />
9+
<link rel="stylesheet" href="@Assets["Components.TestServer.styles.css"]" />
910
<HeadOutlet />
1011
</head>
1112
<body>
@@ -64,7 +65,15 @@
6465
startButton.textContent = 'Call Blazor.start()';
6566
startButton.onclick = callBlazorStart;
6667
document.body.appendChild(startButton);
67-
}
68+
}
6869
</script>
70+
71+
@if(CustomReconnectUI) {
72+
<ReconnectModal></ReconnectModal>
73+
}
6974
</body>
7075
</html>
76+
77+
@code {
78+
[SupplyParameterFromQuery(Name = "custom-reconnect-ui")] public bool CustomReconnectUI { get; set; }
79+
}

src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Layout/ReconnectModal.razor

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,14 @@
2222
<button id="components-reconnect-button" class="components-reconnect-failed-visible">
2323
Retry
2424
</button>
25+
<p class="components-pause-visible">
26+
The session has been paused by the server.
27+
</p>
28+
<button id="components-resume-button" class="components-pause-visible">
29+
Resume
30+
</button>
31+
<p class="components-resume-failed-visible">
32+
Failed to resume the session.<br />Please reload the page.
33+
</p>
2534
</div>
2635
</dialog>

src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Layout/ReconnectModal.razor.css

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
.components-reconnect-first-attempt-visible,
22
.components-reconnect-repeated-attempt-visible,
33
.components-reconnect-failed-visible,
4+
.components-pause-visible,
5+
.components-resume-failed-visible,
46
.components-rejoining-animation {
57
display: none;
68
}
79

810
#components-reconnect-modal.components-reconnect-show .components-reconnect-first-attempt-visible,
911
#components-reconnect-modal.components-reconnect-show .components-rejoining-animation,
12+
#components-reconnect-modal.components-reconnect-paused .components-pause-visible,
13+
#components-reconnect-modal.components-reconnect-resume-failed .components-resume-failed-visible,
1014
#components-reconnect-modal.components-reconnect-retrying,
1115
#components-reconnect-modal.components-reconnect-retrying .components-reconnect-repeated-attempt-visible,
1216
#components-reconnect-modal.components-reconnect-retrying .components-rejoining-animation,
@@ -101,7 +105,6 @@
101105
}
102106

103107
.components-rejoining-animation {
104-
display: block;
105108
position: relative;
106109
width: 80px;
107110
height: 80px;

0 commit comments

Comments
 (0)