From e400f1949e84ab5b05780a7ef603b7f377938619 Mon Sep 17 00:00:00 2001 From: Chuan Ren Date: Mon, 19 Nov 2018 11:25:15 -0800 Subject: [PATCH 1/3] Add test for verify iOS client --- .../EarlGreyTests/FIRVerifyIOSClientTests.m | 90 +++++++++++++++++ .../EarlGreyTests/FirebaseAuthEarlGreyTests.m | 6 +- Example/Auth/Sample/MainViewController.m | 97 ++++++++++++++++--- Example/Firebase.xcodeproj/project.pbxproj | 8 +- .../xcschemes/Auth_Sample.xcscheme | 14 ++- .../Auth/Source/Public/FIRPhoneAuthProvider.h | 1 - 6 files changed, 189 insertions(+), 27 deletions(-) create mode 100644 Example/Auth/EarlGreyTests/FIRVerifyIOSClientTests.m diff --git a/Example/Auth/EarlGreyTests/FIRVerifyIOSClientTests.m b/Example/Auth/EarlGreyTests/FIRVerifyIOSClientTests.m new file mode 100644 index 00000000000..f3de7c5dbeb --- /dev/null +++ b/Example/Auth/EarlGreyTests/FIRVerifyIOSClientTests.m @@ -0,0 +1,90 @@ +/* + * Copyright 2018 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 +#import + +#import +#import "FirebaseAuth.h" + +static CGFloat const kShortScrollDistance = 100; + +static NSTimeInterval const kWaitForElementTimeOut = 15; + +@interface FIRVerifyIOSClientTests : XCTestCase +@end + +/** Convenience function for EarlGrey tests. */ +static id grey_scrollView(void) { + return [GREYMatchers matcherForKindOfClass:[UIScrollView class]]; +} + +@implementation FIRVerifyIOSClientTests + +/** To reset the app so that each test sees the app in a clean state. */ +- (void)setUp { + [super setUp]; + + [self signOut]; + + [[EarlGrey selectElementWithMatcher:grey_allOf(grey_scrollView(), + grey_kindOfClass([UITableView class]), nil)] + performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)]; +} + +#pragma mark - Tests + +/** Test verify ios client*/ +- (void)testVerifyIOSClient { + [[[EarlGrey selectElementWithMatcher:grey_allOf(grey_text(@"Verify iOS client"), + grey_sufficientlyVisible(), nil)] + usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, kShortScrollDistance) + onElementWithMatcher:grey_allOf(grey_scrollView(), grey_kindOfClass([UITableView class]), + nil)] performAction:grey_tap()]; + + [self waitForElementWithText:@"OK" withDelay:kWaitForElementTimeOut]; + + [[EarlGrey selectElementWithMatcher:grey_text(@"OK")] performAction:grey_tap()]; +} + +#pragma mark - Helpers + +/** Sign out current account. */ +- (void)signOut { + NSError *signOutError; + BOOL status = [[FIRAuth auth] signOut:&signOutError]; + + // Just log the error because we don't want to fail the test if signing out fails. + if (!status) { + NSLog(@"Error signing out: %@", signOutError); + } +} + +/** Wait for an element with text to appear. */ +- (void)waitForElementWithText:(NSString *)text withDelay:(NSTimeInterval)maxDelay { + GREYCondition *displayed = + [GREYCondition conditionWithName:@"Wait for element" + block:^BOOL { + NSError *error = nil; + [[EarlGrey selectElementWithMatcher:grey_text(text)] + assertWithMatcher:grey_sufficientlyVisible() + error:&error]; + return !error; + }]; + GREYAssertTrue([displayed waitWithTimeout:maxDelay], @"Failed to wait for element '%@'.", text); +} + +@end diff --git a/Example/Auth/EarlGreyTests/FirebaseAuthEarlGreyTests.m b/Example/Auth/EarlGreyTests/FirebaseAuthEarlGreyTests.m index 0b860f54bac..df21e47d1e0 100644 --- a/Example/Auth/EarlGreyTests/FirebaseAuthEarlGreyTests.m +++ b/Example/Auth/EarlGreyTests/FirebaseAuthEarlGreyTests.m @@ -38,7 +38,7 @@ @interface BasicUITest :XCTestCase @end /** Convenience function for EarlGrey tests. */ -id grey_scrollView(void) { +static id grey_scrollView(void) { return [GREYMatchers matcherForKindOfClass:[UIScrollView class]]; } @@ -148,8 +148,7 @@ - (void)testSignInWithInvalidBYOAuthToken { [[EarlGrey selectElementWithMatcher:grey_text(@"Done")] performAction:grey_tap()]; - NSString *invalidTokenErrorMessage = - @"The custom token format is incorrect. Please check the documentation."; + NSString *invalidTokenErrorMessage = @"Sign-In Error"; [self waitForElementWithText:invalidTokenErrorMessage withDelay:kWaitForElementTimeOut]; @@ -182,4 +181,5 @@ - (void)waitForElementWithText:(NSString *)text withDelay:(NSTimeInterval)maxDel }]; GREYAssertTrue([displayed waitWithTimeout:maxDelay], @"Failed to wait for element '%@'.", text); } + @end diff --git a/Example/Auth/Sample/MainViewController.m b/Example/Auth/Sample/MainViewController.m index 9b928fb7bca..85c2c0c45bf 100644 --- a/Example/Auth/Sample/MainViewController.m +++ b/Example/Auth/Sample/MainViewController.m @@ -39,6 +39,16 @@ #import "UserInfoViewController.h" #import "UserTableViewCell.h" +#import "FIRAuth_Internal.h" +#import "FIRAuthAPNSToken.h" +#import "FIRAuthAPNSTokenManager.h" +#import "FIRAuthAppCredential.h" +#import "FIRAuthAppCredentialManager.h" +#import "FIRAuthBackend.h" +#import "FIRVerifyClientRequest.h" +#import "FIRVerifyClientResponse.h" +#import "FIRSendVerificationCodeRequest.h" + NS_ASSUME_NONNULL_BEGIN /** @typedef textInputCompletionBlock @@ -578,6 +588,11 @@ */ static NSString *const kPhoneNumberSignInReCaptchaTitle = @"Sign in With Phone Number"; +/** @var kVerifyIOSClientTitle + @brief The title for button to verify iOS client. + */ +static NSString *const kVerifyIOSClientTitle = @"Verify iOS client"; + /** @var kIsNewUserToggleTitle @brief The title for button to enable new or existing user toggle. */ @@ -741,6 +756,8 @@ - (void)updateTable { action:^{ [weakSelf unlinkFromProvider:FIRPhoneAuthProviderID completion:nil]; }], + [StaticContentTableViewCell cellWithTitle:kVerifyIOSClientTitle + action:^{ [weakSelf verifyIOSClient]; }], ]], [StaticContentTableViewSection sectionWithTitle:kSectionTitleSignIn cells:@[ [StaticContentTableViewCell cellWithTitle:kSwitchToInMemoryUserTitle @@ -1605,17 +1622,19 @@ - (void)removeIDTokenListener { @param string The string to add to the console. */ - (void)log:(NSString *)string { - NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; - dateFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss"; - NSString *date = [dateFormatter stringFromDate:[NSDate date]]; - if (!_consoleString) { - _consoleString = [NSMutableString string]; - } - [_consoleString appendString:[NSString stringWithFormat:@"%@ %@\n", date, string]]; - _consoleTextView.text = _consoleString; + dispatch_async(dispatch_get_main_queue(), ^{ + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + dateFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss"; + NSString *date = [dateFormatter stringFromDate:[NSDate date]]; + if (!_consoleString) { + _consoleString = [NSMutableString string]; + } + [_consoleString appendString:[NSString stringWithFormat:@"%@ %@\n", date, string]]; + _consoleTextView.text = _consoleString; - CGRect targetRect = CGRectMake(0, _consoleTextView.contentSize.height - 1, 1, 1); - [_consoleTextView scrollRectToVisible:targetRect animated:YES]; + CGRect targetRect = CGRectMake(0, _consoleTextView.contentSize.height - 1, 1, 1); + [_consoleTextView scrollRectToVisible:targetRect animated:YES]; + }); } /** @fn logSuccess: @@ -2885,14 +2904,66 @@ - (void)signInWithPhoneNumber:(NSString *_Nullable)phoneNumber }]; } +/** @fn verifyIOSClient + @brief Trigger verify iOS client by sending a verification code to the test number. + */ +- (void)verifyIOSClient { + [[AppManager auth].tokenManager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token, + NSError *_Nullable error) { + if (!token) { + [self logFailure:@"Verify iOS client failed." error:error]; + return; + } + FIRVerifyClientRequest *request = + [[FIRVerifyClientRequest alloc] initWithAppToken:token.string + isSandbox:token.type == FIRAuthAPNSTokenTypeSandbox + requestConfiguration:[AppManager auth].requestConfiguration]; + [FIRAuthBackend verifyClient:request callback:^(FIRVerifyClientResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + [self logFailure:@"Verify iOS client failed." error:error]; + return; + } + NSTimeInterval timeout = [response.suggestedTimeOutDate timeIntervalSinceNow]; + [[AppManager auth].appCredentialManager + didStartVerificationWithReceipt:response.receipt + timeout:timeout + callback:^(FIRAuthAppCredential *credential) { + if (!credential.secret) { + [self logFailure:@"Failed to receive remote notification to verify app identity." + error:error]; + return; + } + NSString *testPhoneNumber = @"+16509964692"; + FIRSendVerificationCodeRequest *request = + [[FIRSendVerificationCodeRequest alloc] initWithPhoneNumber:testPhoneNumber + appCredential:credential + reCAPTCHAToken:nil + requestConfiguration:[AppManager auth].requestConfiguration]; + [FIRAuthBackend sendVerificationCode:request + callback:^(FIRSendVerificationCodeResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + [self logFailure:@"Verify iOS client failed." error:error]; + return; + } else { + [self logSuccess:@"Verify iOS client succeeded."]; + [self showMessagePrompt:@"Verify iOS client succeed."]; + } + }]; + }]; + }]; + }]; +} + /** @fn signInWithPhoneNumberWithPrompt - @brief Allows sign in with phone number via popup prompt. + @brief Allows sign in with phone number via popup prompt. */ - (void)signInWithPhoneNumberWithPrompt { [self commonPhoneNumberInputWithTitle:@"Phone #" Completion:^(NSString *_Nullable phone) { - [self signInWithPhoneNumber:phone completion:nil]; - }]; + [self signInWithPhoneNumber:phone completion:nil]; + }]; } /** @fn commonPhoneNumberInputWithLabel:Completion diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj index 7390ffe1d4a..309a9497018 100644 --- a/Example/Firebase.xcodeproj/project.pbxproj +++ b/Example/Firebase.xcodeproj/project.pbxproj @@ -116,6 +116,7 @@ 0672F2F31EBBA7D900818E87 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0672F2F11EBBA7D900818E87 /* GoogleService-Info.plist */; }; 069428831EC3B38C00F7BC69 /* 1mb.dat in Resources */ = {isa = PBXBuildFile; fileRef = 069428801EC3B35A00F7BC69 /* 1mb.dat */; }; 06C24A061EC39BCB005208CA /* FIRStorageIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 06121ECA1EC39A0B0008D70E /* FIRStorageIntegrationTests.m */; }; + 409E1130219FA260000E6CFC /* FIRVerifyIOSClientTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 409E112F219FA260000E6CFC /* FIRVerifyIOSClientTests.m */; }; 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 */; }; @@ -947,6 +948,7 @@ 069428801EC3B35A00F7BC69 /* 1mb.dat */ = {isa = PBXFileReference; lastKnownFileType = file; path = 1mb.dat; sourceTree = ""; }; 0697B1201EC13D8A00542174 /* Base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Base64.h; sourceTree = ""; }; 0697B1211EC13D8A00542174 /* Base64.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Base64.m; sourceTree = ""; }; + 409E112F219FA260000E6CFC /* FIRVerifyIOSClientTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRVerifyIOSClientTests.m; sourceTree = ""; }; 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; @@ -2017,6 +2019,7 @@ isa = PBXGroup; children = ( DE26D1FA1F70333E004AE1D3 /* FirebaseAuthEarlGreyTests.m */, + 409E112F219FA260000E6CFC /* FIRVerifyIOSClientTests.m */, DE26D1FB1F70333E004AE1D3 /* Info.plist */, ); path = EarlGreyTests; @@ -4181,6 +4184,7 @@ buildActionMask = 2147483647; files = ( DE26D2771F705CB5004AE1D3 /* FirebaseAuthEarlGreyTests.m in Sources */, + 409E1130219FA260000E6CFC /* FIRVerifyIOSClientTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -6033,7 +6037,7 @@ "\"${PODS_ROOT}/Headers/Public\"", "\"${PODS_ROOT}/Headers/Public/FirebaseInstanceID\"", "\"${PODS_ROOT}/Headers/Public/GoogleSignIn\"", - "\"${PODS_ROOT}/../../Firebase/Auth/Source\"", + "\"${PODS_ROOT}/../../Firebase/Auth/Source\"/**", ); INFOPLIST_FILE = Auth/Sample/Application.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; @@ -6072,7 +6076,7 @@ "\"${PODS_ROOT}/Headers/Public\"", "\"${PODS_ROOT}/Headers/Public/FirebaseInstanceID\"", "\"${PODS_ROOT}/Headers/Public/GoogleSignIn\"", - "\"${PODS_ROOT}/../../Firebase/Auth/Source\"", + "\"${PODS_ROOT}/../../Firebase/Auth/Source\"/**", ); INFOPLIST_FILE = Auth/Sample/Application.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; diff --git a/Example/Firebase.xcodeproj/xcshareddata/xcschemes/Auth_Sample.xcscheme b/Example/Firebase.xcodeproj/xcshareddata/xcschemes/Auth_Sample.xcscheme index 9404fa79bb4..a0ada521f71 100644 --- a/Example/Firebase.xcodeproj/xcshareddata/xcschemes/Auth_Sample.xcscheme +++ b/Example/Firebase.xcodeproj/xcshareddata/xcschemes/Auth_Sample.xcscheme @@ -26,16 +26,15 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" shouldUseLaunchSchemeArgsEnv = "YES"> @@ -43,9 +42,9 @@ skipped = "NO"> @@ -66,7 +65,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/Firebase/Auth/Source/Public/FIRPhoneAuthProvider.h b/Firebase/Auth/Source/Public/FIRPhoneAuthProvider.h index 6758b266e20..301939e16da 100644 --- a/Firebase/Auth/Source/Public/FIRPhoneAuthProvider.h +++ b/Firebase/Auth/Source/Public/FIRPhoneAuthProvider.h @@ -32,7 +32,6 @@ extern NSString *const FIRPhoneAuthProviderID NS_SWIFT_NAME(PhoneAuthProviderID) */ extern NSString *const _Nonnull FIRPhoneAuthSignInMethod NS_SWIFT_NAME(PhoneAuthSignInMethod); - /** @typedef FIRVerificationResultCallback @brief The type of block invoked when a request to send a verification code has finished. From 382a387296916c7decb4745250f4aa19041d2331 Mon Sep 17 00:00:00 2001 From: Chuan Ren Date: Mon, 19 Nov 2018 11:27:35 -0800 Subject: [PATCH 2/3] Fix indent --- Example/Auth/Sample/MainViewController.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Example/Auth/Sample/MainViewController.m b/Example/Auth/Sample/MainViewController.m index 85c2c0c45bf..4ff1d550f0a 100644 --- a/Example/Auth/Sample/MainViewController.m +++ b/Example/Auth/Sample/MainViewController.m @@ -2957,13 +2957,13 @@ - (void)verifyIOSClient { } /** @fn signInWithPhoneNumberWithPrompt - @brief Allows sign in with phone number via popup prompt. + @brief Allows sign in with phone number via popup prompt. */ - (void)signInWithPhoneNumberWithPrompt { [self commonPhoneNumberInputWithTitle:@"Phone #" Completion:^(NSString *_Nullable phone) { - [self signInWithPhoneNumber:phone completion:nil]; - }]; + [self signInWithPhoneNumber:phone completion:nil]; + }]; } /** @fn commonPhoneNumberInputWithLabel:Completion From 6a559047722dd3aec14beb642cce7a01368d3534 Mon Sep 17 00:00:00 2001 From: Chuan Ren Date: Mon, 19 Nov 2018 15:05:33 -0800 Subject: [PATCH 3/3] Fix pr issues --- .../Auth/EarlGreyTests/FIRVerifyIOSClientTests.m | 5 ++--- .../EarlGreyTests/FirebaseAuthEarlGreyTests.m | 2 +- Example/Auth/Sample/MainViewController.m | 15 ++++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Example/Auth/EarlGreyTests/FIRVerifyIOSClientTests.m b/Example/Auth/EarlGreyTests/FIRVerifyIOSClientTests.m index f3de7c5dbeb..1ccbd17b882 100644 --- a/Example/Auth/EarlGreyTests/FIRVerifyIOSClientTests.m +++ b/Example/Auth/EarlGreyTests/FIRVerifyIOSClientTests.m @@ -16,7 +16,6 @@ #import #import - #import #import "FirebaseAuth.h" @@ -42,7 +41,7 @@ - (void)setUp { [[EarlGrey selectElementWithMatcher:grey_allOf(grey_scrollView(), grey_kindOfClass([UITableView class]), nil)] - performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)]; + performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)]; } #pragma mark - Tests @@ -81,7 +80,7 @@ - (void)waitForElementWithText:(NSString *)text withDelay:(NSTimeInterval)maxDel NSError *error = nil; [[EarlGrey selectElementWithMatcher:grey_text(text)] assertWithMatcher:grey_sufficientlyVisible() - error:&error]; + error:&error]; return !error; }]; GREYAssertTrue([displayed waitWithTimeout:maxDelay], @"Failed to wait for element '%@'.", text); diff --git a/Example/Auth/EarlGreyTests/FirebaseAuthEarlGreyTests.m b/Example/Auth/EarlGreyTests/FirebaseAuthEarlGreyTests.m index df21e47d1e0..d8130fe0a91 100644 --- a/Example/Auth/EarlGreyTests/FirebaseAuthEarlGreyTests.m +++ b/Example/Auth/EarlGreyTests/FirebaseAuthEarlGreyTests.m @@ -52,7 +52,7 @@ - (void)setUp { [[EarlGrey selectElementWithMatcher:grey_allOf(grey_scrollView(), grey_kindOfClass([UITableView class]), nil)] - performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)]; + performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)]; } #pragma mark - Tests diff --git a/Example/Auth/Sample/MainViewController.m b/Example/Auth/Sample/MainViewController.m index 4ff1d550f0a..032b936c201 100644 --- a/Example/Auth/Sample/MainViewController.m +++ b/Example/Auth/Sample/MainViewController.m @@ -2915,9 +2915,9 @@ - (void)verifyIOSClient { return; } FIRVerifyClientRequest *request = - [[FIRVerifyClientRequest alloc] initWithAppToken:token.string - isSandbox:token.type == FIRAuthAPNSTokenTypeSandbox - requestConfiguration:[AppManager auth].requestConfiguration]; + [[FIRVerifyClientRequest alloc] initWithAppToken:token.string + isSandbox:token.type == FIRAuthAPNSTokenTypeSandbox + requestConfiguration:[AppManager auth].requestConfiguration]; [FIRAuthBackend verifyClient:request callback:^(FIRVerifyClientResponse *_Nullable response, NSError *_Nullable error) { if (error) { @@ -2936,10 +2936,11 @@ - (void)verifyIOSClient { } NSString *testPhoneNumber = @"+16509964692"; FIRSendVerificationCodeRequest *request = - [[FIRSendVerificationCodeRequest alloc] initWithPhoneNumber:testPhoneNumber - appCredential:credential - reCAPTCHAToken:nil - requestConfiguration:[AppManager auth].requestConfiguration]; + [[FIRSendVerificationCodeRequest alloc] initWithPhoneNumber:testPhoneNumber + appCredential:credential + reCAPTCHAToken:nil + requestConfiguration: + [AppManager auth].requestConfiguration]; [FIRAuthBackend sendVerificationCode:request callback:^(FIRSendVerificationCodeResponse *_Nullable response, NSError *_Nullable error) {