From a0d76aea59632c4a285c2a8d424d531b3fe08d10 Mon Sep 17 00:00:00 2001 From: brianna Date: Wed, 9 Apr 2025 15:23:52 -0700 Subject: [PATCH 1/3] Remove current user requirement for VwG flow. --- .../Implementations/GIDVerifyAccountDetail.m | 11 ----------- 1 file changed, 11 deletions(-) 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]) { From 18a188eacd2da9f8d8950d91c460ebe67ff5c528 Mon Sep 17 00:00:00 2001 From: brianna Date: Wed, 9 Apr 2025 17:48:12 -0700 Subject: [PATCH 2/3] Add integration test to DaysUntilBirthday for VwG. --- .../DaysUntilBirthdayUITests_iOS.swift | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayUITests(iOS)/DaysUntilBirthdayUITests_iOS.swift b/Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayUITests(iOS)/DaysUntilBirthdayUITests_iOS.swift index cb3cc1e4..bb6f7242 100644 --- a/Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayUITests(iOS)/DaysUntilBirthdayUITests_iOS.swift +++ b/Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayUITests(iOS)/DaysUntilBirthdayUITests_iOS.swift @@ -26,6 +26,7 @@ class DaysUntilBirthdayUITests_iOS: XCTestCase { 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 +69,23 @@ class DaysUntilBirthdayUITests_iOS: XCTestCase { return XCTFail("Signing out should return user to sign in view") } } + + func testFetchVerificationSignalAndDisconnect() { + sampleApp.launch() + + XCTAssertTrue(signIn()) + XCTAssertTrue(navigateToVerifyMyAge()) + XCTAssertTrue(displayVerificationSignal()) + XCTAssertTrue(navigateBackToUserProfileView()) + + sampleApp.navigationBars.buttons["Disconnect"].tap() + + guard sampleApp + .buttons["GoogleSignInButton"] + .waitForExistence(timeout: timeout) else { + return XCTFail("Disconnecting should return user to sign in view") + } + } } extension DaysUntilBirthdayUITests_iOS { @@ -262,6 +280,84 @@ 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 My Age"] + .waitForExistence(timeout: timeout) else { + XCTFail("Failed to find button navigating to verify my age view") + return false + } + sampleApp.buttons["Verify My Age"].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[chooseAnAccountHeaderText] + .waitForExistence(timeout: timeout) { + guard findAndTapExistingSignedInEmail() else { + XCTFail("Failed to find signed-in account") + return false + } + } + + 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] @@ -275,6 +371,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] From 1086a253a785892a9abe28a7204521f412cfb15e Mon Sep 17 00:00:00 2001 From: brianna Date: Fri, 11 Apr 2025 09:12:37 -0700 Subject: [PATCH 3/3] Add verify button to Sign In view of sample app. --- .../DaysUntilBirthdayUITests_iOS.swift | 49 +++++++++++-------- .../Shared/Views/SignInView.swift | 36 ++++++++++++++ .../iOS/UserProfileView.swift | 10 ---- 3 files changed, 64 insertions(+), 31 deletions(-) diff --git a/Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayUITests(iOS)/DaysUntilBirthdayUITests_iOS.swift b/Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayUITests(iOS)/DaysUntilBirthdayUITests_iOS.swift index bb6f7242..f35f3761 100644 --- a/Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayUITests(iOS)/DaysUntilBirthdayUITests_iOS.swift +++ b/Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayUITests(iOS)/DaysUntilBirthdayUITests_iOS.swift @@ -23,6 +23,7 @@ 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" @@ -73,17 +74,21 @@ class DaysUntilBirthdayUITests_iOS: XCTestCase { func testFetchVerificationSignalAndDisconnect() { sampleApp.launch() - XCTAssertTrue(signIn()) XCTAssertTrue(navigateToVerifyMyAge()) XCTAssertTrue(displayVerificationSignal()) - XCTAssertTrue(navigateBackToUserProfileView()) - sampleApp.navigationBars.buttons["Disconnect"].tap() + 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("Disconnecting should return user to sign in view") + return XCTFail("Failed to return user to sign in view") } } } @@ -285,12 +290,12 @@ extension DaysUntilBirthdayUITests_iOS { /// - 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 My Age"] + guard sampleApp.buttons["Verify"] .waitForExistence(timeout: timeout) else { XCTFail("Failed to find button navigating to verify my age view") return false } - sampleApp.buttons["Verify My Age"].tap() + sampleApp.buttons["Verify"].tap() if springboardApp .staticTexts[signInStaticText] @@ -304,12 +309,11 @@ extension DaysUntilBirthdayUITests_iOS { springboardApp.buttons["Continue"].tap() if sampleApp - .staticTexts[chooseAnAccountHeaderText] - .waitForExistence(timeout: timeout) { - guard findAndTapExistingSignedInEmail() else { - XCTFail("Failed to find signed-in account") - return false - } + .staticTexts[Credential.email.rawValue] + .waitForExistence(timeout: timeout) { + XCTAssertTrue(useExistingSignIn()) + } else { + XCTAssertTrue(signInForTheFirstTime()) } handleConsentRequestIfNeeded() @@ -360,8 +364,11 @@ extension DaysUntilBirthdayUITests_iOS { /// 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) @@ -372,15 +379,15 @@ 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) + func handleConsentRequestIfNeeded() { + let currentlyShowingConsentRequest = + sampleApp.staticTexts[consentRequestHeaderText].waitForExistence(timeout: timeout) + && sampleApp.buttons["Agree"].waitForExistence(timeout: timeout) - if currentlyShowingConsentRequest { - sampleApp.buttons["Agree"].tap() - } + if currentlyShowingConsentRequest { + sampleApp.buttons["Agree"].tap() } + } /// Proceeds through the sign-in disclaimer view if needed. func handleSignInDisclaimerIfNeeded() { 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 {