diff --git a/GoogleSignIn/Sources/GIDVerifyAccountDetail/Implementations/GIDVerifyAccountDetail.m b/GoogleSignIn/Sources/GIDVerifyAccountDetail/Implementations/GIDVerifyAccountDetail.m index 51bacc69..7ff865f4 100644 --- a/GoogleSignIn/Sources/GIDVerifyAccountDetail/Implementations/GIDVerifyAccountDetail.m +++ b/GoogleSignIn/Sources/GIDVerifyAccountDetail/Implementations/GIDVerifyAccountDetail.m @@ -149,8 +149,6 @@ - (void)verifyAccountDetailsInteractivelyWithOptions:(GIDSignInInternalOptions * return; } - [self assertValidCurrentUser]; - // Explicitly throw exception for missing client ID here. This must come before // scheme check because schemes rely on reverse client IDs. [self assertValidParameters:options]; @@ -231,15 +229,6 @@ - (void)processAuthorizationResponse:(OIDAuthorizationResponse *)authorizationRe #pragma mark - Helpers -// Assert that a current user exists. -- (void)assertValidCurrentUser { - if (!GIDSignIn.sharedInstance.currentUser) { - // NOLINTNEXTLINE(google-objc-avoid-throwing-exception) - [NSException raise:NSInvalidArgumentException - format:@"|currentUser| must be set to verify."]; - } -} - // Asserts the parameters being valid. - (void)assertValidParameters:(GIDSignInInternalOptions *)options { if (![options.configuration.clientID length]) { diff --git a/Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayUITests(iOS)/DaysUntilBirthdayUITests_iOS.swift b/Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayUITests(iOS)/DaysUntilBirthdayUITests_iOS.swift index cb3cc1e4..f35f3761 100644 --- a/Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayUITests(iOS)/DaysUntilBirthdayUITests_iOS.swift +++ b/Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayUITests(iOS)/DaysUntilBirthdayUITests_iOS.swift @@ -23,9 +23,11 @@ class DaysUntilBirthdayUITests_iOS: XCTestCase { "Would you like to save this password to use with apps and websites?" private let signInDisclaimerHeaderText = "Sign in to Days Until Birthday" + private let accessHeaderText = "Days Until Birthday wants access to your Google Account" private let additionalAccessHeaderText = "Days Until Birthday wants additional access to your Google Account" private let appTrustWarningText = "Make sure you trust Days Until Birthday" private let chooseAnAccountHeaderText = "Choose an account" + private let consentRequestHeaderText = "Share some info with Days Until Birthday" private let notNowText = "Not Now" private let timeout: TimeInterval = 30 @@ -68,6 +70,27 @@ class DaysUntilBirthdayUITests_iOS: XCTestCase { return XCTFail("Signing out should return user to sign in view") } } + + func testFetchVerificationSignalAndDisconnect() { + sampleApp.launch() + + XCTAssertTrue(navigateToVerifyMyAge()) + XCTAssertTrue(displayVerificationSignal()) + + guard sampleApp + .navigationBars + .buttons["Sign-in with Google"] + .waitForExistence(timeout: timeout) else { + return XCTFail("Failed to show navigation button back to Sign In View") + } + sampleApp.navigationBars.buttons["Sign-in with Google"].tap() + + guard sampleApp + .buttons["GoogleSignInButton"] + .waitForExistence(timeout: timeout) else { + return XCTFail("Failed to return user to sign in view") + } + } } extension DaysUntilBirthdayUITests_iOS { @@ -262,10 +285,90 @@ extension DaysUntilBirthdayUITests_iOS { return true } + /// Navigates to the verification view from the user profile view. + /// - returns: `true` if the navigation was performed successfully. + /// - note: This method will attempt to find a pop up asking for permission to + /// sign in with Google. + func navigateToVerifyMyAge() -> Bool { + guard sampleApp.buttons["Verify"] + .waitForExistence(timeout: timeout) else { + XCTFail("Failed to find button navigating to verify my age view") + return false + } + sampleApp.buttons["Verify"].tap() + + if springboardApp + .staticTexts[signInStaticText] + .waitForExistence(timeout: timeout) { + guard springboardApp + .buttons["Continue"] + .waitForExistence(timeout: timeout) else { + XCTFail("Failed to find 'Continue' button") + return false + } + springboardApp.buttons["Continue"].tap() + + if sampleApp + .staticTexts[Credential.email.rawValue] + .waitForExistence(timeout: timeout) { + XCTAssertTrue(useExistingSignIn()) + } else { + XCTAssertTrue(signInForTheFirstTime()) + } + + handleConsentRequestIfNeeded() + } + + guard sampleApp.staticTexts["Verified Account!"] + .waitForExistence(timeout: timeout) else { + XCTFail("Failed to show age verification view") + return false + } + + guard sampleApp.staticTexts["Access Token:"] + .waitForExistence(timeout: timeout) else { + XCTFail("Access Token element did not appear") + return false + } + + return true + } + + /// Displays a sheet over the Verification view with the fetched verification signal.. + /// - returns: `true` if the display was performed successfully. + func displayVerificationSignal() -> Bool { + guard sampleApp.buttons["Fetch Age Verification Signal"] + .waitForExistence(timeout: timeout) else { + XCTFail("Failed to find button to refresh access token") + return false + } + sampleApp.buttons["Fetch Age Verification Signal"].tap() + + guard sampleApp.staticTexts["User is verified over 18!"] + .waitForExistence(timeout: timeout) else { + XCTFail("Failed to show age verification signal view") + return false + } + + guard sampleApp + .navigationBars + .buttons["Close"] + .waitForExistence(timeout: timeout) else { + XCTFail("Failed to show close button back to age signal view") + return false + } + sampleApp.navigationBars.buttons["Close"].tap() + + return true + } + /// Proceeds through the view with header "Days Until Birthday wants additional access to your Google Account" if needed. func handleAccessRequestIfNeeded() { - let currentlyShowingAdditionalAccessRequest = sampleApp.staticTexts[additionalAccessHeaderText] - .waitForExistence(timeout: timeout) && sampleApp.staticTexts[appTrustWarningText] + let currentlyShowingAdditionalAccessHeaderText = + sampleApp.staticTexts[additionalAccessHeaderText].waitForExistence(timeout: timeout) || + sampleApp.staticTexts[accessHeaderText].waitForExistence(timeout: timeout) + let currentlyShowingAdditionalAccessRequest = + currentlyShowingAdditionalAccessHeaderText && sampleApp.staticTexts[appTrustWarningText] .waitForExistence(timeout: timeout) && sampleApp.buttons["Continue"] .waitForExistence(timeout: timeout) @@ -275,6 +378,17 @@ extension DaysUntilBirthdayUITests_iOS { } } + /// Proceeds through the view with header "Share some info with Days Until Birthday" if needed. + func handleConsentRequestIfNeeded() { + let currentlyShowingConsentRequest = + sampleApp.staticTexts[consentRequestHeaderText].waitForExistence(timeout: timeout) + && sampleApp.buttons["Agree"].waitForExistence(timeout: timeout) + + if currentlyShowingConsentRequest { + sampleApp.buttons["Agree"].tap() + } + } + /// Proceeds through the sign-in disclaimer view if needed. func handleSignInDisclaimerIfNeeded() { let currentlyShowingSignInDisclaimer = sampleApp.staticTexts[signInDisclaimerHeaderText] diff --git a/Samples/Swift/DaysUntilBirthday/Shared/Views/SignInView.swift b/Samples/Swift/DaysUntilBirthday/Shared/Views/SignInView.swift index 5eecffcc..2d7eb0aa 100644 --- a/Samples/Swift/DaysUntilBirthday/Shared/Views/SignInView.swift +++ b/Samples/Swift/DaysUntilBirthday/Shared/Views/SignInView.swift @@ -19,6 +19,10 @@ import GoogleSignInSwift struct SignInView: View { @EnvironmentObject var authViewModel: AuthenticationViewModel +#if os(iOS) + @State var verificationflowInitiated = false + @StateObject var verifiedAgeViewModel = VerifiedAgeViewModel() +#endif @ObservedObject var vm = GoogleSignInButtonViewModel() var body: some View { @@ -69,6 +73,38 @@ struct SignInView: View { #endif } } + HStack { + VStack { +#if os(iOS) + Text("Verify your Age") + .font(.title) + .fontWeight(.semibold) + .frame(maxWidth: .infinity, alignment: .leading) + .padding() + Button(action: { + verifiedAgeViewModel.verifyUserAgeOver18() + verificationflowInitiated = true + }) { + Text("Verify") + .font(.headline) + .foregroundColor(.gray) + .padding() + .frame(maxWidth: .infinity, alignment: .leading) + .background(Color.white) + .overlay( + RoundedRectangle(cornerRadius: 2) + .stroke(Color.gray.opacity(0.3), lineWidth: 1)) + .shadow(color: Color.black.opacity(0.1), radius: 3, x: 0, y: 2) + } + .padding(.horizontal) + NavigationLink(isActive: $verificationflowInitiated) { + VerificationView(verifiedAgeViewModel: verifiedAgeViewModel) + } label: { + EmptyView() + } +#endif + } + } Spacer() } } diff --git a/Samples/Swift/DaysUntilBirthday/iOS/UserProfileView.swift b/Samples/Swift/DaysUntilBirthday/iOS/UserProfileView.swift index 860e2167..93366f47 100644 --- a/Samples/Swift/DaysUntilBirthday/iOS/UserProfileView.swift +++ b/Samples/Swift/DaysUntilBirthday/iOS/UserProfileView.swift @@ -19,9 +19,6 @@ import GoogleSignIn struct UserProfileView: View { @EnvironmentObject var authViewModel: AuthenticationViewModel -#if os(iOS) - @StateObject var verifiedAgeViewModel = VerifiedAgeViewModel() -#endif @StateObject var birthdayViewModel = BirthdayViewModel() private var user: GIDGoogleUser? { return GIDSignIn.sharedInstance.currentUser @@ -53,13 +50,6 @@ struct UserProfileView: View { return } }) -#if os(iOS) - NavigationLink(NSLocalizedString("Verify My Age", comment: "Verify Age"), - destination: VerificationView(verifiedAgeViewModel: verifiedAgeViewModel) - .onAppear { - verifiedAgeViewModel.verifyUserAgeOver18() - }) -#endif Spacer() } .toolbar {