diff options
author | Paul Beusterien <paulbeusterien@google.com> | 2018-03-07 07:47:09 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-07 07:47:09 -0800 |
commit | 8ef0f1490a72fd700f609dc9971ec16868d6747b (patch) | |
tree | 61cdcbb7099535abd791dc523d8e2d19268faf4f /Example | |
parent | 0b8f216e4ce85b968ac0803d729cd24eb110d0b6 (diff) |
Adds Email link sign-in (#882)
Diffstat (limited to 'Example')
-rw-r--r-- | Example/Auth/Sample/MainViewController.m | 127 | ||||
-rw-r--r-- | Example/Auth/Tests/FIRAuthTests.m | 286 | ||||
-rw-r--r-- | Example/Auth/Tests/FIREmailLinkRequestTests.m | 137 | ||||
-rw-r--r-- | Example/Auth/Tests/FIREmailLinkSignInResponseTests.m | 195 | ||||
-rw-r--r-- | Example/Auth/Tests/FIRGetOOBConfirmationCodeRequestTests.m | 43 | ||||
-rw-r--r-- | Example/Firebase.xcodeproj/project.pbxproj | 10 |
6 files changed, 793 insertions, 5 deletions
diff --git a/Example/Auth/Sample/MainViewController.m b/Example/Auth/Sample/MainViewController.m index 31c103a..36ef92d 100644 --- a/Example/Auth/Sample/MainViewController.m +++ b/Example/Auth/Sample/MainViewController.m @@ -91,6 +91,16 @@ static NSString *const kSetPhotoURLText = @"Set Photo url"; */ static NSString *const kSignInGoogleButtonText = @"Sign in with Google"; +/** @var kSignInWithEmailLink + @brief The text of the "Sign in with Email Link" button. + */ +static NSString *const kSignInWithEmailLink = @"Sign in with Email Link"; + +/** @var kSendEmailSignInLink + @brief The text of the "Send Email SignIn link" button +*/ +static NSString *const kSendEmailSignInLink = @"Send Email Sign in Link"; + /** @var kSignInAndRetrieveGoogleButtonText @brief The text of the "Sign in with Google and retrieve data" button. */ @@ -279,6 +289,11 @@ static NSString *const kUnlinkFromEmailPassword = @"Unlink from Email/Password"; */ static NSString *const kGetProvidersForEmail = @"Get Provider IDs for Email"; +/** @var kGetAllSignInMethodsForEmail + @brief The text of the "Get sign-in methods for Email" button. + */ +static NSString *const kGetAllSignInMethodsForEmail = @"Get Sign-in methods for Email"; + /** @var kActionCodeTypeDescription @brief The description of the "Action Type" entry. */ @@ -722,6 +737,10 @@ typedef enum { action:^{ [weakSelf createUserAuthDataResult]; }], [StaticContentTableViewCell cellWithTitle:kSignInGoogleButtonText action:^{ [weakSelf signInGoogle]; }], + [StaticContentTableViewCell cellWithTitle:kSignInWithEmailLink + action:^{ [weakSelf signInWithEmailLink]; }], + [StaticContentTableViewCell cellWithTitle:kSendEmailSignInLink + action:^{ [weakSelf sendEmailSignInLink]; }], [StaticContentTableViewCell cellWithTitle:kSignInGoogleAndRetrieveDataButtonText action:^{ [weakSelf signInGoogleAndRetrieveData]; }], [StaticContentTableViewCell cellWithTitle:kSignInFacebookButtonText @@ -754,6 +773,8 @@ typedef enum { action:^{ [weakSelf reloadUser]; }], [StaticContentTableViewCell cellWithTitle:kGetProvidersForEmail action:^{ [weakSelf getProvidersForEmail]; }], + [StaticContentTableViewCell cellWithTitle:kGetAllSignInMethodsForEmail + action:^{ [weakSelf getAllSignInMethodsForEmail]; }], [StaticContentTableViewCell cellWithTitle:kUpdateEmailText action:^{ [weakSelf updateEmail]; }], [StaticContentTableViewCell cellWithTitle:kUpdatePasswordText @@ -1657,7 +1678,7 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) { } /** @fn signInEmailPassword - @brief Invoked when "sign in with Email/Password" row is pressed. + @brief Invoked when "Sign in with Email/Password" row is pressed. */ - (void)signInEmailPassword { [self showTextInputPromptWithMessage:@"Email Address:" @@ -1724,6 +1745,75 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) { }]; } +/** @fn signInWithEmailLink + @brief Invoked when "Sign in with email link" row is pressed. + */ +- (void)signInWithEmailLink { + [self showTextInputPromptWithMessage:@"Email Address:" + keyboardType:UIKeyboardTypeEmailAddress + completionBlock:^(BOOL userPressedOK, NSString *_Nullable email) { + if (!userPressedOK || !email.length) { + return; + } + [self showTextInputPromptWithMessage:@"Email Sign-In Link:" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable link) { + if (!userPressedOK) { + return; + } + if ([[FIRAuth auth] isSignInWithEmailLink:link]) { + [self showSpinner:^{ + [[AppManager auth] signInWithEmail:email + link:link + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + [self hideSpinner:^{ + if (error) { + [self logFailure:@"sign-in with Email/Sign-In failed" error:error]; + } else { + [self logSuccess:@"sign-in with Email/Sign-In link succeeded."]; + [self log:[NSString stringWithFormat:@"UID: %@",authResult.user.uid]]; + } + [self showTypicalUIForUserUpdateResultsWithTitle:@"Sign-In Error" error:error]; + }]; + }]; + }]; + } else { + [self log:@"The sign-in link is invalid"]; + } + }]; + }]; +} + +/** @fn sendEmailSignInLink + @brief Invoked when "Send email sign-in link" row is pressed. + */ +- (void)sendEmailSignInLink { + [self showTextInputPromptWithMessage:@"Email:" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) { + if (!userPressedOK) { + return; + } + [self showSpinner:^{ + void (^requestEmailSignInLink)(void (^)(NSError *)) = ^(void (^completion)(NSError *)) { + [[AppManager auth] sendSignInLinkToEmail:userInput + actionCodeSettings:[self actionCodeSettings] + completion:completion]; + }; + requestEmailSignInLink(^(NSError *_Nullable error) { + [self hideSpinner:^{ + if (error) { + [self logFailure:@"Email Link request failed" error:error]; + [self showMessagePrompt:error.localizedDescription]; + return; + } + [self logSuccess:@"Email Link request succeeded."]; + [self showMessagePrompt:@"Sent"]; + }]; + }); + }]; + }]; +} + /** @fn signUpNewEmail @brief Invoked if sign-in is attempted with new email/password. @remarks Should only be called if @c FIRAuthErrorCodeInvalidEmail is encountered on attepmt to @@ -2245,6 +2335,39 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) { }]; } +/** @fn getAllSignInMethodsForEmail + @brief Prompts user for an email address, calls @c FIRAuth.getAllSignInMethodsForEmail:callback: + and displays the result. + */ +- (void)getAllSignInMethodsForEmail { + [self showTextInputPromptWithMessage:@"Email:" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) { + if (!userPressedOK || !userInput.length) { + return; + } + + [self showSpinner:^{ + [[AppManager auth] fetchSignInMethodsForEmail:userInput + completion:^(NSArray<NSString *> *_Nullable signInMethods, + NSError *_Nullable error) { + if (error) { + [self logFailure:@"get sign-in methods for email failed" error:error]; + } else { + [self logSuccess:@"get sign-in methods for email succeeded."]; + } + [self hideSpinner:^{ + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + [self showMessagePrompt:[signInMethods componentsJoinedByString:@", "]]; + }]; + }]; + }]; + }]; +} + + /** @fn actionCodeRequestTypeString @brief Returns a string description for the type of the next action code request. */ @@ -2486,6 +2609,8 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) { return @"Recover Email"; case FIRActionCodeOperationPasswordReset: return @"Password Reset"; + case FIRActionCodeOperationEmailLink: + return @"Email Sign-In Link"; case FIRActionCodeOperationUnknown: return @"Unknown action"; } diff --git a/Example/Auth/Tests/FIRAuthTests.m b/Example/Auth/Tests/FIRAuthTests.m index b22c600..914c58b 100644 --- a/Example/Auth/Tests/FIRAuthTests.m +++ b/Example/Auth/Tests/FIRAuthTests.m @@ -33,6 +33,8 @@ #import "FIRAuthBackend.h" #import "FIRCreateAuthURIRequest.h" #import "FIRCreateAuthURIResponse.h" +#import "FIREmailLinkSignInRequest.h" +#import "FIREmailLinkSignInResponse.h" #import "FIRGetAccountInfoRequest.h" #import "FIRGetAccountInfoResponse.h" #import "FIRGetOOBConfirmationCodeRequest.h" @@ -56,6 +58,7 @@ #import "FIRApp+FIRAuthUnitTests.h" #import "OCMStubRecorder+FIRAuthUnitTests.h" #import <OCMock/OCMock.h> +#import "FIRActionCodeSettings.h" #if TARGET_OS_IOS #import "FIRPhoneAuthCredential.h" @@ -167,6 +170,38 @@ static NSString *const kVerificationCode = @"12345678"; */ static NSString *const kVerificationID = @"55432"; +/** @var kContinueURL + @brief Fake string value of continue url. + */ +static NSString *const kContinueURL = @"continueURL"; + +/** @var kCanHandleCodeInAppKey + @brief The key for the request parameter indicating whether the action code can be handled in + the app or not. + */ +static NSString *const kCanHandleCodeInAppKey = @"canHandleCodeInApp"; + +/** @var kFIREmailLinkAuthSignInMethod + @brief Fake email link sign-in method for testing. + */ +static NSString *const kFIREmailLinkAuthSignInMethod = @"emailLink"; + +/** @var kFIRFacebookAuthSignInMethod + @brief Fake Facebook sign-in method for testing. + */ +static NSString *const kFIRFacebookAuthSignInMethod = @"facebook.com"; + +/** @var kBadSignInEmailLink + @brief Bad sign-in link to test email link sign-in + */ +static NSString *const kBadSignInEmailLink = @"http://www.facebook.com"; + +/** @var kFakeEmailSignInlink + @brief Fake email sign-in link + */ +static NSString *const kFakeEmailSignInlink = @"https://fex9s.app.goo.gl?link=" + "https://fb-sa-upgraded.firebaseapp.com/_?mode%3DsignIn%26oobCode%3Dtestoobcode"; + /** @var kExpectationTimeout @brief The maximum time waiting for expectations to fulfill. */ @@ -360,6 +395,39 @@ static const NSTimeInterval kWaitInterval = .5; OCMVerifyAll(_mockBackend); } +/** @fn testFetchSignInMethodsForEmailSuccess + @brief Tests the flow of a successful @c fetchSignInMethodsForEmail:completion: call. + */ +- (void)testFetchSignInMethodsForEmailSuccess { + NSArray<NSString *> *allSignInMethods = + @[ kFIREmailLinkAuthSignInMethod, kFIRFacebookAuthSignInMethod ]; + OCMExpect([_mockBackend createAuthURI:[OCMArg any] + callback:[OCMArg any]]) + .andCallBlock2(^(FIRCreateAuthURIRequest *_Nullable request, + FIRCreateAuthURIResponseCallback callback) { + XCTAssertEqualObjects(request.identifier, kEmail); + XCTAssertNotNil(request.endpoint); + XCTAssertEqualObjects(request.APIKey, kAPIKey); + dispatch_async(FIRAuthGlobalWorkQueue(), ^() { + id mockCreateAuthURIResponse = OCMClassMock([FIRCreateAuthURIResponse class]); + OCMStub([mockCreateAuthURIResponse signinMethods]).andReturn(allSignInMethods); + callback(mockCreateAuthURIResponse, nil); + }); + }); + XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; + [[FIRAuth auth] fetchSignInMethodsForEmail:kEmail + completion:^(NSArray<NSString *> *_Nullable signInMethods, + NSError *_Nullable error) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertEqualObjects(signInMethods, allSignInMethods); + XCTAssertTrue([allSignInMethods isKindOfClass:[NSArray class]]); + XCTAssertNil(error); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; + OCMVerifyAll(_mockBackend); +} + /** @fn testFetchProvidersForEmailSuccessDeprecatedProviderID @brief Tests the flow of a successful @c fetchProvidersForEmail:completion: call using the deprecated FIREmailPasswordAuthProviderID. @@ -416,6 +484,25 @@ static const NSTimeInterval kWaitInterval = .5; OCMVerifyAll(_mockBackend); } +/** @fn testFetchSignInMethodsForEmailFailure + @brief Tests the flow of a failed @c fetchSignInMethodsForEmail:completion: call. + */ +- (void)testFetchSignInMethodsForEmailFailure { + OCMExpect([_mockBackend createAuthURI:[OCMArg any] callback:[OCMArg any]]) + .andDispatchError2([FIRAuthErrorUtils tooManyRequestsErrorWithMessage:nil]); + XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; + [[FIRAuth auth] fetchSignInMethodsForEmail:kEmail + completion:^(NSArray<NSString *> *_Nullable signInMethods, + NSError *_Nullable error) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertNil(signInMethods); + XCTAssertEqual(error.code, FIRAuthErrorCodeTooManyRequests); + XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; + OCMVerifyAll(_mockBackend); +} #if TARGET_OS_IOS /** @fn testPhoneAuthSuccess @brief Tests the flow of a successful @c signInWithCredential:completion for phone auth. @@ -501,6 +588,63 @@ static const NSTimeInterval kWaitInterval = .5; } #endif +/** @fn testSignInWithEmailLinkSuccess + @brief Tests the flow of a successful @c signInWithEmail:link:completion: call. + */ +- (void)testSignInWithEmailLinkSuccess { + NSString *fakeCode = @"testoobcode"; + OCMExpect([_mockBackend emailLinkSignin:[OCMArg any] callback:[OCMArg any]]) + .andCallBlock2(^(FIREmailLinkSignInRequest *_Nullable request, + FIREmailLinkSigninResponseCallback callback) { + XCTAssertEqualObjects(request.email, kEmail); + XCTAssertEqualObjects(request.oobCode, fakeCode); + dispatch_async(FIRAuthGlobalWorkQueue(), ^() { + id mockEmailLinkSignInResponse = OCMClassMock([FIREmailLinkSignInResponse class]); + [self stubTokensWithMockResponse:mockEmailLinkSignInResponse]; + callback(mockEmailLinkSignInResponse, nil); + OCMStub([mockEmailLinkSignInResponse refreshToken]).andReturn(kRefreshToken); + }); + }); + [self expectGetAccountInfo]; + XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; + [[FIRAuth auth] signOut:NULL]; + [[FIRAuth auth] signInWithEmail:kEmail + link:kFakeEmailSignInlink + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertNotNil(authResult.user); + XCTAssertEqualObjects(authResult.user.refreshToken, kRefreshToken); + XCTAssertFalse(authResult.user.anonymous); + XCTAssertEqualObjects(authResult.user.email, kEmail); + XCTAssertNil(error); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; + [self assertUser:[FIRAuth auth].currentUser]; + OCMVerifyAll(_mockBackend); +} + +/** @fn testSignInWithEmailLinkFailure + @brief Tests the flow of a failed @c signInWithEmail:link:completion: call. + */ +- (void)testSignInWithEmailLinkFailure { + OCMExpect([_mockBackend emailLinkSignin:[OCMArg any] callback:[OCMArg any]]) + ._andDispatchError2([FIRAuthErrorUtils invalidActionCodeErrorWithMessage:nil]); + XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; + [[FIRAuth auth] signOut:NULL]; + [[FIRAuth auth] signInWithEmail:kEmail + link:kFakeEmailSignInlink + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidActionCode); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; + OCMVerifyAll(_mockBackend); +} + /** @fn testSignInWithEmailPasswordSuccess @brief Tests the flow of a successful @c signInWithEmail:password:completion: call. */ @@ -521,8 +665,10 @@ static const NSTimeInterval kWaitInterval = .5; [self expectGetAccountInfo]; XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; [[FIRAuth auth] signOut:NULL]; - [[FIRAuth auth] signInWithEmail:kEmail password:kFakePassword completion:^(FIRUser *_Nullable user, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithEmail:kEmail + password:kFakePassword + completion:^(FIRUser *_Nullable user, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); [self assertUser:user]; XCTAssertNil(error); @@ -541,8 +687,10 @@ static const NSTimeInterval kWaitInterval = .5; .andDispatchError2([FIRAuthErrorUtils wrongPasswordErrorWithMessage:nil]); XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; [[FIRAuth auth] signOut:NULL]; - [[FIRAuth auth] signInWithEmail:kEmail password:kFakePassword completion:^(FIRUser *_Nullable user, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithEmail:kEmail + password:kFakePassword + completion:^(FIRUser *_Nullable user, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); XCTAssertNil(user); XCTAssertEqual(error.code, FIRAuthErrorCodeWrongPassword); @@ -829,6 +977,68 @@ static const NSTimeInterval kWaitInterval = .5; OCMVerifyAll(_mockBackend); } +/** @fn testSignInWithEmailLinkCredentialSuccess + @brief Tests the flow of a successfully @c signInWithCredential:completion: call with an + email sign-in link credential using FIREmailAuthProvider. + */ +- (void)testSignInWithEmailLinkCredentialSuccess { + NSString *fakeCode = @"testoobcode"; + OCMExpect([_mockBackend emailLinkSignin:[OCMArg any] callback:[OCMArg any]]) + .andCallBlock2(^(FIREmailLinkSignInRequest *_Nullable request, + FIREmailLinkSigninResponseCallback callback) { + XCTAssertEqualObjects(request.email, kEmail); + XCTAssertEqualObjects(request.oobCode, fakeCode); + dispatch_async(FIRAuthGlobalWorkQueue(), ^() { + id mockEmailLinkSigninResponse = OCMClassMock([FIREmailLinkSignInResponse class]); + [self stubTokensWithMockResponse:mockEmailLinkSigninResponse]; + callback(mockEmailLinkSigninResponse, nil); + }); + }); + [self expectGetAccountInfo]; + XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; + [[FIRAuth auth] signOut:NULL]; + FIRAuthCredential *emailCredential = + [FIREmailAuthProvider credentialWithEmail:kEmail link:kFakeEmailSignInlink]; + [[FIRAuth auth] signInAndRetrieveDataWithCredential:emailCredential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertNotNil(authResult.user); + XCTAssertEqualObjects(authResult.user.refreshToken, kRefreshToken); + XCTAssertFalse(authResult.user.anonymous); + XCTAssertEqualObjects(authResult.user.email, kEmail); + XCTAssertNil(error); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; + [self assertUser:[FIRAuth auth].currentUser]; + OCMVerifyAll(_mockBackend); +} + +/** @fn testSignInWithEmailLinkCredentialFailure + @brief Tests the flow of a failed @c signInWithCredential:completion: call with an + email-email sign-in link credential using FIREmailAuthProvider. + */ +- (void)testSignInWithEmailLinkCredentialFailure { + OCMExpect([_mockBackend emailLinkSignin:[OCMArg any] callback:[OCMArg any]]) + .andDispatchError2([FIRAuthErrorUtils userDisabledErrorWithMessage:nil]); + XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; + [[FIRAuth auth] signOut:NULL]; + FIRAuthCredential *emailCredential = + [FIREmailAuthProvider credentialWithEmail:kEmail link:kFakeEmailSignInlink]; + [[FIRAuth auth] signInWithCredential:emailCredential completion:^(FIRUser *_Nullable user, + NSError *_Nullable error) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertNil(user); + XCTAssertEqual(error.code, FIRAuthErrorCodeUserDisabled); + XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; + XCTAssertNil([FIRAuth auth].currentUser); + OCMVerifyAll(_mockBackend); +} + /** @fn testSignInWithEmailCredentialSuccess @brief Tests the flow of a successfully @c signInWithCredential:completion: call with an email-password credential. @@ -1504,6 +1714,65 @@ static const NSTimeInterval kWaitInterval = .5; OCMVerifyAll(_mockBackend); } +/** @fn testSendSignInLinkToEmailSuccess + @brief Tests the flow of a successful @c sendSignInLinkToEmail:actionCodeSettings:completion: + call. + */ +- (void)testSendSignInLinkToEmailSuccess { + OCMExpect([_mockBackend getOOBConfirmationCode:[OCMArg any] callback:[OCMArg any]]) + .andCallBlock2(^(FIRGetOOBConfirmationCodeRequest *_Nullable request, + FIRGetOOBConfirmationCodeResponseCallback callback) { + XCTAssertEqualObjects(request.APIKey, kAPIKey); + XCTAssertEqualObjects(request.email, kEmail); + XCTAssertEqualObjects(request.continueURL, kContinueURL); + XCTAssertTrue(request.handleCodeInApp); + dispatch_async(FIRAuthGlobalWorkQueue(), ^() { + callback([[FIRGetOOBConfirmationCodeResponse alloc] init], nil); + }); + }); + XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; + [[FIRAuth auth] sendSignInLinkToEmail:kEmail + actionCodeSettings:[self fakeActionCodeSettings] + completion:^(NSError *_Nullable error) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertNil(error); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; + OCMVerifyAll(_mockBackend); +} + +/** @fn testSendSignInLinkToEmailFailure + @brief Tests the flow of a failed @c sendSignInLinkToEmail:actionCodeSettings:completion: + call. + */ +- (void)testSendSignInLinkToEmailFailure { + OCMExpect([_mockBackend getOOBConfirmationCode:[OCMArg any] callback:[OCMArg any]]) + .andDispatchError2([FIRAuthErrorUtils appNotAuthorizedError]); + XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; + [[FIRAuth auth] sendSignInLinkToEmail:kEmail + actionCodeSettings:[self fakeActionCodeSettings] + completion:^(NSError *_Nullable error) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertEqual(error.code, FIRAuthErrorCodeAppNotAuthorized); + XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; + OCMVerifyAll(_mockBackend); +} + +/** @fn fakeActionCodeSettings + @brief Constructs and returns a fake instance of @c FIRActionCodeSettings for testing. + @return An instance of @c FIRActionCodeSettings for testing. + */ +- (FIRActionCodeSettings *)fakeActionCodeSettings { + FIRActionCodeSettings *actionCodeSettings = [[FIRActionCodeSettings alloc]init]; + actionCodeSettings.URL = [NSURL URLWithString:kContinueURL]; + actionCodeSettings.handleCodeInApp = YES; + return actionCodeSettings; +} + /** @fn testSignOut @brief Tests the @c signOut: method. */ @@ -1515,6 +1784,15 @@ static const NSTimeInterval kWaitInterval = .5; XCTAssertNil([FIRAuth auth].currentUser); } +/** @fn testIsSignInWithEmailLink + @brief Tests the @c isSignInWithEmailLink: method. +*/ +- (void)testIsSignInWithEmailLink { + XCTAssertTrue([[FIRAuth auth] isSignInWithEmailLink:kFakeEmailSignInlink]); + XCTAssertFalse([[FIRAuth auth] isSignInWithEmailLink:kBadSignInEmailLink]); + XCTAssertFalse([[FIRAuth auth] isSignInWithEmailLink:@""]); +} + /** @fn testAuthStateChanges @brief Tests @c addAuthStateDidChangeListener: and @c removeAuthStateDidChangeListener: methods. */ diff --git a/Example/Auth/Tests/FIREmailLinkRequestTests.m b/Example/Auth/Tests/FIREmailLinkRequestTests.m new file mode 100644 index 0000000..90d7c18 --- /dev/null +++ b/Example/Auth/Tests/FIREmailLinkRequestTests.m @@ -0,0 +1,137 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import <XCTest/XCTest.h> + +#import "FIRAuthErrors.h" +#import "FIRAuthBackend.h" +#import "FIREmailLinkSignInRequest.h" +#import "FIREmailLinkSignInResponse.h" +#import "FIRFakeBackendRPCIssuer.h" + +/** @var kTestAPIKey + @brief Fake API key used for testing. + */ +static NSString *const kTestAPIKey = @"APIKey"; + +/** @var kTestEmail + @brief The key for the "email" value in the request. + */ +static NSString *const kTestEmail = @"TestEmail@email.com"; + +/** @var kTestOOBCode + @brief The test value for the "oobCode" in the request. + */ +static NSString *const kTestOOBCode = @"TestOOBCode"; + +/** @var kTestIDToken + @brief The test value for "idToken" in the request. + */ +static NSString *const kTestIDToken = @"testIDToken"; + +/** @var kEmailKey + @brief The key for the "identifier" value in the request. + */ +static NSString *const kEmailKey = @"email"; + +/** @var kEmailLinkKey + @brief The key for the "oobCode" value in the request. + */ +static NSString *const kOOBCodeKey = @"oobCode"; + +/** @var kIDTokenKey + @brief The key for the "IDToken" value in the request. + */ +static NSString *const kIDTokenKey = @"idToken"; + +/** @var kExpectedAPIURL + @brief The value of the expected URL (including the backend endpoint) in the request. + */ +static NSString *const kExpectedAPIURL = + @"https://www.googleapis.com/identitytoolkit/v3/relyingparty/emailLinkSignin?key=APIKey"; + +/** @class FIREmailLinkRequestTests + @brief Tests for @c FIREmailLinkRequests. + */ +@interface FIREmailLinkRequestTests : XCTestCase +@end + +@implementation FIREmailLinkRequestTests { + /** @var _RPCIssuer + @brief This backend RPC issuer is used to fake network responses for each test in the suite. + In the @c setUp method we initialize this and set @c FIRAuthBackend's RPC issuer to it. + */ + FIRFakeBackendRPCIssuer *_RPCIssuer; + + /** @var _requestConfiguration + @brief This is the request configuration used for testing. + */ + FIRAuthRequestConfiguration *_requestConfiguration; +} + +- (void)setUp { + [super setUp]; + FIRFakeBackendRPCIssuer *RPCIssuer = [[FIRFakeBackendRPCIssuer alloc] init]; + [FIRAuthBackend setDefaultBackendImplementationWithRPCIssuer:RPCIssuer]; + _RPCIssuer = RPCIssuer; + _requestConfiguration = [[FIRAuthRequestConfiguration alloc] initWithAPIKey:kTestAPIKey]; +} + +- (void)tearDown { + _RPCIssuer = nil; + _requestConfiguration = nil; + [FIRAuthBackend setDefaultBackendImplementationWithRPCIssuer:nil]; + [super tearDown]; +} + +/** @fn testEmailLinkRequestCreation + @brief Tests the email link sign-in request with mandatory parameters. + */ +- (void)testEmailLinkRequest { + FIREmailLinkSignInRequest *request = + [[FIREmailLinkSignInRequest alloc] initWithEmail:kTestEmail + oobCode:kTestOOBCode + requestConfiguration:_requestConfiguration]; + [FIRAuthBackend emailLinkSignin:request callback:^(FIREmailLinkSignInResponse *_Nullable response, + NSError *_Nullable error) { + }]; + XCTAssertEqualObjects(_RPCIssuer.requestURL.absoluteString, kExpectedAPIURL); + XCTAssertNotNil(_RPCIssuer.decodedRequest); + XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kEmailKey], kTestEmail); + XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kOOBCodeKey], kTestOOBCode); + XCTAssertNil(_RPCIssuer.decodedRequest[kIDTokenKey]); +} + +/** @fn testEmailLinkRequestCreationOptional + @brief Tests the email link sign-in request with mandatory parameters and optional ID token. + */ +- (void)testEmailLinkRequestCreationOptional { + FIREmailLinkSignInRequest *request = + [[FIREmailLinkSignInRequest alloc] initWithEmail:kTestEmail + oobCode:kTestOOBCode + requestConfiguration:_requestConfiguration]; + request.IDToken = kTestIDToken; + [FIRAuthBackend emailLinkSignin:request callback:^(FIREmailLinkSignInResponse *_Nullable response, + NSError *_Nullable error) { + }]; + XCTAssertEqualObjects(_RPCIssuer.requestURL.absoluteString, kExpectedAPIURL); + XCTAssertNotNil(_RPCIssuer.decodedRequest); + XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kEmailKey], kTestEmail); + XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kOOBCodeKey], kTestOOBCode); + XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kIDTokenKey], kTestIDToken); +} + +@end diff --git a/Example/Auth/Tests/FIREmailLinkSignInResponseTests.m b/Example/Auth/Tests/FIREmailLinkSignInResponseTests.m new file mode 100644 index 0000000..cc2c544 --- /dev/null +++ b/Example/Auth/Tests/FIREmailLinkSignInResponseTests.m @@ -0,0 +1,195 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import <XCTest/XCTest.h> + +#import "FIRAuthErrors.h" +#import "FIRAuthErrorUtils.h" +#import "FIRAuthBackend.h" +#import "FIREmailLinkSignInRequest.h" +#import "FIREmailLinkSignInResponse.h" +#import "FIRFakeBackendRPCIssuer.h" + +/** @var kTestAPIKey + @brief Fake API key used for testing. + */ +static NSString *const kTestAPIKey = @"APIKey"; + +/** @var kTestEmail + @brief The key for the "email" value in the request. + */ +static NSString *const kTestEmail = @"TestEmail@email.com"; + +/** @var kTestOOBCode + @brief The test value for the "oobCode" in the request. + */ +static NSString *const kTestOOBCode = @"TestOOBCode"; + +/** @var kTestIDToken + @brief The test value for "idToken" in the request. + */ +static NSString *const kTestIDToken = @"testIDToken"; + +/** @var kEmailKey + @brief The key for the "identifier" value in the request. + */ +static NSString *const kEmailKey = @"email"; + +/** @var kEmailLinkKey + @brief The key for the "emailLink" value in the request. + */ +static NSString *const kOOBCodeKey = @"oobCode"; + +/** @var kIDTokenKey + @brief The key for the "IDToken" value in the request. + */ +static NSString *const kIDTokenKey = @"idToken"; + +/** @var kTestIDTokenResponse + @brief A fake ID Token in the server response. + */ +static NSString *const kTestIDTokenResponse = @"fakeToken"; + +/** @var kTestEmailResponse + @brief A fake email in the server response. + */ +static NSString *const kTestEmailResponse = @"fake email"; + +/** @var kTestRefreshToken + @brief A fake refresh token in the server response. + */ +static NSString *const kTestRefreshToken = @"testRefreshToken"; + +/** @var kInvalidEmailErrorMessage + @brief The error returned by the server if the email is invalid. + */ +static NSString *const kInvalidEmailErrorMessage = @"INVALID_EMAIL"; + +/** @var kTestTokenExpirationTimeInterval + @brief The fake time interval that it takes a token to expire. + */ +static const NSTimeInterval kTestTokenExpirationTimeInterval = 55 * 60; + +/** @var kMaxDifferenceBetweenDates + @brief The maximum difference between time two dates (in seconds), after which they will be + considered different. + */ +static const NSTimeInterval kMaxDifferenceBetweenDates = 0.0001; + +/** @var kFakeIsNewUSerFlag + @brief The fake fake isNewUser flag in the response. + */ +static const BOOL kFakeIsNewUSerFlag = YES; + +/** @class FIREmailLinkRequestTests + @brief Tests for @c FIREmailLinkRequests. + */ +@interface FIREmailLinkSignInResponseTests : XCTestCase +@end + +@implementation FIREmailLinkSignInResponseTests { + /** @var _RPCIssuer + @brief This backend RPC issuer is used to fake network responses for each test in the suite. + In the @c setUp method we initialize this and set @c FIRAuthBackend's RPC issuer to it. + */ + FIRFakeBackendRPCIssuer *_RPCIssuer; + + /** @var _requestConfiguration + @brief This is the request configuration used for testing. + */ + FIRAuthRequestConfiguration *_requestConfiguration; +} + +- (void)setUp { + [super setUp]; + FIRFakeBackendRPCIssuer *RPCIssuer = [[FIRFakeBackendRPCIssuer alloc] init]; + [FIRAuthBackend setDefaultBackendImplementationWithRPCIssuer:RPCIssuer]; + _RPCIssuer = RPCIssuer; + _requestConfiguration = [[FIRAuthRequestConfiguration alloc] initWithAPIKey:kTestAPIKey]; +} + +/** @fn testFailedEmailLinkSignInResponse + @brief Tests a failed email link sign-in response. + */ +- (void)testFailedEmailLinkSignInResponse { + FIREmailLinkSignInRequest *request = + [[FIREmailLinkSignInRequest alloc] initWithEmail:kTestEmail + oobCode:kTestOOBCode + requestConfiguration:_requestConfiguration]; + + __block BOOL callbackInvoked = NO; + __block FIREmailLinkSignInResponse *RPCResponse; + __block NSError *RPCError; + [FIRAuthBackend emailLinkSignin:request + callback:^(FIREmailLinkSignInResponse *_Nullable response, + NSError *_Nullable error) { + callbackInvoked = YES; + RPCResponse = response; + RPCError = error; + }]; + + [_RPCIssuer respondWithServerErrorMessage:kInvalidEmailErrorMessage]; + + XCTAssert(callbackInvoked); + XCTAssertNil(RPCResponse); + XCTAssertEqual(RPCError.code, FIRAuthErrorCodeInvalidEmail); +} + +/** @fn testSuccessfulEmailLinkSignInResponse + @brief Tests a succesful email link sign-in response. + */ +- (void)testSuccessfulEmailLinkSignInResponse { + FIREmailLinkSignInRequest *request = + [[FIREmailLinkSignInRequest alloc] initWithEmail:kTestEmail + oobCode:kTestOOBCode + requestConfiguration:_requestConfiguration]; + + __block BOOL callbackInvoked = NO; + __block FIREmailLinkSignInResponse *RPCResponse; + __block NSError *RPCError; + [FIRAuthBackend emailLinkSignin:request + callback:^(FIREmailLinkSignInResponse *_Nullable response, + NSError *_Nullable error) { + callbackInvoked = YES; + RPCResponse = response; + RPCError = error; + }]; + + [_RPCIssuer respondWithJSON:@{ + @"idToken" : kTestIDTokenResponse, + @"email" : kTestEmailResponse, + @"isNewUser" : kFakeIsNewUSerFlag ? @YES : @NO, + @"expiresIn" : [NSString stringWithFormat:@"%f",kTestTokenExpirationTimeInterval], + @"refreshToken" : kTestRefreshToken, + }]; + + XCTAssert(callbackInvoked); + XCTAssertNil(RPCError); + XCTAssertNotNil(RPCResponse); + XCTAssertEqualObjects(RPCResponse.IDToken, kTestIDTokenResponse); + XCTAssertEqualObjects(RPCResponse.email, kTestEmailResponse); + XCTAssertEqualObjects(RPCResponse.refreshToken, kTestRefreshToken); + XCTAssertTrue(RPCResponse.isNewUser); + NSTimeInterval expirationTimeInterval = + [RPCResponse.approximateExpirationDate timeIntervalSinceNow]; + NSTimeInterval testTimeInterval = + [[NSDate dateWithTimeIntervalSinceNow:kTestTokenExpirationTimeInterval] timeIntervalSinceNow]; + NSTimeInterval timeIntervalDifference = + fabs(expirationTimeInterval - testTimeInterval); + XCTAssert(timeIntervalDifference < kMaxDifferenceBetweenDates); +} + +@end diff --git a/Example/Auth/Tests/FIRGetOOBConfirmationCodeRequestTests.m b/Example/Auth/Tests/FIRGetOOBConfirmationCodeRequestTests.m index 965af8a..b11c759 100644 --- a/Example/Auth/Tests/FIRGetOOBConfirmationCodeRequestTests.m +++ b/Example/Auth/Tests/FIRGetOOBConfirmationCodeRequestTests.m @@ -49,6 +49,11 @@ static NSString *const kPasswordResetRequestTypeValue = @"PASSWORD_RESET"; */ static NSString *const kVerifyEmailRequestTypeValue = @"VERIFY_EMAIL"; +/** @var kEmailLinkSignInTypeValue + @brief The value for the "EMAIL_SIGNIN" request type. + */ +static NSString *const kEmailLinkSignInTypeValue = @"EMAIL_SIGNIN"; + /** @var kEmailKey @brief The name of the "email" property in the request. */ @@ -124,6 +129,7 @@ static NSString *const kCanHandleCodeInAppKey = @"canHandleCodeInApp"; /** @class FIRGetOOBConfirmationCodeRequestTests @brief Tests for @c FIRGetOOBConfirmationCodeRequest. */ + @interface FIRGetOOBConfirmationCodeRequestTests : XCTestCase @end @implementation FIRGetOOBConfirmationCodeRequestTests { @@ -190,6 +196,43 @@ static NSString *const kCanHandleCodeInAppKey = @"canHandleCodeInApp"; [NSNumber numberWithBool:YES]); } +/** @fn testSignInWithEmailLinkRequest + @brief Tests the encoding of a email sign-in link request. + */ +- (void)testSignInWithEmailLinkRequest { + FIRGetOOBConfirmationCodeRequest *request = + [FIRGetOOBConfirmationCodeRequest signInWithEmailLinkRequest:kTestEmail + actionCodeSettings:[self fakeActionCodeSettings] + requestConfiguration:_requestConfiguration]; + + __block BOOL callbackInvoked; + __block FIRGetOOBConfirmationCodeResponse *RPCResponse; + __block NSError *RPCError; + [FIRAuthBackend getOOBConfirmationCode:request + callback:^(FIRGetOOBConfirmationCodeResponse *_Nullable response, + NSError *_Nullable error) { + callbackInvoked = YES; + RPCResponse = response; + RPCError = error; + }]; + + XCTAssertEqualObjects(_RPCIssuer.requestURL.absoluteString, kExpectedAPIURL); + XCTAssertNotNil(_RPCIssuer.decodedRequest); + XCTAssert([_RPCIssuer.decodedRequest isKindOfClass:[NSDictionary class]]); + XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kEmailKey], kTestEmail); + XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kRequestTypeKey], kEmailLinkSignInTypeValue); + XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kContinueURLKey], kContinueURL); + XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kIosBundleIDKey], kIosBundleID); + XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kAndroidPackageNameKey], kAndroidPackageName); + XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kAndroidMinimumVersionKey], + kAndroidMinimumVersion); + XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kAndroidInstallAppKey], + [NSNumber numberWithBool:YES]); + XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kCanHandleCodeInAppKey], + [NSNumber numberWithBool:YES]); +} + + /** @fn testEmailVerificationRequest @brief Tests the encoding of an email verification request. */ diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj index c8400d6..7c00d1c 100644 --- a/Example/Firebase.xcodeproj/project.pbxproj +++ b/Example/Firebase.xcodeproj/project.pbxproj @@ -135,6 +135,8 @@ 7E26CF28514D041D284F00A5 /* Pods_Database_Tests_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5318F3AE32EEBCC9FF608813 /* Pods_Database_Tests_tvOS.framework */; }; 7E5BD38D202BFB8CD9CCEB53 /* Pods_Auth_EarlGreyTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FAA82401DA4259800B142EA /* Pods_Auth_EarlGreyTests.framework */; }; 7E9485421F578AC4005A3939 /* FIRAuthURLPresenterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E94853F1F578A9D005A3939 /* FIRAuthURLPresenterTests.m */; }; + 7EE21F7A1FE89193009B1370 /* FIREmailLinkRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7EE21F791FE89193009B1370 /* FIREmailLinkRequestTests.m */; }; + 7EE21F7C1FE8919E009B1370 /* FIREmailLinkSignInResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7EE21F7B1FE8919D009B1370 /* FIREmailLinkSignInResponseTests.m */; }; 7EFA2E041F71C93300DD354F /* FIRUserMetadataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7EFA2E031F71C93300DD354F /* FIRUserMetadataTests.m */; }; 7F41B0EFBDDA90CB9CE6CE5B /* Pods_Database_Tests_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 61377AC9FE132A8D7BF71881 /* Pods_Database_Tests_macOS.framework */; }; 822CE316AE9827F7F0889B30 /* Pods_Auth_Example_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9878B57CF73D2F865992E6EA /* Pods_Auth_Example_macOS.framework */; }; @@ -1037,6 +1039,8 @@ 7981511F571E13DECA09B4B1 /* Pods-Core_Example_macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Core_Example_macOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Core_Example_macOS/Pods-Core_Example_macOS.release.xcconfig"; sourceTree = "<group>"; }; 7E94853F1F578A9D005A3939 /* FIRAuthURLPresenterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRAuthURLPresenterTests.m; sourceTree = "<group>"; }; 7ED0DF69C095C21EFC81F672 /* Pods-Database_Example_tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Database_Example_tvOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Database_Example_tvOS/Pods-Database_Example_tvOS.release.xcconfig"; sourceTree = "<group>"; }; + 7EE21F791FE89193009B1370 /* FIREmailLinkRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIREmailLinkRequestTests.m; sourceTree = "<group>"; }; + 7EE21F7B1FE8919D009B1370 /* FIREmailLinkSignInResponseTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIREmailLinkSignInResponseTests.m; sourceTree = "<group>"; }; 7EFA2E031F71C93300DD354F /* FIRUserMetadataTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRUserMetadataTests.m; sourceTree = "<group>"; }; 8496034D8156555C5FCF8F14 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; }; 84EC7975F05977AE75E90A12 /* Pods_Database_Example_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Database_Example_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -2371,9 +2375,11 @@ DE9315001E86C6FF0083EDBF /* FIRAuthGlobalWorkQueueTests.m */, DE9315011E86C6FF0083EDBF /* FIRAuthKeychainTests.m */, DE750DB81EB3DD4000A75E47 /* FIRAuthNotificationManagerTests.m */, + 7EE21F791FE89193009B1370 /* FIREmailLinkRequestTests.m */, DE9315021E86C6FF0083EDBF /* FIRAuthSerialTaskQueueTests.m */, DE9315031E86C6FF0083EDBF /* FIRAuthTests.m */, 7E94853F1F578A9D005A3939 /* FIRAuthURLPresenterTests.m */, + 7EE21F7B1FE8919D009B1370 /* FIREmailLinkSignInResponseTests.m */, DE9315041E86C6FF0083EDBF /* FIRAuthUserDefaultsStorageTests.m */, DE9315051E86C6FF0083EDBF /* FIRCreateAuthURIRequestTests.m */, DE9315061E86C6FF0083EDBF /* FIRCreateAuthURIResponseTests.m */, @@ -4662,6 +4668,7 @@ name = "[CP] Check Pods Manifest.lock"; outputPaths = ( "$(DERIVED_FILE_DIR)/Pods-Database_Tests_tvOS-checkManifestLockResult.txt", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -5425,6 +5432,7 @@ ); name = "[CP] Copy Pods Resources"; outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -6188,6 +6196,7 @@ DE9315571E86C71C0083EDBF /* FIRAdditionalUserInfoTests.m in Sources */, DE750DBF1EB3DD6C00A75E47 /* FIRAuthAppCredentialManagerTests.m in Sources */, DE93157B1E86C71C0083EDBF /* FIRVerifyPasswordResponseTests.m in Sources */, + 7EE21F7A1FE89193009B1370 /* FIREmailLinkRequestTests.m in Sources */, DE93155B1E86C71C0083EDBF /* FIRAuthDispatcherTests.m in Sources */, DE9315791E86C71C0083EDBF /* FIRVerifyCustomTokenResponseTests.m in Sources */, DE9315601E86C71C0083EDBF /* FIRAuthUserDefaultsStorageTests.m in Sources */, @@ -6216,6 +6225,7 @@ DE93155A1E86C71C0083EDBF /* FIRAuthBackendRPCImplementationTests.m in Sources */, DE93157D1E86C71C0083EDBF /* FIRVerifyPhoneNumberResponseTests.m in Sources */, DE93157E1E86C71C0083EDBF /* OCMStubRecorder+FIRAuthUnitTests.m in Sources */, + 7EE21F7C1FE8919E009B1370 /* FIREmailLinkSignInResponseTests.m in Sources */, DE9315771E86C71C0083EDBF /* FIRVerifyAssertionResponseTests.m in Sources */, DE9315721E86C71C0083EDBF /* FIRSignUpNewUserRequestTests.m in Sources */, DE9315671E86C71C0083EDBF /* FIRGetAccountInfoResponseTests.m in Sources */, |