Skip to content

Commit fd068f2

Browse files
authored
Create Login page EditContext before async work (#7671)
1 parent 9e4b222 commit fd068f2

File tree

2 files changed

+82
-6
lines changed

2 files changed

+82
-6
lines changed

src/Aspire.Dashboard/Components/Pages/Login.razor.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,15 @@ public partial class Login : IAsyncDisposable
3838

3939
protected override async Task OnInitializedAsync()
4040
{
41+
// Create EditContext before awaiting. This is required to prevent an await in OnInitializedAsync
42+
// triggering parameters being set on EditForm before EditContext is created.
43+
// If that happens then EditForm errors that it requires an EditContext.
44+
_formModel = new TokenFormModel();
45+
EditContext = new EditContext(_formModel);
46+
_messageStore = new(EditContext);
47+
EditContext.OnValidationRequested += (s, e) => _messageStore.Clear();
48+
EditContext.OnFieldChanged += (s, e) => _messageStore.Clear(e.FieldIdentifier);
49+
4150
// If the browser is already authenticated then redirect to the app.
4251
if (AuthenticationState is { } authStateTask)
4352
{
@@ -48,12 +57,6 @@ protected override async Task OnInitializedAsync()
4857
return;
4958
}
5059
}
51-
52-
_formModel = new TokenFormModel();
53-
EditContext = new EditContext(_formModel);
54-
_messageStore = new(EditContext);
55-
EditContext.OnValidationRequested += (s, e) => _messageStore.Clear();
56-
EditContext.OnFieldChanged += (s, e) => _messageStore.Clear(e.FieldIdentifier);
5760
}
5861

5962
protected override async Task OnAfterRenderAsync(bool firstRender)
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Aspire.Dashboard.Components.Tests.Shared;
5+
using Aspire.Dashboard.Model;
6+
using Bunit;
7+
using Microsoft.AspNetCore.Components.Authorization;
8+
using Microsoft.Extensions.DependencyInjection;
9+
using Microsoft.Extensions.Localization;
10+
using Microsoft.Extensions.Logging;
11+
using Microsoft.FluentUI.AspNetCore.Components;
12+
using Xunit;
13+
using Xunit.Abstractions;
14+
15+
namespace Aspire.Dashboard.Components.Tests.Pages;
16+
17+
[UseCulture("en-US")]
18+
public partial class LoginTests : TestContext
19+
{
20+
private readonly ITestOutputHelper _testOutputHelper;
21+
22+
public LoginTests(ITestOutputHelper testOutputHelper)
23+
{
24+
_testOutputHelper = testOutputHelper;
25+
}
26+
27+
[Fact]
28+
public void Initialize_LongRunningAuthStateFunc_EditContextSet()
29+
{
30+
// Arrange
31+
SetupLoginServices();
32+
33+
// This represents a long running auth state task.
34+
var tcs = new TaskCompletionSource<AuthenticationState>(TaskCreationOptions.RunContinuationsAsynchronously);
35+
36+
// Act
37+
var cut = RenderComponent<Components.Pages.Login>(builder =>
38+
{
39+
builder.Add(p => p.AuthenticationState, tcs.Task);
40+
});
41+
42+
var instance = cut.Instance;
43+
var logger = Services.GetRequiredService<ILogger<ConsoleLogsTests>>();
44+
var loc = Services.GetRequiredService<IStringLocalizer<Resources.ConsoleLogs>>();
45+
46+
cut.WaitForState(() => instance.EditContext != null);
47+
}
48+
49+
private void SetupLoginServices()
50+
{
51+
var version = typeof(FluentMain).Assembly.GetName().Version!;
52+
53+
JSInterop.SetupModule("/Components/Pages/Login.razor.js");
54+
55+
JSInterop.SetupModule(GetFluentFile("./_content/Microsoft.FluentUI.AspNetCore.Components/Components/Anchor/FluentAnchor.razor.js", version));
56+
57+
var textboxModule = JSInterop.SetupModule(GetFluentFile("./_content/Microsoft.FluentUI.AspNetCore.Components/Components/TextField/FluentTextField.razor.js", version));
58+
textboxModule.SetupVoid("setControlAttribute", _ => true);
59+
60+
var loggerFactory = IntegrationTestHelpers.CreateLoggerFactory(_testOutputHelper);
61+
62+
Services.AddLocalization();
63+
Services.AddSingleton<ILoggerFactory>(loggerFactory);
64+
Services.AddSingleton<IDashboardClient>(new TestDashboardClient());
65+
Services.AddSingleton<LibraryConfiguration>();
66+
Services.AddSingleton<IKeyCodeService, KeyCodeService>();
67+
}
68+
69+
private static string GetFluentFile(string filePath, Version version)
70+
{
71+
return $"{filePath}?v={version}";
72+
}
73+
}

0 commit comments

Comments
 (0)