-
Notifications
You must be signed in to change notification settings - Fork 143
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
IOS Sign Up E2E Test #2422
IOS Sign Up E2E Test #2422
Changes from 24 commits
2565b80
3247d2a
fd3a63a
a469024
9974b64
578c479
24e6838
2bf3f9e
e90dadc
94b550b
db431c4
9cd2c77
b197a17
95bfcaf
b7e826d
dc67b95
91e59f0
c2ff7f7
273af9b
e163297
4ab385a
08ba0c8
61569bb
faa59c5
886d1fe
6a82162
f4b68df
d1ce495
cc3864d
14531e1
4463c60
9b56bc2
3812849
cefac96
99d3b11
7fe3bc8
1422d14
d239b4c
4f65bff
1c93d83
96c3908
08ef259
6704e4c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -37,6 +37,11 @@ class MSALNativeAuthEndToEndBaseTestCase: XCTestCase { | |||||
static let signInEmailPasswordMFAUsernameKey = "sign_in_email_password_mfa_username" | ||||||
static let signInEmailPasswordMFANoDefaultAuthMethodUsernameKey = "sign_in_email_password_mfa_no_default_username" | ||||||
static let signInEmailCodeUsernameKey = "sign_in_email_code_username" | ||||||
static let customDomainFormat = [ | ||||||
"https://<tenantName>.ciamlogin.com/<tenantName>.onmicrosoft.com", | ||||||
"https://<tenantName>.ciamlogin.com/<tenantId>", | ||||||
"https://<tenantName>.ciamlogin.com/", | ||||||
] | ||||||
#if !os(macOS) | ||||||
static let resetPasswordUsernameKey = "reset_password_username" | ||||||
#else | ||||||
|
@@ -71,14 +76,46 @@ class MSALNativeAuthEndToEndBaseTestCase: XCTestCase { | |||||
|
||||||
func initialisePublicClientApplication( | ||||||
clientIdType: ClientIdType = .password, | ||||||
challengeTypes: MSALNativeAuthChallengeTypes = [.OOB, .password] | ||||||
challengeTypes: MSALNativeAuthChallengeTypes = [.OOB, .password], | ||||||
customSubdomainFormat: Int? = nil | ||||||
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. Using an Int in this case create the following issues:
Did you take in consideration using an enum instead?
So, the firm of the method can be changed to: 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. Good idea. Done. |
||||||
) -> MSALNativeAuthPublicClientApplication? { | ||||||
let clientIdKey = getClientIdKey(type: clientIdType) | ||||||
guard let clientId = MSALNativeAuthEndToEndBaseTestCase.nativeAuthConfFileContent?[clientIdKey] as? String, let tenantSubdomain = MSALNativeAuthEndToEndBaseTestCase.nativeAuthConfFileContent?[Constants.tenantSubdomainKey] as? String else { | ||||||
XCTFail("ClientId or tenantSubdomain not found in conf.json") | ||||||
guard let clientId = MSALNativeAuthEndToEndBaseTestCase.nativeAuthConfFileContent?[clientIdKey] as? String else { | ||||||
XCTFail("ClientId not found in conf.json") | ||||||
return nil | ||||||
} | ||||||
return try? MSALNativeAuthPublicClientApplication(clientId: clientId, tenantSubdomain: tenantSubdomain, challengeTypes: challengeTypes) | ||||||
|
||||||
guard let tenantSubdomain = MSALNativeAuthEndToEndBaseTestCase.nativeAuthConfFileContent?[Constants.tenantSubdomainKey] as? String else { | ||||||
XCTFail("TenantName not found in conf.json") | ||||||
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. Can you call it "tenant subdomain"? 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. Done. |
||||||
return nil | ||||||
} | ||||||
|
||||||
|
||||||
if customSubdomainFormat != nil { | ||||||
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. if you use
Suggested change
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. Done. |
||||||
let customSubdomain = getCustomTenantSubdomain( | ||||||
tenantName: tenantSubdomain, | ||||||
tenantId: "", // TODO: Upload the tenant id into the config | ||||||
format: customSubdomainFormat! | ||||||
) | ||||||
|
||||||
let authority = try? MSALCIAMAuthority( | ||||||
url: URL(string: customSubdomain)!, | ||||||
validateFormat: false | ||||||
) | ||||||
|
||||||
let configuration = MSALPublicClientApplicationConfig( | ||||||
clientId: clientId, | ||||||
redirectUri: nil, | ||||||
authority: authority | ||||||
) | ||||||
|
||||||
return try? MSALNativeAuthPublicClientApplication( | ||||||
configuration: configuration, | ||||||
challengeTypes: challengeTypes | ||||||
) | ||||||
} else { | ||||||
return try? MSALNativeAuthPublicClientApplication(clientId: clientId, tenantSubdomain: tenantSubdomain, challengeTypes: challengeTypes) | ||||||
} | ||||||
} | ||||||
|
||||||
func generateSignUpRandomEmail() -> String { | ||||||
|
@@ -129,4 +166,10 @@ class MSALNativeAuthEndToEndBaseTestCase: XCTestCase { | |||||
return Constants.clientIdEmailCodeAttributesKey | ||||||
} | ||||||
} | ||||||
|
||||||
private func getCustomTenantSubdomain(tenantName: String?, tenantId: String?, format: Int) -> String { | ||||||
return Constants.customDomainFormat[format] | ||||||
.replacingOccurrences(of: "<tenantName>", with: tenantName!) | ||||||
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. Misspelling the string to replace can led to unexpected result. Did you consider using String template instead? 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. Good idea. Done. |
||||||
.replacingOccurrences(of: "<tenantId>", with: tenantId!) | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -84,6 +84,58 @@ final class MSALNativeAuthSignUpUsernameAndPasswordEndToEndTests: MSALNativeAuth | |||||
await fulfillment(of: [signInExp]) | ||||||
checkSignInAfterSignUpDelegate(signInAfterSignUpDelegate, expectedUsername: username) | ||||||
} | ||||||
|
||||||
// Use case 1.1.2. Sign up - with Email & Password, Resend email OOB | ||||||
func test_signUpWithEmailPassword_resendEmail_success() async throws { | ||||||
guard let sut = initialisePublicClientApplication() else { | ||||||
XCTFail("Missing information") | ||||||
return | ||||||
} | ||||||
|
||||||
let username = generateSignUpRandomEmail() | ||||||
let password = generateRandomPassword() | ||||||
|
||||||
let codeRequiredExp = expectation(description: "code required") | ||||||
let signUpStartDelegate = SignUpPasswordStartDelegateSpy(expectation: codeRequiredExp) | ||||||
|
||||||
sut.signUp( | ||||||
username: username, | ||||||
password: password, | ||||||
correlationId: correlationId, | ||||||
delegate: signUpStartDelegate | ||||||
) | ||||||
|
||||||
await fulfillment(of: [codeRequiredExp]) | ||||||
checkSignUpStartDelegate(signUpStartDelegate) | ||||||
|
||||||
// Now get code1... | ||||||
guard let code1 = await retrieveCodeFor(email: username) else { | ||||||
XCTFail("OTP code could not be retrieved") | ||||||
return | ||||||
} | ||||||
|
||||||
// Resend code | ||||||
let resendCodeRequiredExp = expectation(description: "code required again") | ||||||
let signUpResendCodeDelegate = SignUpResendCodeDelegateSpy(expectation: resendCodeRequiredExp) | ||||||
|
||||||
// Call resend code method | ||||||
signUpStartDelegate.newState?.resendCode(delegate: signUpResendCodeDelegate) | ||||||
|
||||||
await fulfillment(of: [resendCodeRequiredExp]) | ||||||
|
||||||
// Verify that resend code method was called | ||||||
XCTAssertTrue(signUpResendCodeDelegate.onSignUpResendCodeCodeRequiredCalled, | ||||||
"Resend code method should have been called") | ||||||
|
||||||
// Now get code2... | ||||||
guard let code2 = await retrieveCodeFor(email: username) else { | ||||||
XCTFail("OTP code could not be retrieved") | ||||||
return | ||||||
} | ||||||
|
||||||
// Verify that the codes are different | ||||||
XCTAssertNotEqual(code1, code2, "Resent code should be different from the original code") | ||||||
} | ||||||
|
||||||
// Hero Scenario 1.1.3. Sign up - with Email verification as LAST step & Custom Attributes (Email & Password) | ||||||
func test_signUpWithPassword_withEmailVerificationAsLastStepAndCustomAttributes_succeeds() async throws { | ||||||
|
@@ -218,6 +270,72 @@ final class MSALNativeAuthSignUpUsernameAndPasswordEndToEndTests: MSALNativeAuth | |||||
await fulfillment(of: [signInExp]) | ||||||
checkSignInAfterSignUpDelegate(signInAfterSignUpDelegate, expectedUsername: username) | ||||||
} | ||||||
|
||||||
// Use case 1.1.5. Sign up - with Email & Password, Verify email address using email OTP, resend OTP and then set password | ||||||
func test_signUpWithEmailOTP_andSetPasswordAfterOTP_success() async throws { | ||||||
guard let sut = initialisePublicClientApplication() else { | ||||||
XCTFail("Missing information") | ||||||
return | ||||||
} | ||||||
|
||||||
let username = generateSignUpRandomEmail() | ||||||
let password = generateRandomPassword() | ||||||
|
||||||
let codeRequiredExp = expectation(description: "code required") | ||||||
let signUpStartDelegate = SignUpPasswordStartDelegateSpy(expectation: codeRequiredExp) | ||||||
|
||||||
sut.signUp( | ||||||
username: username, | ||||||
password: password, | ||||||
correlationId: correlationId, | ||||||
delegate: signUpStartDelegate | ||||||
) | ||||||
|
||||||
await fulfillment(of: [codeRequiredExp]) | ||||||
checkSignUpStartDelegate(signUpStartDelegate) | ||||||
|
||||||
guard signUpStartDelegate.onSignUpCodeRequiredCalled else { | ||||||
XCTFail("onSignUpCodeRequired not called") | ||||||
return | ||||||
} | ||||||
|
||||||
// First attempt to get code | ||||||
guard let initialCode = await retrieveCodeFor(email: username) else { | ||||||
XCTFail("Initial OTP code could not be retrieved") | ||||||
return | ||||||
} | ||||||
|
||||||
// Resend code expectation | ||||||
let resendCodeRequiredExp = expectation(description: "code resend required") | ||||||
let signUpResendCodeDelegate = SignUpResendCodeDelegateSpy(expectation: resendCodeRequiredExp) | ||||||
|
||||||
// Call resend code method | ||||||
signUpStartDelegate.newState?.resendCode(delegate: signUpResendCodeDelegate) | ||||||
|
||||||
await fulfillment(of: [resendCodeRequiredExp]) | ||||||
|
||||||
// Verify resend code was triggered | ||||||
XCTAssertTrue(signUpResendCodeDelegate.onSignUpResendCodeCodeRequiredCalled, | ||||||
"Resend code method should have been called") | ||||||
|
||||||
// Get new code after resend | ||||||
guard let newCode = await retrieveCodeFor(email: username) else { | ||||||
XCTFail("Resent OTP code could not be retrieved") | ||||||
return | ||||||
} | ||||||
|
||||||
// Verify that the new code is different from the initial code | ||||||
XCTAssertNotEqual(initialCode, newCode, "Resent code should be different from the initial code") | ||||||
|
||||||
// Complete sign up with the new code | ||||||
let signUpCompleteExp = expectation(description: "sign-up complete") | ||||||
let signUpVerifyCodeDelegate = SignUpVerifyCodeDelegateSpy(expectation: signUpCompleteExp) | ||||||
|
||||||
signUpStartDelegate.newState?.submitCode(code: newCode, delegate: signUpVerifyCodeDelegate) | ||||||
|
||||||
await fulfillment(of: [signUpCompleteExp]) | ||||||
XCTAssertTrue(signUpVerifyCodeDelegate.onSignUpCompletedCalled, "Sign-up should be completed successfully") | ||||||
} | ||||||
|
||||||
// Hero Scenario 1.1.6. Sign up - with Email verification as FIRST step & Custom Attribute (Email & Password) | ||||||
func test_signUpWithPasswordWithEmailVerificationAsFirstStepAndCustomAttributes_succeeds() async throws { | ||||||
|
@@ -464,7 +582,116 @@ final class MSALNativeAuthSignUpUsernameAndPasswordEndToEndTests: MSALNativeAuth | |||||
await fulfillment(of: [signUpCompleteExp]) | ||||||
XCTAssertTrue(signUpVerifyCodeDelegate.onSignUpCompletedCalled) | ||||||
} | ||||||
|
||||||
|
||||||
// Use case 1.1.10. Sign up - with Email & Password, User already exists with given email as email-pw account | ||||||
func test_signUpWithEmailPassword_andAgainSameEmail_fails() async throws { | ||||||
guard let sut = initialisePublicClientApplication(clientIdType: .password), let username = retrieveUsernameForSignInUsernameAndPassword() else { | ||||||
XCTFail("Missing information") | ||||||
return | ||||||
} | ||||||
|
||||||
let password = generateRandomPassword() | ||||||
|
||||||
let signUpFailureExp = expectation(description: "sign-up with existing email fails") | ||||||
let signUpStartDelegate = SignUpPasswordStartDelegateSpy(expectation: signUpFailureExp) | ||||||
|
||||||
sut.signUp( | ||||||
username: username, | ||||||
password: password, | ||||||
correlationId: correlationId, | ||||||
delegate: signUpStartDelegate | ||||||
) | ||||||
|
||||||
await fulfillment(of: [signUpFailureExp]) | ||||||
|
||||||
// Verify error condition | ||||||
XCTAssertTrue(signUpStartDelegate.onSignUpPasswordErrorCalled) | ||||||
XCTAssertEqual(signUpStartDelegate.error?.isUserAlreadyExists, true) | ||||||
} | ||||||
|
||||||
// Use case 1.1.11. Sign up - with Email & Password, User already exists with given email as social account | ||||||
func test_signUpWithEmailPassword_socialAccount_fails() async throws { | ||||||
throw XCTSkip("Skipping test as it requires a Social account, not present in MSIDLAB") | ||||||
|
||||||
guard let sut = initialisePublicClientApplication() else { | ||||||
XCTFail("Missing information") | ||||||
return | ||||||
} | ||||||
|
||||||
let username = "social_account" | ||||||
let password = generateRandomPassword() | ||||||
|
||||||
let signUpFailureExp = expectation(description: "sign-up with social account email fails") | ||||||
let signUpStartDelegate = SignUpPasswordStartDelegateSpy(expectation: signUpFailureExp) | ||||||
|
||||||
sut.signUp( | ||||||
username: username, | ||||||
password: password, | ||||||
correlationId: correlationId, | ||||||
delegate: signUpStartDelegate | ||||||
) | ||||||
|
||||||
await fulfillment(of: [signUpFailureExp]) | ||||||
|
||||||
// Verify error condition | ||||||
XCTAssertTrue(signUpStartDelegate.onSignUpPasswordErrorCalled) | ||||||
XCTAssertEqual(signUpStartDelegate.error!.isInvalidUsername, true) | ||||||
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. Try to never use
Suggested change
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. Good point. Done and modify and check all the sign up cases. |
||||||
} | ||||||
|
||||||
// Use case 1.1.12. Sign up - with Email & Password, Developer makes a request with invalid format email address | ||||||
func test_signUpWithEmailPassword_invalidEmail_fails() async throws { | ||||||
guard let sut = initialisePublicClientApplication(clientIdType: .password) else { | ||||||
XCTFail("Missing information") | ||||||
return | ||||||
} | ||||||
|
||||||
let username = "invalid" | ||||||
let password = generateRandomPassword() | ||||||
|
||||||
let signUpFailureExp = expectation(description: "sign-up with invalid format email fails") | ||||||
let signUpStartDelegate = SignUpPasswordStartDelegateSpy(expectation: signUpFailureExp) | ||||||
|
||||||
sut.signUp( | ||||||
username: username, | ||||||
password: password, | ||||||
correlationId: correlationId, | ||||||
delegate: signUpStartDelegate | ||||||
) | ||||||
|
||||||
await fulfillment(of: [signUpFailureExp]) | ||||||
|
||||||
// Verify error condition | ||||||
XCTAssertTrue(signUpStartDelegate.onSignUpPasswordErrorCalled) | ||||||
XCTAssertEqual(signUpStartDelegate.error!.isInvalidUsername, true) | ||||||
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.
Suggested change
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. Done |
||||||
} | ||||||
|
||||||
// Use case 1.1.13. Sign up - with Email & Password, Developer makes a request with password that does not match password complexity requirements set on portal | ||||||
func test_signUpWithEmailPassword_invalidPassword_fails() async throws { | ||||||
guard let sut = initialisePublicClientApplication(clientIdType: .password) else { | ||||||
XCTFail("Missing information") | ||||||
return | ||||||
} | ||||||
|
||||||
let username = generateSignUpRandomEmail() | ||||||
let password = "invalid" | ||||||
|
||||||
let signUpFailureExp = expectation(description: "sign-up with invalid password complexity fails") | ||||||
let signUpStartDelegate = SignUpPasswordStartDelegateSpy(expectation: signUpFailureExp) | ||||||
|
||||||
sut.signUp( | ||||||
username: username, | ||||||
password: password, | ||||||
correlationId: correlationId, | ||||||
delegate: signUpStartDelegate | ||||||
) | ||||||
|
||||||
await fulfillment(of: [signUpFailureExp]) | ||||||
|
||||||
// Verify error condition | ||||||
XCTAssertTrue(signUpStartDelegate.onSignUpPasswordErrorCalled) | ||||||
XCTAssertEqual(signUpStartDelegate.error!.isInvalidPassword, true) | ||||||
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.
Suggested change
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. Done. |
||||||
} | ||||||
|
||||||
private func checkSignUpStartDelegate(_ delegate: SignUpPasswordStartDelegateSpy) { | ||||||
XCTAssertTrue(delegate.onSignUpCodeRequiredCalled) | ||||||
XCTAssertEqual(delegate.channelTargetType?.isEmailType, true) | ||||||
|
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.
This pull request does not update CHANGELOG.md.
Please consider if this change would be noticeable to a partner or user and either update CHANGELOG.md or resolve this conversation.