Skip to content

Commit c38d0a5

Browse files
committed
Add tests for register + confirm flow
1 parent a34f435 commit c38d0a5

File tree

8 files changed

+136
-4
lines changed

8 files changed

+136
-4
lines changed

src/Identity/UI/src/Areas/Identity/Pages/V3/Account/RegisterConfirmation.cshtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
{
1111
<p>
1212
This app does not currently have a real email sender registered, see <a href="https://docs.microsoft.com/en-us/aspnet/core/security/authentication/accconfirm">these docs</a> for how to configure a real email sender.
13-
Normally this would be emailed: <a href="@Model.EmailConfirmationUrl">Click here to confirm your account</a>
13+
Normally this would be emailed: <a id="confirm-link" href="@Model.EmailConfirmationUrl">Click here to confirm your account</a>
1414
</p>
1515
}
1616
else

src/Identity/UI/src/Areas/Identity/Pages/V4/Account/RegisterConfirmation.cshtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
{
1111
<p>
1212
This app does not currently have a real email sender registered, see <a href="https://docs.microsoft.com/en-us/aspnet/core/security/authentication/accconfirm">these docs</a> for how to configure a real email sender.
13-
Normally this would be emailed: <a href="@Model.EmailConfirmationUrl">Click here to confirm your account</a>
13+
Normally this would be emailed: <a id="confirm-link" href="@Model.EmailConfirmationUrl">Click here to confirm your account</a>
1414
</p>
1515
}
1616
else

src/Identity/test/Identity.FunctionalTests/Infrastructure/DefaultUIContext.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// 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

44
using System;
@@ -35,6 +35,9 @@ internal DefaultUIContext WithPasswordLogin() =>
3535
internal DefaultUIContext WithCookieConsent() =>
3636
new DefaultUIContext(this) { CookiePolicyAccepted = true };
3737

38+
internal DefaultUIContext WithRealEmailSender() =>
39+
new DefaultUIContext(this) { HasRealEmailSender = true };
40+
3841
public string AuthenticatorKey
3942
{
4043
get => GetValue<string>(nameof(AuthenticatorKey));
@@ -93,5 +96,11 @@ public bool CookiePolicyAccepted
9396
get => GetValue<bool>(nameof(CookiePolicyAccepted));
9497
set => SetValue(nameof(CookiePolicyAccepted), value);
9598
}
99+
100+
public bool HasRealEmailSender
101+
{
102+
get => GetValue<bool>(nameof(HasRealEmailSender));
103+
set => SetValue(nameof(HasRealEmailSender), value);
104+
}
96105
}
97106
}

src/Identity/test/Identity.FunctionalTests/Pages/Account/Register.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,22 @@ public async Task<Index> SubmitRegisterFormForValidUserAsync(string userName, st
5353

5454
return new Index(Client, index, Context.WithAuthenticatedUser());
5555
}
56+
57+
public async Task<RegisterConfirmation> SubmitRegisterFormWithConfirmation(string userName, string password, bool hasRealEmail = false)
58+
{
59+
var registered = await Client.SendAsync(_registerForm, new Dictionary<string, string>()
60+
{
61+
["Input_Email"] = userName,
62+
["Input_Password"] = password,
63+
["Input_ConfirmPassword"] = password
64+
});
65+
66+
var registeredLocation = ResponseAssert.IsRedirect(registered);
67+
Assert.Equal(RegisterConfirmation.Path + "?email="+userName, registeredLocation.ToString());
68+
var registerResponse = await Client.GetAsync(registeredLocation);
69+
var register = await ResponseAssert.IsHtmlDocumentAsync(registerResponse);
70+
71+
return new RegisterConfirmation(Client, register, hasRealEmail ? Context.WithRealEmailSender() : Context);
72+
}
5673
}
5774
}

src/Identity/test/Identity.FunctionalTests/Pages/Index.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// 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

44
using System.Net.Http;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Net.Http;
5+
using System.Threading.Tasks;
6+
using AngleSharp.Dom.Html;
7+
using Microsoft.AspNetCore.Identity.FunctionalTests.Account;
8+
using Xunit;
9+
10+
namespace Microsoft.AspNetCore.Identity.FunctionalTests
11+
{
12+
public class RegisterConfirmation : DefaultUIPage
13+
{
14+
private readonly IHtmlAnchorElement _confirmLink;
15+
public static readonly string Path = "/Identity/Account/RegisterConfirmation";
16+
17+
public RegisterConfirmation(
18+
HttpClient client,
19+
IHtmlDocument register,
20+
DefaultUIContext context)
21+
: base(client, register, context)
22+
{
23+
if (Context.HasRealEmailSender)
24+
{
25+
Assert.Empty(Document.QuerySelectorAll("#confirm-link"));
26+
}
27+
else
28+
{
29+
_confirmLink = HtmlAssert.HasLink("#confirm-link", Document);
30+
}
31+
}
32+
33+
public async Task<ConfirmEmail> ClickConfirmLinkAsync()
34+
{
35+
var goToConfirm = await Client.GetAsync(_confirmLink.Href);
36+
var confirm = await ResponseAssert.IsHtmlDocumentAsync(goToConfirm);
37+
38+
return await ConfirmEmail.Create(_confirmLink, Client, Context);
39+
}
40+
}
41+
}

src/Identity/test/Identity.FunctionalTests/RegistrationTests.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Threading.Tasks;
6+
using Microsoft.AspNetCore.Identity.UI.Services;
67
using Microsoft.EntityFrameworkCore;
78
using Microsoft.Extensions.DependencyInjection;
89
using Xunit;
@@ -37,6 +38,59 @@ public async Task CanRegisterAUser()
3738
await UserStories.RegisterNewUserAsync(client, userName, password);
3839
}
3940

41+
[Fact]
42+
public async Task CanRegisterAUserWithRequiredConfirmation()
43+
{
44+
// Arrange
45+
void ConfigureTestServices(IServiceCollection services) { services.Configure<IdentityOptions>(o => o.SignIn.RequireConfirmedAccount = true); };
46+
47+
var server = ServerFactory
48+
.WithWebHostBuilder(whb => whb.ConfigureServices(ConfigureTestServices));
49+
var client = server.CreateClient();
50+
var client2 = server.CreateClient();
51+
52+
var userName = $"{Guid.NewGuid()}@example.com";
53+
var password = $"!Test.Password1$";
54+
55+
// Act & Assert
56+
var register = await UserStories.RegisterNewUserAsyncWithConfirmation(client, userName, password);
57+
58+
// Since we aren't confirmed yet, login should fail until we confirm
59+
await UserStories.LoginFailsWithWrongPasswordAsync(client, userName, password);
60+
await register.ClickConfirmLinkAsync();
61+
await UserStories.LoginExistingUserAsync(client, userName, password);
62+
}
63+
64+
private class FakeEmailSender : IEmailSender
65+
{
66+
public Task SendEmailAsync(string email, string subject, string htmlMessage)
67+
=> Task.CompletedTask;
68+
}
69+
70+
[Fact]
71+
public async Task RegisterWithRealConfirmationDoesNotShowLink()
72+
{
73+
// Arrange
74+
void ConfigureTestServices(IServiceCollection services) {
75+
services.Configure<IdentityOptions>(o => o.SignIn.RequireConfirmedAccount = true);
76+
services.AddSingleton<IEmailSender, FakeEmailSender>();
77+
};
78+
79+
var server = ServerFactory
80+
.WithWebHostBuilder(whb => whb.ConfigureServices(ConfigureTestServices));
81+
var client = server.CreateClient();
82+
var client2 = server.CreateClient();
83+
84+
var userName = $"{Guid.NewGuid()}@example.com";
85+
var password = $"!Test.Password1$";
86+
87+
// Act & Assert
88+
var register = await UserStories.RegisterNewUserAsyncWithConfirmation(client, userName, password, hasRealEmailSender: true);
89+
90+
// Since we aren't confirmed yet, login should fail until we confirm
91+
await UserStories.LoginFailsWithWrongPasswordAsync(client, userName, password);
92+
}
93+
4094
[Fact]
4195
public async Task CanRegisterAUser_WithGlobalAuthorizeFilter()
4296
{

src/Identity/test/Identity.FunctionalTests/UserStories.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ internal static async Task<Index> RegisterNewUserAsync(HttpClient client, string
2727
return await register.SubmitRegisterFormForValidUserAsync(userName, password);
2828
}
2929

30+
internal static async Task<RegisterConfirmation> RegisterNewUserAsyncWithConfirmation(HttpClient client, string userName = null, string password = null, bool hasRealEmailSender = false)
31+
{
32+
userName = userName ?? $"{Guid.NewGuid()}@example.com";
33+
password = password ?? $"!Test.Password1$";
34+
35+
var index = await Index.CreateAsync(client);
36+
var register = await index.ClickRegisterLinkAsync();
37+
38+
return await register.SubmitRegisterFormWithConfirmation(userName, password, hasRealEmailSender);
39+
}
40+
3041

3142
internal static async Task<Index> LoginExistingUserAsync(HttpClient client, string userName, string password)
3243
{

0 commit comments

Comments
 (0)