From 6f984ac462e72bb56d7fcef8b9bdc29ce8313683 Mon Sep 17 00:00:00 2001 From: Guilherme Souza Date: Fri, 5 Jul 2024 09:05:22 -0300 Subject: [PATCH] test(auth): update user after anonymous sign in (#433) --- Examples/Examples.xcodeproj/project.pbxproj | 4 + Examples/Examples/Profile/ProfileView.swift | 12 +- .../Examples/Profile/UpdateProfileView.swift | 116 ++++++++++++++++++ .../AuthClientIntegrationTests.swift | 11 ++ 4 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 Examples/Examples/Profile/UpdateProfileView.swift diff --git a/Examples/Examples.xcodeproj/project.pbxproj b/Examples/Examples.xcodeproj/project.pbxproj index 47f0f4ed..eb1c1c6d 100644 --- a/Examples/Examples.xcodeproj/project.pbxproj +++ b/Examples/Examples.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ 79AF04862B2CE586008761AD /* Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79AF04852B2CE586008761AD /* Debug.swift */; }; 79B1C80C2BABFF8000D991AA /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79B1C80B2BABFF8000D991AA /* ProfileView.swift */; }; 79B1C80E2BAC017C00D991AA /* AnyJSONView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79B1C80D2BAC017C00D991AA /* AnyJSONView.swift */; }; + 79B3261C2BF359A50023661C /* UpdateProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79B3261B2BF359A50023661C /* UpdateProfileView.swift */; }; 79B8F4242B5FED7C0000E839 /* IdentifiedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = 79B8F4232B5FED7C0000E839 /* IdentifiedCollections */; }; 79B8F4262B602F640000E839 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79B8F4252B602F640000E839 /* Logger.swift */; }; 79BD76772B59C3E300CA3D68 /* UserStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79BD76762B59C3E300CA3D68 /* UserStore.swift */; }; @@ -111,6 +112,7 @@ 79AF04852B2CE586008761AD /* Debug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debug.swift; sourceTree = ""; }; 79B1C80B2BABFF8000D991AA /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = ""; }; 79B1C80D2BAC017C00D991AA /* AnyJSONView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyJSONView.swift; sourceTree = ""; }; + 79B3261B2BF359A50023661C /* UpdateProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateProfileView.swift; sourceTree = ""; }; 79B8F4252B602F640000E839 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; 79BD76762B59C3E300CA3D68 /* UserStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserStore.swift; sourceTree = ""; }; 79BD76782B59C53900CA3D68 /* ChannelStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelStore.swift; sourceTree = ""; }; @@ -273,6 +275,7 @@ children = ( 79B1C80B2BABFF8000D991AA /* ProfileView.swift */, 794C61D52BAD1E12000E6B0F /* UserIdentityList.swift */, + 79B3261B2BF359A50023661C /* UpdateProfileView.swift */, ); path = Profile; sourceTree = ""; @@ -498,6 +501,7 @@ 793895CC2954ABFF0044F2B8 /* RootView.swift in Sources */, 7956406A2955AFBD0088A06F /* ErrorText.swift in Sources */, 79AF04812B2CE261008761AD /* AuthView.swift in Sources */, + 79B3261C2BF359A50023661C /* UpdateProfileView.swift in Sources */, 794EF1242955F3DE008C9526 /* TodoListRow.swift in Sources */, 797EFB662BABD82A00098D6B /* BucketList.swift in Sources */, 79E2B55C2B97A2310042CD21 /* UIApplicationExtensions.swift in Sources */, diff --git a/Examples/Examples/Profile/ProfileView.swift b/Examples/Examples/Profile/ProfileView.swift index f418faa2..e70bf089 100644 --- a/Examples/Examples/Profile/ProfileView.swift +++ b/Examples/Examples/Profile/ProfileView.swift @@ -18,9 +18,17 @@ struct ProfileView: View { var body: some View { NavigationStack { List { - if let user = user.flatMap({ try? AnyJSON($0) }) { + if let user, + let json = try? AnyJSON(user) { Section { - AnyJSONView(value: user) + AnyJSONView(value: json) + } + } + + if let user { + NavigationLink("Update profile") { + UpdateProfileView(user: user) + .navigationTitle("Update profile") } } diff --git a/Examples/Examples/Profile/UpdateProfileView.swift b/Examples/Examples/Profile/UpdateProfileView.swift new file mode 100644 index 00000000..f55caec4 --- /dev/null +++ b/Examples/Examples/Profile/UpdateProfileView.swift @@ -0,0 +1,116 @@ +// +// UpdateProfileView.swift +// Examples +// +// Created by Guilherme Souza on 14/05/24. +// + +import SwiftUI +import Supabase + +struct UpdateProfileView: View { + let user: User + + @State var email = "" + @State var phone = "" + + @State var otp = "" + @State var showTokenField = false + + var formUpdated: Bool { + emailChanged || phoneChanged + } + + var emailChanged: Bool { + email != user.email + } + + var phoneChanged: Bool { + phone != user.phone + } + + var body: some View { + Form { + Section { + TextField("Email", text: $email) + .textContentType(.emailAddress) + .keyboardType(.emailAddress) + .autocorrectionDisabled() + .textInputAutocapitalization(.never) + TextField("Phone", text: $phone) + .textContentType(.telephoneNumber) + .keyboardType(.phonePad) + .autocorrectionDisabled() + .textInputAutocapitalization(.never) + } + + Section { + Button("Update") { + Task { + await updateButtonTapped() + } + } + .disabled(!formUpdated) + } + + if showTokenField { + Section { + TextField("OTP", text: $otp) + Button("Verify") { + Task { + await verifyTapped() + } + } + } + } + } + .onAppear { + email = user.email ?? "" + phone = user.phone ?? "" + } + } + + @MainActor + private func updateButtonTapped() async { + var attributes = UserAttributes() + if emailChanged { + attributes.email = email + } + + if phoneChanged { + attributes.phone = phone + } + + do { + try await supabase.auth.update(user: attributes, redirectTo: Constants.redirectToURL) + + if phoneChanged { + showTokenField = true + } + } catch { + debug("Fail to update user: \(error)") + } + } + + @MainActor + private func verifyTapped() async { + do { + try await supabase.auth.verifyOTP(phone: phone, token: otp, type: .phoneChange) + } catch { + debug("Fail to verify OTP: \(error)") + } + } +} + +#Preview { + UpdateProfileView( + user: User( + id: UUID(), + appMetadata: [:], + userMetadata: [:], + aud: "", + createdAt: Date(), + updatedAt: Date() + ) + ) +} diff --git a/Tests/IntegrationTests/AuthClientIntegrationTests.swift b/Tests/IntegrationTests/AuthClientIntegrationTests.swift index 3603d182..9aac7d6c 100644 --- a/Tests/IntegrationTests/AuthClientIntegrationTests.swift +++ b/Tests/IntegrationTests/AuthClientIntegrationTests.swift @@ -193,6 +193,17 @@ final class AuthClientIntegrationTests: XCTestCase { } } + func testSignInAnonymousAndLinkUserWithEmail() async throws { + try await XCTAssertAuthChangeEvents([.initialSession, .signedIn, .userUpdated]) { + try await authClient.signInAnonymously() + + let email = mockEmail() + let user = try await authClient.update(user: UserAttributes(email: email)) + + XCTAssertEqual(user.newEmail, email) + } + } + func testDeleteAccountAndSignOut() async throws { let response = try await signUpIfNeededOrSignIn(email: mockEmail(), password: mockPassword())