From db46d60f85aa9d77d247c1f59a30a7b9f7909f82 Mon Sep 17 00:00:00 2001 From: Andreas Bauer Date: Tue, 21 Nov 2023 10:35:58 -0800 Subject: [PATCH] Rework FirestoreAccountStorage implementation (#25) # Rework FirestoreAccountStorage implementation ## :recycle: Current situation & Problem This PR fixes several issues identified within #24. As it turns out, we completely forgot to add testing for this component. This follow-up PR addresses some of the issues and adds proper UI testing. ## :gear: Release Notes * Fixes several issues with the `FirestoreAccountStorage` module ## :books: Documentation -- ## :white_check_mark: Testing We added testing for the respective components. ## :pencil: Code of Conduct & Contributing Guidelines By submitting creating this pull request, you agree to follow our [Code of Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md): - [x] I agree to follow the [Code of Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md). --- Package.swift | 2 +- .../FirebaseAccountService.swift | 11 +-- .../FirestoreAccountStorage.swift | 7 +- .../Visitor/FirestoreEncodeVisitor.swift | 20 ++++- .../AccountStorageTestStandard.swift | 42 +++++++++ .../FirebaseAccountStorage/BiographyKey.swift | 53 ++++++++++++ .../UITests/TestApp/Shared/FeatureFlags.swift | 14 +++ .../TestApp/Shared/TestAppDelegate.swift | 34 ++++++-- .../FirebaseAccountStorageTests.swift | 86 +++++++++++++++++++ .../TestAppUITests/FirebaseAccountTests.swift | 29 ++----- .../UITests/UITests.xcodeproj/project.pbxproj | 35 +++++++- .../xcshareddata/swiftpm/Package.resolved | 4 +- .../xcshareddata/xcschemes/TestApp.xcscheme | 54 +++++++++--- 13 files changed, 335 insertions(+), 56 deletions(-) create mode 100644 Tests/UITests/TestApp/FirebaseAccountStorage/AccountStorageTestStandard.swift create mode 100644 Tests/UITests/TestApp/FirebaseAccountStorage/BiographyKey.swift create mode 100644 Tests/UITests/TestApp/Shared/FeatureFlags.swift create mode 100644 Tests/UITests/TestAppUITests/FirebaseAccountStorageTests.swift diff --git a/Package.swift b/Package.swift index ecdf938..3cd4d15 100644 --- a/Package.swift +++ b/Package.swift @@ -27,7 +27,7 @@ let package = Package( dependencies: [ .package(url: "https://github.com/StanfordSpezi/Spezi", .upToNextMinor(from: "0.8.0")), .package(url: "https://github.com/StanfordSpezi/SpeziViews.git", .upToNextMinor(from: "0.6.1")), - .package(url: "https://github.com/StanfordSpezi/SpeziAccount", .upToNextMinor(from: "0.8.0")), + .package(url: "https://github.com/StanfordSpezi/SpeziAccount", .upToNextMinor(from: "0.8.1")), .package(url: "https://github.com/StanfordSpezi/SpeziStorage", .upToNextMinor(from: "0.5.0")), .package(url: "https://github.com/firebase/firebase-ios-sdk", from: "10.13.0") ], diff --git a/Sources/SpeziFirebaseAccount/Account Services/FirebaseAccountService.swift b/Sources/SpeziFirebaseAccount/Account Services/FirebaseAccountService.swift index c9e06fb..b5ffc5f 100644 --- a/Sources/SpeziFirebaseAccount/Account Services/FirebaseAccountService.swift +++ b/Sources/SpeziFirebaseAccount/Account Services/FirebaseAccountService.swift @@ -92,8 +92,6 @@ extension FirebaseAccountService { throw FirebaseAccountError.notSignedIn } - var changes = false - do { // if we modify sensitive credentials and require a recent login if modifications.modifiedDetails.storage[UserIdKey.self] != nil || modifications.modifiedDetails.password != nil { @@ -107,7 +105,6 @@ extension FirebaseAccountService { if let userId = modifications.modifiedDetails.storage[UserIdKey.self] { Self.logger.debug("updateEmail(to:) for user.") try await currentUser.updateEmail(to: userId) - changes = true } if let password = modifications.modifiedDetails.password { @@ -120,14 +117,10 @@ extension FirebaseAccountService { let changeRequest = currentUser.createProfileChangeRequest() changeRequest.displayName = name.formatted(.name(style: .long)) try await changeRequest.commitChanges() - - changes = true } - if changes { - // non of the above request will trigger our state change listener, therefore just call it manually. - try await context.notifyUserSignIn(user: currentUser, for: self) - } + // None of the above requests will trigger our state change listener, therefore, we just call it manually. + try await context.notifyUserSignIn(user: currentUser, for: self) } catch let error as NSError { Self.logger.error("Received NSError on firebase dispatch: \(error)") throw FirebaseAccountError(authErrorCode: AuthErrorCode(_nsError: error)) diff --git a/Sources/SpeziFirebaseAccountStorage/FirestoreAccountStorage.swift b/Sources/SpeziFirebaseAccountStorage/FirestoreAccountStorage.swift index 97e432a..1352c0c 100644 --- a/Sources/SpeziFirebaseAccountStorage/FirestoreAccountStorage.swift +++ b/Sources/SpeziFirebaseAccountStorage/FirestoreAccountStorage.swift @@ -72,15 +72,16 @@ import SpeziFirestore public actor FirestoreAccountStorage: Module, AccountStorageStandard { @Dependency private var firestore: SpeziFirestore.Firestore // ensure firestore is configured - private let collection: CollectionReference + private let collection: () -> CollectionReference - public init(storeIn collection: CollectionReference) { + public init(storeIn collection: @Sendable @autoclosure @escaping () -> CollectionReference) { self.collection = collection } + private func userDocument(for accountId: String) -> DocumentReference { - collection.document(accountId) + collection().document(accountId) } public func create(_ identifier: AdditionalRecordId, _ details: SignupDetails) async throws { diff --git a/Sources/SpeziFirebaseAccountStorage/Visitor/FirestoreEncodeVisitor.swift b/Sources/SpeziFirebaseAccountStorage/Visitor/FirestoreEncodeVisitor.swift index 38ef4b8..0bf292e 100644 --- a/Sources/SpeziFirebaseAccountStorage/Visitor/FirestoreEncodeVisitor.swift +++ b/Sources/SpeziFirebaseAccountStorage/Visitor/FirestoreEncodeVisitor.swift @@ -8,12 +8,20 @@ import FirebaseFirestore +import OSLog import SpeziAccount +private struct SingleKeyContainer: Codable { + let value: Value +} + + class FirestoreEncodeVisitor: AccountValueVisitor { typealias Data = [String: Any] + private let logger = Logger(subsystem: "edu.stanford.spezi.firebase", category: "FirestoreEncode") + private var values: Data = [:] private var errors: [String: Error] = [:] @@ -21,9 +29,19 @@ class FirestoreEncodeVisitor: AccountValueVisitor { func visit(_ key: Key.Type, _ value: Key.Value) { let encoder = Firestore.Encoder() + + // the firestore encode method expects a container type! + let container = SingleKeyContainer(value: value) + do { - values["\(Key.self)"] = try encoder.encode(value) + let result = try encoder.encode(container) + guard let encoded = result["value"] else { + preconditionFailure("Layout of SingleKeyContainer changed. Does not contain value anymore: \(result)") + } + + values["\(Key.self)"] = encoded } catch { + logger.error("Failed to encode \("\(value)") for key \(key): \(error)") errors["\(Key.self)"] = error } } diff --git a/Tests/UITests/TestApp/FirebaseAccountStorage/AccountStorageTestStandard.swift b/Tests/UITests/TestApp/FirebaseAccountStorage/AccountStorageTestStandard.swift new file mode 100644 index 0000000..c072798 --- /dev/null +++ b/Tests/UITests/TestApp/FirebaseAccountStorage/AccountStorageTestStandard.swift @@ -0,0 +1,42 @@ +// +// This source file is part of the Stanford Spezi open-source project +// +// SPDX-FileCopyrightText: 2022 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import FirebaseFirestore +import Spezi +import SpeziAccount +import SpeziFirebaseAccountStorage + + +actor AccountStorageTestStandard: Standard, AccountStorageStandard { + static var collection: CollectionReference { + Firestore.firestore().collection("users") + } + + @Dependency var storage = FirestoreAccountStorage(storeIn: collection) + + + func create(_ identifier: AdditionalRecordId, _ details: SignupDetails) async throws { + try await storage.create(identifier, details) + } + + func load(_ identifier: AdditionalRecordId, _ keys: [any AccountKey.Type]) async throws -> PartialAccountDetails { + try await storage.load(identifier, keys) + } + + func modify(_ identifier: AdditionalRecordId, _ modifications: SpeziAccount.AccountModifications) async throws { + try await storage.modify(identifier, modifications) + } + + func clear(_ identifier: AdditionalRecordId) async { + await storage.clear(identifier) + } + + func delete(_ identifier: AdditionalRecordId) async throws { + try await storage.delete(identifier) + } +} diff --git a/Tests/UITests/TestApp/FirebaseAccountStorage/BiographyKey.swift b/Tests/UITests/TestApp/FirebaseAccountStorage/BiographyKey.swift new file mode 100644 index 0000000..d9353c8 --- /dev/null +++ b/Tests/UITests/TestApp/FirebaseAccountStorage/BiographyKey.swift @@ -0,0 +1,53 @@ +// +// This source file is part of the Spezi open-source project +// +// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import SpeziAccount +import SpeziValidation +import SwiftUI + + +struct BiographyKey: AccountKey { + typealias Value = String + + + static let name: LocalizedStringResource = "Biography" // we don't bother to translate + static let category: AccountKeyCategory = .personalDetails +} + + +extension AccountKeys { + var biography: BiographyKey.Type { + BiographyKey.self + } +} + + +extension AccountValues { + var biography: String? { + storage[BiographyKey.self] + } +} + + +extension BiographyKey { + public struct DataEntry: DataEntryView { + public typealias Key = BiographyKey + + + @Binding private var biography: Value + + public init(_ value: Binding) { + self._biography = value + } + + public var body: some View { + VerifiableTextField(Key.name, text: $biography) + .autocorrectionDisabled() + } + } +} diff --git a/Tests/UITests/TestApp/Shared/FeatureFlags.swift b/Tests/UITests/TestApp/Shared/FeatureFlags.swift new file mode 100644 index 0000000..284913d --- /dev/null +++ b/Tests/UITests/TestApp/Shared/FeatureFlags.swift @@ -0,0 +1,14 @@ +// +// This source file is part of the Stanford Spezi open-source project +// +// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import Foundation + + +enum FeatureFlags { + static let accountStorageTests = ProcessInfo.processInfo.arguments.contains("--account-storage") +} diff --git a/Tests/UITests/TestApp/Shared/TestAppDelegate.swift b/Tests/UITests/TestApp/Shared/TestAppDelegate.swift index 340fb17..ea6e81c 100644 --- a/Tests/UITests/TestApp/Shared/TestAppDelegate.swift +++ b/Tests/UITests/TestApp/Shared/TestAppDelegate.swift @@ -16,17 +16,37 @@ import SwiftUI class TestAppDelegate: SpeziAppDelegate { override var configuration: Configuration { - Configuration { + if FeatureFlags.accountStorageTests { + return Configuration(standard: AccountStorageTestStandard(), configurationsClosure) + } else { + return Configuration(configurationsClosure) + } + } + + var configurationsClosure: () -> ModuleCollection { + { + self.configurations + } + } + + @ModuleBuilder var configurations: ModuleCollection { + if FeatureFlags.accountStorageTests { + AccountConfiguration(configuration: [ + .requires(\.userId), + .requires(\.name), + .requires(\.biography) + ]) + } else { AccountConfiguration(configuration: [ .requires(\.userId), .collects(\.name) ]) - Firestore(settings: .emulator) - FirebaseAccountConfiguration( - authenticationMethods: [.emailAndPassword, .signInWithApple], - emulatorSettings: (host: "localhost", port: 9099) - ) - FirebaseStorageConfiguration(emulatorSettings: (host: "localhost", port: 9199)) } + Firestore(settings: .emulator) + FirebaseAccountConfiguration( + authenticationMethods: [.emailAndPassword, .signInWithApple], + emulatorSettings: (host: "localhost", port: 9099) + ) + FirebaseStorageConfiguration(emulatorSettings: (host: "localhost", port: 9199)) } } diff --git a/Tests/UITests/TestAppUITests/FirebaseAccountStorageTests.swift b/Tests/UITests/TestAppUITests/FirebaseAccountStorageTests.swift new file mode 100644 index 0000000..6aa380a --- /dev/null +++ b/Tests/UITests/TestAppUITests/FirebaseAccountStorageTests.swift @@ -0,0 +1,86 @@ +// +// This source file is part of the Stanford Spezi open-source project +// +// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import XCTest +import XCTestExtensions + + +final class FirebaseAccountStorageTests: XCTestCase { + @MainActor + override func setUp() async throws { + try await super.setUp() + + continueAfterFailure = false + + try disablePasswordAutofill() + + try await FirebaseClient.deleteAllAccounts() + try await Task.sleep(for: .seconds(0.5)) + } + + @MainActor + func testAdditionalAccountStorage() async throws { + let app = XCUIApplication() + app.launchArguments = ["--account-storage"] + app.launch() + + XCTAssert(app.buttons["FirebaseAccount"].waitForExistence(timeout: 10.0)) + app.buttons["FirebaseAccount"].tap() + + if app.buttons["Logout"].waitForExistence(timeout: 5.0) && app.buttons["Logout"].isHittable { + app.buttons["Logout"].tap() + } + + + try app.signup( + username: "test@username1.edu", + password: "TestPassword1", + givenName: "Test1", + familyName: "Username1", + biography: "Hello Stanford" + ) + + + XCTAssertTrue(app.buttons["Account Overview"].waitForExistence(timeout: 2.0)) + app.buttons["Account Overview"].tap() + XCTAssertTrue(app.staticTexts["Biography, Hello Stanford"].waitForExistence(timeout: 2.0)) + + + // TEST ACCOUNT EDIT + XCTAssertTrue(app.navigationBars.buttons["Edit"].exists) + app.navigationBars.buttons["Edit"].tap() + + try app.textFields["Biography"].enter(value: "2") + + XCTAssertTrue(app.navigationBars.buttons["Done"].exists) + app.navigationBars.buttons["Done"].tap() + + XCTAssertTrue(app.staticTexts["Biography, Hello Stanford2"].waitForExistence(timeout: 2.0)) + + // TEST ACCOUNT DELETION + XCTAssertTrue(app.navigationBars.buttons["Edit"].exists) + app.navigationBars.buttons["Edit"].tap() + + XCTAssertTrue(app.buttons["Delete Account"].waitForExistence(timeout: 4.0)) + app.buttons["Delete Account"].tap() + + let alert = "Are you sure you want to delete your account?" + XCTAssertTrue(XCUIApplication().alerts[alert].waitForExistence(timeout: 6.0)) + XCUIApplication().alerts[alert].scrollViews.otherElements.buttons["Delete"].tap() + + XCTAssertTrue(app.alerts["Authentication Required"].waitForExistence(timeout: 2.0)) + XCTAssertTrue(app.alerts["Authentication Required"].secureTextFields["Password"].waitForExistence(timeout: 0.5)) + app.typeText("TestPassword1") // the password field has focus already + XCTAssertTrue(app.alerts["Authentication Required"].buttons["Login"].waitForExistence(timeout: 0.5)) + app.alerts["Authentication Required"].buttons["Login"].tap() + + sleep(2) + let accountsNew = try await FirebaseClient.getAllAccounts() + XCTAssertTrue(accountsNew.isEmpty) + } +} diff --git a/Tests/UITests/TestAppUITests/FirebaseAccountTests.swift b/Tests/UITests/TestAppUITests/FirebaseAccountTests.swift index c640289..31a5388 100644 --- a/Tests/UITests/TestAppUITests/FirebaseAccountTests.swift +++ b/Tests/UITests/TestAppUITests/FirebaseAccountTests.swift @@ -451,23 +451,12 @@ final class FirebaseAccountTests: XCTestCase { // swiftlint:disable:this type_bo extension XCUIApplication { - func extendedDismissKeyboard() { - let keyboard = keyboards.firstMatch - - if keyboard.waitForExistence(timeout: 1.0) && keyboard.buttons["Done"].isHittable { - keyboard.buttons["Done"].tap() - } - } - - fileprivate func login(username: String, password: String, close: Bool = true) throws { + func login(username: String, password: String, close: Bool = true) throws { buttons["Account Setup"].tap() XCTAssertTrue(self.buttons["Login"].waitForExistence(timeout: 2.0)) try textFields["E-Mail Address"].enter(value: username) - extendedDismissKeyboard() - try secureTextFields["Password"].enter(value: password) - extendedDismissKeyboard() swipeUp() @@ -478,9 +467,8 @@ extension XCUIApplication { self.buttons["Close"].tap() } } - - - fileprivate func signup(username: String, password: String, givenName: String, familyName: String) throws { + + func signup(username: String, password: String, givenName: String, familyName: String, biography: String? = nil) throws { buttons["Account Setup"].tap() buttons["Signup"].tap() @@ -488,24 +476,23 @@ extension XCUIApplication { sleep(2) try collectionViews.textFields["E-Mail Address"].enter(value: username) - extendedDismissKeyboard() - try collectionViews.secureTextFields["Password"].enter(value: password) - extendedDismissKeyboard() swipeUp() try textFields["enter first name"].enter(value: givenName) - extendedDismissKeyboard() swipeUp() try textFields["enter last name"].enter(value: familyName) - extendedDismissKeyboard() swipeUp() + if let biography { + try textFields["Biography"].enter(value: biography) + } + collectionViews.buttons["Signup"].tap() sleep(3) buttons["Close"].tap() } -} // swiftlint:disable:this file_length +} diff --git a/Tests/UITests/UITests.xcodeproj/project.pbxproj b/Tests/UITests/UITests.xcodeproj/project.pbxproj index 1d2732e..0b4d073 100644 --- a/Tests/UITests/UITests.xcodeproj/project.pbxproj +++ b/Tests/UITests/UITests.xcodeproj/project.pbxproj @@ -25,6 +25,11 @@ 978DFE922ADB1E1600E2B9B5 /* SpeziFirebaseStorage in Frameworks */ = {isa = PBXBuildFile; productRef = 978DFE912ADB1E1600E2B9B5 /* SpeziFirebaseStorage */; }; 978E198E2ADB40A300732324 /* FirebaseStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 978E198D2ADB40A300732324 /* FirebaseStorageTests.swift */; }; A95D60D02AA35E2200EB5968 /* FirebaseClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95D60CF2AA35E2200EB5968 /* FirebaseClient.swift */; }; + A9D83F992B0BDB13000D0C78 /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D83F982B0BDB13000D0C78 /* FeatureFlags.swift */; }; + A9D83F9B2B0BDB1D000D0C78 /* BiographyKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D83F9A2B0BDB1D000D0C78 /* BiographyKey.swift */; }; + A9D83F9D2B0BDB3A000D0C78 /* FirebaseAccountStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D83F9C2B0BDB3A000D0C78 /* FirebaseAccountStorageTests.swift */; }; + A9D83F9F2B0BDCC7000D0C78 /* SpeziFirebaseAccountStorage in Frameworks */ = {isa = PBXBuildFile; productRef = A9D83F9E2B0BDCC7000D0C78 /* SpeziFirebaseAccountStorage */; }; + A9D83FA22B0BE048000D0C78 /* AccountStorageTestStandard.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D83FA12B0BE048000D0C78 /* AccountStorageTestStandard.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -58,6 +63,10 @@ 97359F652ADB286D0080CB11 /* StorageMetadata+Sendable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StorageMetadata+Sendable.swift"; sourceTree = ""; }; 978E198D2ADB40A300732324 /* FirebaseStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirebaseStorageTests.swift; sourceTree = ""; }; A95D60CF2AA35E2200EB5968 /* FirebaseClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirebaseClient.swift; sourceTree = ""; }; + A9D83F982B0BDB13000D0C78 /* FeatureFlags.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeatureFlags.swift; sourceTree = ""; }; + A9D83F9A2B0BDB1D000D0C78 /* BiographyKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BiographyKey.swift; sourceTree = ""; }; + A9D83F9C2B0BDB3A000D0C78 /* FirebaseAccountStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FirebaseAccountStorageTests.swift; sourceTree = ""; }; + A9D83FA12B0BE048000D0C78 /* AccountStorageTestStandard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountStorageTestStandard.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -65,6 +74,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + A9D83F9F2B0BDCC7000D0C78 /* SpeziFirebaseAccountStorage in Frameworks */, 2FB07595299DF96E00C0B37F /* SpeziFirebaseConfiguration in Frameworks */, 2FB07597299DF96E00C0B37F /* SpeziFirestore in Frameworks */, 978DFE922ADB1E1600E2B9B5 /* SpeziFirebaseStorage in Frameworks */, @@ -118,6 +128,7 @@ 2FA43E8E2AE022A4009B1B2C /* TestApp.entitlements */, 2FA7382B290ADFAA007ACEB9 /* TestApp.swift */, 2F148BFE298BB14100031B7F /* FirebaseAccountTests */, + A9D83FA02B0BE01F000D0C78 /* FirebaseAccountStorage */, 2FE62C3B2966071400FCBE7F /* FirestoreDataStorageTests */, 97359F622ADB27430080CB11 /* FirebaseStorageTests */, 2F9F07ED29090AF500CDC598 /* Shared */, @@ -131,11 +142,12 @@ 2F6D13AF28F5F386007C25D6 /* TestAppUITests */ = { isa = PBXGroup; children = ( - 2FB926E42974B0FC008E7B03 /* Info.plist */, + A9D83F9C2B0BDB3A000D0C78 /* FirebaseAccountStorageTests.swift */, 2F8CE160298C2C6D003799A8 /* FirebaseAccountTests.swift */, - 2F2E4B8329749C5900FF710F /* FirestoreDataStorageTests.swift */, A95D60CF2AA35E2200EB5968 /* FirebaseClient.swift */, 978E198D2ADB40A300732324 /* FirebaseStorageTests.swift */, + 2F2E4B8329749C5900FF710F /* FirestoreDataStorageTests.swift */, + 2FB926E42974B0FC008E7B03 /* Info.plist */, ); path = TestAppUITests; sourceTree = ""; @@ -150,6 +162,7 @@ 2F9F07ED29090AF500CDC598 /* Shared */ = { isa = PBXGroup; children = ( + A9D83F982B0BDB13000D0C78 /* FeatureFlags.swift */, 2F8A431629130BBC005D2B8F /* TestAppType.swift */, 2F9F07F029090B0500CDC598 /* TestAppDelegate.swift */, ); @@ -173,6 +186,15 @@ path = FirebaseStorageTests; sourceTree = ""; }; + A9D83FA02B0BE01F000D0C78 /* FirebaseAccountStorage */ = { + isa = PBXGroup; + children = ( + A9D83F9A2B0BDB1D000D0C78 /* BiographyKey.swift */, + A9D83FA12B0BE048000D0C78 /* AccountStorageTestStandard.swift */, + ); + path = FirebaseAccountStorage; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -194,6 +216,7 @@ 2FB07594299DF96E00C0B37F /* SpeziFirebaseConfiguration */, 2FB07596299DF96E00C0B37F /* SpeziFirestore */, 978DFE912ADB1E1600E2B9B5 /* SpeziFirebaseStorage */, + A9D83F9E2B0BDCC7000D0C78 /* SpeziFirebaseAccountStorage */, ); productName = Example; productReference = 2F6D139228F5F384007C25D6 /* TestApp.app */; @@ -289,9 +312,12 @@ 2F148C00298BB15900031B7F /* FirebaseAccountTestsView.swift in Sources */, 2FA7382C290ADFAA007ACEB9 /* TestApp.swift in Sources */, 2FE62C3D2966074F00FCBE7F /* FirestoreDataStorageTests.swift in Sources */, + A9D83FA22B0BE048000D0C78 /* AccountStorageTestStandard.swift in Sources */, 2F8A431729130BBC005D2B8F /* TestAppType.swift in Sources */, 2F9F07F129090B0500CDC598 /* TestAppDelegate.swift in Sources */, + A9D83F992B0BDB13000D0C78 /* FeatureFlags.swift in Sources */, 97359F642ADB27500080CB11 /* FirebaseStorageTestsView.swift in Sources */, + A9D83F9B2B0BDB1D000D0C78 /* BiographyKey.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -300,6 +326,7 @@ buildActionMask = 2147483647; files = ( A95D60D02AA35E2200EB5968 /* FirebaseClient.swift in Sources */, + A9D83F9D2B0BDB3A000D0C78 /* FirebaseAccountStorageTests.swift in Sources */, 2F2E4B8429749C5900FF710F /* FirestoreDataStorageTests.swift in Sources */, 978E198E2ADB40A300732324 /* FirebaseStorageTests.swift in Sources */, 2F8CE161298C2C6D003799A8 /* FirebaseAccountTests.swift in Sources */, @@ -736,6 +763,10 @@ isa = XCSwiftPackageProductDependency; productName = SpeziFirebaseStorage; }; + A9D83F9E2B0BDCC7000D0C78 /* SpeziFirebaseAccountStorage */ = { + isa = XCSwiftPackageProductDependency; + productName = SpeziFirebaseAccountStorage; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 2F6D138A28F5F384007C25D6 /* Project object */; diff --git a/Tests/UITests/UITests.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Tests/UITests/UITests.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1ada950..fb334d5 100644 --- a/Tests/UITests/UITests.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Tests/UITests/UITests.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -122,8 +122,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/StanfordSpezi/SpeziAccount", "state" : { - "revision" : "901cf9d11cc3e4ea3704c65982866fc278e50790", - "version" : "0.8.0" + "revision" : "a9d37971e78fbdce02b0f8b57edfd2eecd4bb22e", + "version" : "0.8.1" } }, { diff --git a/Tests/UITests/UITests.xcodeproj/xcshareddata/xcschemes/TestApp.xcscheme b/Tests/UITests/UITests.xcodeproj/xcshareddata/xcschemes/TestApp.xcscheme index 5ba8ba7..46472a0 100644 --- a/Tests/UITests/UITests.xcodeproj/xcshareddata/xcschemes/TestApp.xcscheme +++ b/Tests/UITests/UITests.xcodeproj/xcshareddata/xcschemes/TestApp.xcscheme @@ -10,8 +10,8 @@ buildForTesting = "YES" buildForRunning = "YES" buildForProfiling = "YES" - buildForArchiving = "NO" - buildForAnalyzing = "NO"> + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + buildForRunning = "NO" + buildForProfiling = "NO" + buildForArchiving = "NO" + buildForAnalyzing = "NO"> + BlueprintIdentifier = "SpeziFirebaseAccount" + BuildableName = "SpeziFirebaseAccount" + BlueprintName = "SpeziFirebaseAccount" + ReferencedContainer = "container:../.."> + + + + + + + + @@ -109,6 +137,12 @@ ReferencedContainer = "container:UITests.xcodeproj"> + + + +