diff --git a/ENGAGEHF.xcodeproj/project.pbxproj b/ENGAGEHF.xcodeproj/project.pbxproj index b7d17a2d..15900be9 100644 --- a/ENGAGEHF.xcodeproj/project.pbxproj +++ b/ENGAGEHF.xcodeproj/project.pbxproj @@ -162,7 +162,6 @@ 4DBDD3462BBFAE2D001FB0CA /* InvitationCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DBDD3452BBFAE2D001FB0CA /* InvitationCodeView.swift */; }; 4DBDD3482BC073EF001FB0CA /* FirebaseFunctions in Frameworks */ = {isa = PBXBuildFile; productRef = 4DBDD3472BC073EF001FB0CA /* FirebaseFunctions */; }; 4DBE86DD2C7F9BBF0003390B /* MobilePlatform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DBE86DC2C7F9BBF0003390B /* MobilePlatform.swift */; }; - 4DBE86E02C7FAF6E0003390B /* Data+HexString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DBE86DF2C7FAF6E0003390B /* Data+HexString.swift */; }; 4DC0F6712C34818B0025AB13 /* HKSample+GetDoubleValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DC0F6702C34818B0025AB13 /* HKSample+GetDoubleValues.swift */; }; 4DC0F67A2C34AD620025AB13 /* VitalsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DC0F6792C34AD620025AB13 /* VitalsList.swift */; }; 4DC0F6842C34B66F0025AB13 /* ResolutionPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DC0F6832C34B66F0025AB13 /* ResolutionPicker.swift */; }; @@ -201,6 +200,7 @@ 9733CFC62A8066DE001B7ABC /* SpeziOnboarding in Frameworks */ = {isa = PBXBuildFile; productRef = 2FE5DC8029EDD91D004B9AB4 /* SpeziOnboarding */; }; 9739A0C62AD7B5730084BEA5 /* FirebaseStorage in Frameworks */ = {isa = PBXBuildFile; productRef = 9739A0C52AD7B5730084BEA5 /* FirebaseStorage */; }; 97D73D6A2AD860AD00B47FA0 /* SpeziFirebaseStorage in Frameworks */ = {isa = PBXBuildFile; productRef = 97D73D692AD860AD00B47FA0 /* SpeziFirebaseStorage */; }; + 9B0723AB2C8F6AE700D901E5 /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 9B0723AA2C8F6AE700D901E5 /* FirebaseMessaging */; }; 9B1864882C9226E90042AC81 /* AccountDetails+Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B1864872C9226E90042AC81 /* AccountDetails+Key.swift */; }; 9B18648B2C9227040042AC81 /* AccountNotifications+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B18648A2C9227040042AC81 /* AccountNotifications+Extras.swift */; }; 9B1864942C9276C40042AC81 /* AuthFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B1864932C9276C40042AC81 /* AuthFlow.swift */; }; @@ -373,7 +373,6 @@ 4DBDD3432BBFAD64001FB0CA /* InvitationCodeError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitationCodeError.swift; sourceTree = ""; }; 4DBDD3452BBFAE2D001FB0CA /* InvitationCodeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitationCodeView.swift; sourceTree = ""; }; 4DBE86DC2C7F9BBF0003390B /* MobilePlatform.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MobilePlatform.swift; sourceTree = ""; }; - 4DBE86DF2C7FAF6E0003390B /* Data+HexString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+HexString.swift"; sourceTree = ""; }; 4DC0F6702C34818B0025AB13 /* HKSample+GetDoubleValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HKSample+GetDoubleValues.swift"; sourceTree = ""; }; 4DC0F6792C34AD620025AB13 /* VitalsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VitalsList.swift; sourceTree = ""; }; 4DC0F6832C34B66F0025AB13 /* ResolutionPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResolutionPicker.swift; sourceTree = ""; }; @@ -444,6 +443,7 @@ 4D49AB022BC9D50400C77310 /* SpeziBluetooth in Frameworks */, 56E708352BB06B7100B08F0A /* SpeziLicense in Frameworks */, 2FE5DC8A29EDD972004B9AB4 /* SpeziLocalStorage in Frameworks */, + 9B0723AB2C8F6AE700D901E5 /* FirebaseMessaging in Frameworks */, 2FE5DC8C29EDD972004B9AB4 /* SpeziSecureStorage in Frameworks */, A9623C042C17A3F500189BA1 /* SpeziOmron in Frameworks */, 2FE5DC7529EDD8E6004B9AB4 /* SpeziFirebaseAccount in Frameworks */, @@ -693,7 +693,6 @@ children = ( 4D8CE0FA2C753A4600560327 /* NotificationManager.swift */, 4D8CE0FB2C753A4600560327 /* NotificationRegistrationSchema.swift */, - 4DBE86DF2C7FAF6E0003390B /* Data+HexString.swift */, ); path = NotificationManager; sourceTree = ""; @@ -1177,6 +1176,7 @@ A977DD532C25E2DA00A2A8E5 /* SpeziDevices */, A977DD552C25E2DA00A2A8E5 /* SpeziDevicesUI */, A977DD5C2C26279E00A2A8E5 /* SpeziBluetoothServices */, + 9B0723AA2C8F6AE700D901E5 /* FirebaseMessaging */, ); productName = ENGAGEHF; productReference = 653A254D283387FE005D4D48 /* ENGAGEHF.app */; @@ -1349,7 +1349,6 @@ 4D734AC02C5C58CA002D6D2F /* View+asButton.swift in Sources */, 4D734AC32C5D7C81002D6D2F /* MessageAction.swift in Sources */, 4D92BA172C3E108700ABCED7 /* CodeableConcept+Contains.swift in Sources */, - 4DBE86E02C7FAF6E0003390B /* Data+HexString.swift in Sources */, 4D62BF2A2C6A71500088C414 /* FirestoreSettings+EmulatorWithHost.swift in Sources */, 4D8CE0FC2C753A4700560327 /* NotificationManager.swift in Sources */, 4DC0F67A2C34AD620025AB13 /* VitalsList.swift in Sources */, @@ -2289,6 +2288,11 @@ package = 2FE5DC7329EDD8E6004B9AB4 /* XCRemoteSwiftPackageReference "SpeziFirebase" */; productName = SpeziFirebaseStorage; }; + 9B0723AA2C8F6AE700D901E5 /* FirebaseMessaging */ = { + isa = XCSwiftPackageProductDependency; + package = 2FE5DC9029EDD9C3004B9AB4 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseMessaging; + }; A9623C032C17A3F500189BA1 /* SpeziOmron */ = { isa = XCSwiftPackageProductDependency; package = A9623C022C17A3F500189BA1 /* XCRemoteSwiftPackageReference "SpeziDevices" */; diff --git a/ENGAGEHF.xcodeproj/xcshareddata/xcschemes/ENGAGEHF.xcscheme b/ENGAGEHF.xcodeproj/xcshareddata/xcschemes/ENGAGEHF.xcscheme index e2a9cbb4..eca0e38c 100644 --- a/ENGAGEHF.xcodeproj/xcshareddata/xcschemes/ENGAGEHF.xcscheme +++ b/ENGAGEHF.xcodeproj/xcshareddata/xcschemes/ENGAGEHF.xcscheme @@ -83,7 +83,7 @@ + isEnabled = "YES"> Bool { logger.debug("Requesting notification permissions.") - let deviceToken = try await self.getDeviceToken(askPermissionIfNeeded: true) - - guard let deviceToken else { + guard let deviceToken = try await self.getDeviceToken(askPermissionIfNeeded: true) else { return false } - try await self.configureRemoteNotifications(using: deviceToken) + try await self.registerDeviceToken(using: deviceToken) return true } - func receiveUpdatedDeviceToken(_ deviceToken: Data) { + func receiveUpdatedDeviceToken(_ apnsToken: Data) { Task { do { - try await self.configureRemoteNotifications(using: deviceToken) + try await self.registerDeviceToken(using: apnsToken) } catch { self.logger.error("Failed to configure remote notifications for updated device token: \(error)") } @@ -166,11 +165,12 @@ class NotificationManager: Module, NotificationHandler, NotificationTokenHandler } - private func configureRemoteNotifications(using deviceToken: Data) async throws { + private func registerDeviceToken(using apnsToken: Data) async throws { self.logger.debug("Registering device for remote notifications.") + let fcmToken = try await convertTokenToFCM(apns: apnsToken) let registerDevice = Functions.functions().httpsCallable("registerDevice") - _ = try await registerDevice.call(NotificationRegistrationSchema(deviceToken).codingRepresentation) + _ = try await registerDevice.call(NotificationRegistrationSchema(fcmToken).codingRepresentation) self.logger.debug("Successfully registered device for remote notifications.") } @@ -183,8 +183,9 @@ class NotificationManager: Module, NotificationHandler, NotificationTokenHandler return } + let fcmToken = try await convertTokenToFCM(apns: deviceToken) let unregisterDevice = Functions.functions().httpsCallable("unregisterDevice") - _ = try await unregisterDevice.call(NotificationRegistrationSchema(deviceToken).codingRepresentation) + _ = try await unregisterDevice.call(NotificationRegistrationSchema(fcmToken).codingRepresentation) self.logger.debug("Successfully unregistered device for remote notifications.") } @@ -215,9 +216,15 @@ class NotificationManager: Module, NotificationHandler, NotificationTokenHandler } #if TEST - return Data() + return nil #else - return FeatureFlags.skipRemoteNotificationRegistration ? Data() : try await registerRemoteNotifications() + return FeatureFlags.skipRemoteNotificationRegistration ? nil : try await registerRemoteNotifications() #endif } + + private func convertTokenToFCM(apns apnsToken: Data) async throws -> String { + let messaging = Messaging.messaging() + messaging.apnsToken = apnsToken + return try await messaging.token() + } } diff --git a/ENGAGEHF/Managers/NotificationManager/NotificationRegistrationSchema.swift b/ENGAGEHF/Managers/NotificationManager/NotificationRegistrationSchema.swift index ef66b98f..dd935879 100644 --- a/ENGAGEHF/Managers/NotificationManager/NotificationRegistrationSchema.swift +++ b/ENGAGEHF/Managers/NotificationManager/NotificationRegistrationSchema.swift @@ -34,8 +34,8 @@ struct NotificationRegistrationSchema: Codable { } - init(_ deviceToken: Data, locale: Locale = Locale.current, timeZone: TimeZone = .current) { - self.notificationToken = deviceToken.hexString + init(_ notificationToken: String, locale: Locale = Locale.current, timeZone: TimeZone = .current) { + self.notificationToken = notificationToken self.platform = MobilePlatform.iOS.rawValue self.osVersion = UIDevice.current.systemVersion self.appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String diff --git a/ENGAGEHF/Resources/Localizable.xcstrings b/ENGAGEHF/Resources/Localizable.xcstrings index 61c9524f..089d3243 100644 --- a/ENGAGEHF/Resources/Localizable.xcstrings +++ b/ENGAGEHF/Resources/Localizable.xcstrings @@ -3,9 +3,6 @@ "strings" : { "" : { - }, - "%@" : { - }, "%@ Date: %@" : { "localizations" : { @@ -115,12 +112,6 @@ }, "Add Medications" : { - }, - "Add Mock" : { - - }, - "Add mock notification" : { - }, "All Data" : { @@ -182,9 +173,6 @@ } } } - }, - "Content ..." : { - }, "Content Unavailable" : { @@ -215,9 +203,6 @@ }, "Expansion Button" : { - }, - "Expected Progress: %lf" : { - }, "Failed to fetch HKUnits for the given samples." : { @@ -407,12 +392,6 @@ } } } - }, - "List With Sections" : { - - }, - "List Without Sections" : { - }, "Logout" : { @@ -658,9 +637,6 @@ } } } - }, - "Tap Here" : { - }, "Target" : { diff --git a/ENGAGEHF/Supporting Files/GoogleService-Info.plist b/ENGAGEHF/Supporting Files/GoogleService-Info.plist index 6a129e9a..23c290cf 100644 --- a/ENGAGEHF/Supporting Files/GoogleService-Info.plist +++ b/ENGAGEHF/Supporting Files/GoogleService-Info.plist @@ -7,7 +7,7 @@ REVERSED_CLIENT_ID REVERSED_CLIENT_ID API_KEY - API_KEY + API_KEY89012345678901234567890123456789 GCM_SENDER_ID GCM_SENDER_ID PLIST_VERSION diff --git a/ENGAGEHFTests/HKSampleGraphUnitTests.swift b/ENGAGEHFTests/HKSampleGraphUnitTests.swift index 17eeca32..78990568 100644 --- a/ENGAGEHFTests/HKSampleGraphUnitTests.swift +++ b/ENGAGEHFTests/HKSampleGraphUnitTests.swift @@ -110,7 +110,7 @@ final class HKSampleGraphUnitTests: XCTestCase { validateSeriesDictionary(actualOutput: viewModel.seriesData, expectedOutput: expectedOutput) // Make sure the correct unit was identified - XCTAssertEqual(viewModel.displayUnit, "lb") + XCTAssertEqual(viewModel.displayUnit, Locale.current.measurementSystem == .us ? "lb" : "kg") // Make sure the correct formatter was chosen XCTAssertEqual(viewModel.formatter([(KnownVitalsSeries.bodyWeight.rawValue, 120.0), ("Diastolic", 60.0)]), "120.0") diff --git a/ENGAGEHFUITests/Account/ContactsUITests.swift b/ENGAGEHFUITests/Account/ContactsUITests.swift index 14e96209..8355f81a 100644 --- a/ENGAGEHFUITests/Account/ContactsUITests.swift +++ b/ENGAGEHFUITests/Account/ContactsUITests.swift @@ -29,7 +29,7 @@ final class ContactsUITests: XCTestCase { _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Home") + app.goTo(tab: "Home") XCTAssertTrue(app.navigationBars.buttons["Your Account"].waitForExistence(timeout: 2)) app.navigationBars.buttons["Your Account"].tap() diff --git a/ENGAGEHFUITests/Account/NotificationSettingsUITests.swift b/ENGAGEHFUITests/Account/NotificationSettingsUITests.swift index 3b66e0b9..db9cc7e0 100644 --- a/ENGAGEHFUITests/Account/NotificationSettingsUITests.swift +++ b/ENGAGEHFUITests/Account/NotificationSettingsUITests.swift @@ -28,7 +28,7 @@ final class NotificationSettingsUITests: XCTestCase { _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Home") + app.goTo(tab: "Home") XCTAssertTrue(app.navigationBars.buttons["Your Account"].waitForExistence(timeout: 2)) app.navigationBars.buttons["Your Account"].tap() diff --git a/ENGAGEHFUITests/Dashboard/MessagesUITests.swift b/ENGAGEHFUITests/Dashboard/MessagesUITests.swift index 97662867..047ebb96 100644 --- a/ENGAGEHFUITests/Dashboard/MessagesUITests.swift +++ b/ENGAGEHFUITests/Dashboard/MessagesUITests.swift @@ -30,7 +30,7 @@ final class MessagesUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Home") + app.goTo(tab: "Home") // Directly dismiss by tapping dismiss button let medicationChangeMessage = app.otherElements["Message Card - Medication Change"] @@ -48,7 +48,7 @@ final class MessagesUITests: XCTestCase { uptitrationMessage.tap() sleep(1) - try app.goTo(tab: "Home") + app.goTo(tab: "Home") XCTAssertFalse(app.otherElements["Message Card - Medication Uptitration"].exists) @@ -59,7 +59,7 @@ final class MessagesUITests: XCTestCase { vitalsMessage.tap() sleep(1) - try app.goTo(tab: "Home") + app.goTo(tab: "Home") XCTAssert(app.otherElements["Message Card - Vitals"].exists) } @@ -67,7 +67,7 @@ final class MessagesUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Home") + app.goTo(tab: "Home") let medicationChangeMessage = app.otherElements["Message Card - Medication Change"] XCTAssert(medicationChangeMessage.exists) @@ -89,7 +89,7 @@ final class MessagesUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Home") + app.goTo(tab: "Home") let questionnaireMessage = app.otherElements["Message Card - Symptom Questionnaire"] XCTAssert(questionnaireMessage.exists) @@ -106,7 +106,7 @@ final class MessagesUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Home") + app.goTo(tab: "Home") let medicationUptitrationMessage = app.otherElements["Message Card - Medication Uptitration"] XCTAssert(medicationUptitrationMessage.exists) @@ -122,7 +122,7 @@ final class MessagesUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Home") + app.goTo(tab: "Home") let vitalsMessage = app.otherElements["Message Card - Vitals"] XCTAssert(vitalsMessage.exists) @@ -138,7 +138,7 @@ final class MessagesUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Home") + app.goTo(tab: "Home") let unknownActionMessage = app.otherElements["Message Card - Unknown"] XCTAssert(unknownActionMessage.exists) @@ -153,7 +153,7 @@ final class MessagesUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Home") + app.goTo(tab: "Home") let medicationChangeMessage = app.otherElements["Message Card - Medication Change"] XCTAssert(medicationChangeMessage.exists) diff --git a/ENGAGEHFUITests/Dashboard/RecentVitalsUITests.swift b/ENGAGEHFUITests/Dashboard/RecentVitalsUITests.swift index 46ca60f9..21fbd2f2 100644 --- a/ENGAGEHFUITests/Dashboard/RecentVitalsUITests.swift +++ b/ENGAGEHFUITests/Dashboard/RecentVitalsUITests.swift @@ -28,7 +28,7 @@ final class RecentVitalsUITests: XCTestCase { let weightUnit = Locale.current.measurementSystem == .us ? "lb" : "kg" // Delete all previous measurements - try app.deleteAllMeasurements("Weight", header: "Body Weight") + app.deleteAllMeasurements("Weight", header: "Body Weight") // Make sure we're on the home screen XCTAssert(app.buttons["Home"].waitForExistence(timeout: 2.0)) @@ -67,8 +67,8 @@ final class RecentVitalsUITests: XCTestCase { _ = app.staticTexts["Home"].waitForExistence(timeout: 5) // Delete previous measurements - try app.deleteAllMeasurements("HR", header: "Heart Rate") - try app.deleteAllMeasurements("BP", header: "Blood Pressure") + app.deleteAllMeasurements("HR", header: "Heart Rate") + app.deleteAllMeasurements("BP", header: "Blood Pressure") // Make sure we're on the home screen XCTAssert(app.buttons["Home"].waitForExistence(timeout: 2.0)) diff --git a/ENGAGEHFUITests/Education/EducationViewUITests.swift b/ENGAGEHFUITests/Education/EducationViewUITests.swift index b72d1143..fb96972e 100644 --- a/ENGAGEHFUITests/Education/EducationViewUITests.swift +++ b/ENGAGEHFUITests/Education/EducationViewUITests.swift @@ -24,7 +24,7 @@ final class EducationViewUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Education") + app.goTo(tab: "Education") let thumbnailOverlay = app.staticTexts["Thumbnail Overlay Title: Long Description"] XCTAssert(thumbnailOverlay.waitForExistence(timeout: 0.5)) @@ -73,7 +73,7 @@ final class EducationViewUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Education") + app.goTo(tab: "Education") let thumbnailOverlay = app.staticTexts["Thumbnail Overlay Title: Short Description"] XCTAssert(thumbnailOverlay.waitForExistence(timeout: 0.5)) @@ -117,7 +117,7 @@ final class EducationViewUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Education") + app.goTo(tab: "Education") let thumbnailOverlay = app.staticTexts["Thumbnail Overlay Title: No Description"] XCTAssert(thumbnailOverlay.waitForExistence(timeout: 0.5)) @@ -147,7 +147,7 @@ final class EducationViewUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Education") + app.goTo(tab: "Education") let sectionHeader = app.staticTexts["ENGAGE-HF Application"] @@ -170,7 +170,7 @@ final class EducationViewUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Education") + app.goTo(tab: "Education") let videoSection = app.otherElements["Video Section: ENGAGE-HF Application"] XCTAssert(videoSection.exists) diff --git a/ENGAGEHFUITests/HeartHealth/AddMeasurementUITests.swift b/ENGAGEHFUITests/HeartHealth/AddMeasurementUITests.swift index 35a2f901..e7d35dad 100644 --- a/ENGAGEHFUITests/HeartHealth/AddMeasurementUITests.swift +++ b/ENGAGEHFUITests/HeartHealth/AddMeasurementUITests.swift @@ -24,7 +24,7 @@ final class AddMeasurementUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Heart Health") + app.goTo(tab: "Heart Health") let expectedUnit = Locale.current.measurementSystem == .us ? "lb" : "kg" let inputs = [(expectedUnit, "100")] @@ -41,7 +41,7 @@ final class AddMeasurementUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Heart Health") + app.goTo(tab: "Heart Health") let expectedUnit = "BPM" let inputs = [(expectedUnit, "60")] @@ -58,7 +58,6 @@ final class AddMeasurementUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Heart Health") let inputs = [("Systolic", "120"), ("Diastolic", "60")] let expectedQuantity = ("120/60", "mmHg") @@ -74,8 +73,7 @@ final class AddMeasurementUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Heart Health") - try app.goTo(tab: "Symptoms", header: "Overall Score") + app.goToHeartHealth(segment: "Symptoms", header: "Overall Score") XCTAssertFalse(app.buttons["Add Measurement: Symptom Score"].exists) } @@ -88,9 +86,9 @@ extension XCUIApplication { inputs: [(label: String, value: String)], expectedQuantity: (value: String, unit: String) ) throws { - try goTo(tab: id.short, header: id.full) + goToHeartHealth(segment: id.short, header: id.full) - swipeUp() + staticTexts["About \(id.full)"].swipeUp() buttons["Add Measurement: \(id.short)"].tap() XCTAssert(staticTexts[id.full].waitForExistence(timeout: 0.5)) diff --git a/ENGAGEHFUITests/HeartHealth/HeartHealthUITests.swift b/ENGAGEHFUITests/HeartHealth/HeartHealthUITests.swift index eca8baf4..c7e982e9 100644 --- a/ENGAGEHFUITests/HeartHealth/HeartHealthUITests.swift +++ b/ENGAGEHFUITests/HeartHealth/HeartHealthUITests.swift @@ -27,36 +27,31 @@ final class HeartHealthUITests: XCTestCase { func testSymptomScores() throws { let app = XCUIApplication() - try app.goTo(tab: "Heart Health") - try app.testEmptySymptomScores() + app.goTo(tab: "Heart Health") + app.testEmptySymptomScores() } func testEmptyBodyWeight() throws { let app = XCUIApplication() - - // Make sure we're on the Heart Health view - try app.goTo(tab: "Heart Health") // Clear out any data present before continuing - try app.deleteAllMeasurements("Weight", header: "Body Weight") - try app.testEmptyVitals(for: "Body Weight", pickerLabel: "Weight") + app.deleteAllMeasurements("Weight", header: "Body Weight") + app.testEmptyVitals(for: "Body Weight", pickerLabel: "Weight") } func testEmptyHeartRate() throws { let app = XCUIApplication() - try app.goTo(tab: "Heart Health") // Clear out any data present before continuing - try app.deleteAllMeasurements("HR", header: "Heart Rate") - try app.testEmptyVitals(for: "Heart Rate", pickerLabel: "HR") + app.deleteAllMeasurements("HR", header: "Heart Rate") + app.testEmptyVitals(for: "Heart Rate", pickerLabel: "HR") } func testEmptyBloodPressure() throws { let app = XCUIApplication() - try app.goTo(tab: "Heart Health") - try app.deleteAllMeasurements("BP", header: "Blood Pressure") - try app.testEmptyVitals(for: "Blood Pressure", pickerLabel: "BP") + app.deleteAllMeasurements("BP", header: "Blood Pressure") + app.testEmptyVitals(for: "Blood Pressure", pickerLabel: "BP") } func testWithWeightSample() throws { @@ -66,13 +61,12 @@ final class HeartHealthUITests: XCTestCase { let expectedUnit = Locale.current.measurementSystem == .us ? "lb" : "kg" // Start fresh - try app.goTo(tab: "Heart Health") - try app.deleteAllMeasurements("Weight", header: "Body Weight") + app.deleteAllMeasurements("Weight", header: "Body Weight") // Trigger a measurement - try app.goTo(tab: "Home") - try app.triggerMockMeasurement("Weight", expect: ["42 kg"]) - try app.goTo(tab: "Heart Health") + app.goTo(tab: "Home") + app.triggerMockMeasurement("Weight", expect: ["42 kg"]) + app.goTo(tab: "Heart Health") // Test to make sure the graph appears try app.testGraphWithSamples( @@ -81,28 +75,27 @@ final class HeartHealthUITests: XCTestCase { ) // Test to make sure the All Data section has an item in it - app.swipeUp() + app.staticTexts["About Body Weight"].swipeUp() XCTAssertFalse(app.staticTexts["Empty Weight List"].waitForExistence(timeout: 0.5)) XCTAssert(app.staticTexts["Weight Quantity: \(expectedWeight)"].exists) XCTAssert(app.staticTexts["Weight Unit: \(expectedUnit)"].exists) XCTAssert(app.staticTexts["Weight Date: Jun 5, 2024"].exists) // Make sure the empty views return when we delete the data - try app.deleteAllMeasurements("Weight", header: "Body Weight") - try app.testEmptyVitals(for: "Body Weight", pickerLabel: "Weight") + app.deleteAllMeasurements("Weight", header: "Body Weight") + app.testEmptyVitals(for: "Body Weight", pickerLabel: "Weight") } func testWithHeartRateSample() throws { let app = XCUIApplication() // Start fresh - try app.goTo(tab: "Heart Health") - try app.deleteAllMeasurements("HR", header: "Heart Rate") + app.deleteAllMeasurements("HR", header: "Heart Rate") // Trigger a measurement - try app.goTo(tab: "Home") - try app.triggerMockMeasurement("Blood Pressure", expect: ["103/64 mmHg", "62 BPM"]) - try app.goTo(tab: "Heart Health") + app.goTo(tab: "Home") + app.triggerMockMeasurement("Blood Pressure", expect: ["103/64 mmHg", "62 BPM"]) + app.goTo(tab: "Heart Health") // Test to make sure the graph appears try app.testGraphWithSamples( @@ -111,28 +104,27 @@ final class HeartHealthUITests: XCTestCase { ) // Test to make sure the All Data section has an item in it - app.swipeUp() + app.staticTexts["About Heart Rate"].swipeUp() XCTAssertFalse(app.staticTexts["Empty HR List"].waitForExistence(timeout: 0.5)) XCTAssert(app.staticTexts["HR Quantity: 62"].exists) XCTAssert(app.staticTexts["HR Unit: BPM"].exists) XCTAssert(app.staticTexts["HR Date: Jun 5, 2024"].exists) // Make sure the empty views return when we delete the data - try app.deleteAllMeasurements("HR", header: "Heart Rate") - try app.testEmptyVitals(for: "Heart Rate", pickerLabel: "HR") + app.deleteAllMeasurements("HR", header: "Heart Rate") + app.testEmptyVitals(for: "Heart Rate", pickerLabel: "HR") } func testWithBloodPressureSample() throws { let app = XCUIApplication() // Start fresh - try app.goTo(tab: "Heart Health") - try app.deleteAllMeasurements("BP", header: "Blood Pressure") + app.deleteAllMeasurements("BP", header: "Blood Pressure") // Trigger a measurement - try app.goTo(tab: "Home") - try app.triggerMockMeasurement("Blood Pressure", expect: ["103/64 mmHg", "62 BPM"]) - try app.goTo(tab: "Heart Health") + app.goTo(tab: "Home") + app.triggerMockMeasurement("Blood Pressure", expect: ["103/64 mmHg", "62 BPM"]) + app.goTo(tab: "Heart Health") // Test to make sure the graph appears try app.testGraphWithSamples( @@ -141,15 +133,15 @@ final class HeartHealthUITests: XCTestCase { ) // Test to make sure the All Data section has an item in it - app.swipeUp() + app.staticTexts["About Blood Pressure"].swipeUp() XCTAssertFalse(app.staticTexts["Empty BP List"].waitForExistence(timeout: 0.5)) XCTAssert(app.staticTexts["BP Quantity: 103/64"].exists) XCTAssert(app.staticTexts["BP Unit: mmHg"].exists) XCTAssert(app.staticTexts["BP Date: Jun 5, 2024"].exists) // Make sure the empty views return when we delete the data - try app.deleteAllMeasurements("BP", header: "Blood Pressure") - try app.testEmptyVitals(for: "Blood Pressure", pickerLabel: "BP") + app.deleteAllMeasurements("BP", header: "Blood Pressure") + app.testEmptyVitals(for: "Blood Pressure", pickerLabel: "BP") } } @@ -195,7 +187,7 @@ extension XCUIApplication { for (resolution, expectedRange) in zip(["Weekly", "Monthly"], expectedRanges) { let pickerID = resolution == "Weekly" ? "Daily" : "Weekly" - try testGraph( + testGraph( id: id, expectedQuantity: expectedQuantity, dateInfo: (resolution, expectedRange), @@ -210,11 +202,11 @@ extension XCUIApplication { expectedQuantity: (value: String, unit: String), dateInfo: (granularity: String, range: String), pickerID: String - ) throws { + ) { // Make sure the vitals are correctly displayed - try goTo(tab: id.short, header: id.full) + goToHeartHealth(segment: id.short, header: id.full) - swipeUp() + staticTexts["About \(id.full)"].swipeUp() swipeUp() // Make sure the measurement is displayed in "All Data" section @@ -248,7 +240,7 @@ extension XCUIApplication { } - fileprivate func triggerMockMeasurement(_ displayName: String, expect measurements: [String]) throws { + fileprivate func triggerMockMeasurement(_ displayName: String, expect measurements: [String]) { XCTAssert(navigationBars.buttons["More"].exists) navigationBars.buttons["More"].tap() @@ -272,20 +264,19 @@ extension XCUIApplication { extension XCUIApplication { - fileprivate func testEmptyVitals(for vitalType: String, pickerLabel: String) throws { + fileprivate func testEmptyVitals(for vitalType: String, pickerLabel: String) { XCTAssert(buttons[pickerLabel].waitForExistence(timeout: 0.5)) buttons[pickerLabel].tap() - - XCTAssert(staticTexts[vitalType].waitForExistence(timeout: 0.5)) + XCTAssert(staticTexts["Overall Summary Quantity: No Data"].waitForExistence(timeout: 0.5)) XCTAssert(staticTexts["About \(vitalType)"].waitForExistence(timeout: 0.5)) + staticTexts["About \(vitalType)"].swipeUp() XCTAssert(staticTexts["\(vitalType) Description"].waitForExistence(timeout: 0.5)) - swipeUp() XCTAssert(staticTexts["Empty \(pickerLabel) List"].waitForExistence(timeout: 0.5)) swipeDown() } - fileprivate func testEmptySymptomScores() throws { + fileprivate func testEmptySymptomScores() { XCTAssert(buttons["Symptoms"].waitForExistence(timeout: 0.5)) buttons["Symptoms"].tap() @@ -318,15 +309,15 @@ extension XCUIApplication { XCTAssert(buttons["\(symptomLabels[nextIdx])"].waitForExistence(timeout: 0.5)) buttons["\(symptomLabels[nextIdx])"].tap() - try testEmptyForSpecificType(scoreType: symptomTypes[nextIdx]) + testEmptyForSpecificType(scoreType: symptomTypes[nextIdx]) } } - private func testEmptyForSpecificType(scoreType: String) throws { + private func testEmptyForSpecificType(scoreType: String) { XCTAssert(staticTexts["Overall Summary Quantity: No Data"].waitForExistence(timeout: 0.5)) XCTAssert(staticTexts["\(scoreType) Score Description"].waitForExistence(timeout: 0.5)) XCTAssert(staticTexts["About \(scoreType) Score"].waitForExistence(timeout: 0.5)) - swipeUp() + staticTexts["About \(scoreType) Score"].swipeUp() XCTAssert(staticTexts["Empty Symptoms List"].waitForExistence(timeout: 0.5)) swipeDown() } diff --git a/ENGAGEHFUITests/HelperFunctions/XCUIApplication+DeleteMeasurements.swift b/ENGAGEHFUITests/HelperFunctions/XCUIApplication+DeleteMeasurements.swift index 2137425b..01b6b334 100644 --- a/ENGAGEHFUITests/HelperFunctions/XCUIApplication+DeleteMeasurements.swift +++ b/ENGAGEHFUITests/HelperFunctions/XCUIApplication+DeleteMeasurements.swift @@ -11,11 +11,10 @@ import XCTest extension XCUIApplication { /// Attempts to delete all measurements of a given type by navigating to Heart Health view, then deleting the items in the All Data section of the page - func deleteAllMeasurements(_ id: String, header: String) throws { - try goTo(tab: "Heart Health") - try goTo(tab: id, header: header) + func deleteAllMeasurements(_ id: String, header: String) { + goToHeartHealth(segment: id, header: header) - swipeUp() + staticTexts["About \(header)"].swipeUp() var dataPresent = !staticTexts["Empty \(id) List"].exists var totalRows = 0 diff --git a/ENGAGEHFUITests/HelperFunctions/XCUIApplication+GoTo.swift b/ENGAGEHFUITests/HelperFunctions/XCUIApplication+GoTo.swift index 46fd6649..de823b1a 100644 --- a/ENGAGEHFUITests/HelperFunctions/XCUIApplication+GoTo.swift +++ b/ENGAGEHFUITests/HelperFunctions/XCUIApplication+GoTo.swift @@ -11,10 +11,18 @@ import XCTest extension XCUIApplication { /// Tries to navigate to a tab by clicking on a button in the current view with label "id", and verifies the correct arrival by looking for a header with label "header" or "id" if no header given. - func goTo(tab tabName: String, header: String? = nil) throws { + func goTo(tab tabName: String, header: String? = nil) { XCTAssert(buttons[tabName].waitForExistence(timeout: 6.0), "No button found for tab \(tabName)") buttons[tabName].tap() swipeDown() XCTAssert(staticTexts[header ?? tabName].waitForExistence(timeout: 1.0)) } + + func goToHeartHealth(segment: String, header: String) { + goTo(tab: "Heart Health") + XCTAssert(buttons[segment].waitForExistence(timeout: 6.0), "No button found for segment \(segment)") + buttons[segment].tap() + staticTexts["About \(header)"].swipeDown() + XCTAssert(staticTexts[header].waitForExistence(timeout: 1.0)) + } } diff --git a/ENGAGEHFUITests/Medications/MedicationsUITests.swift b/ENGAGEHFUITests/Medications/MedicationsUITests.swift index 9331441d..a160dfe7 100644 --- a/ENGAGEHFUITests/Medications/MedicationsUITests.swift +++ b/ENGAGEHFUITests/Medications/MedicationsUITests.swift @@ -25,7 +25,7 @@ final class MedicationsUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Medications") + app.goTo(tab: "Medications") XCTAssert(app.buttons["More"].waitForExistence(timeout: 0.5), "No \"More\" Button Found.") app.buttons["More"].tap() @@ -46,7 +46,7 @@ final class MedicationsUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Medications") + app.goTo(tab: "Medications") XCTAssert(app.staticTexts["No medication recommendations"].waitForExistence(timeout: 0.5)) } @@ -55,7 +55,7 @@ final class MedicationsUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Medications") + app.goTo(tab: "Medications") XCTAssert(app.buttons["More"].waitForExistence(timeout: 0.5), "No \"More\" Button Found.") app.buttons["More"].tap() @@ -82,7 +82,7 @@ final class MedicationsUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Medications") + app.goTo(tab: "Medications") XCTAssert(app.buttons["More"].waitForExistence(timeout: 0.5), "No \"More\" Button Found.") app.buttons["More"].tap() @@ -100,7 +100,7 @@ final class MedicationsUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Medications") + app.goTo(tab: "Medications") XCTAssert(app.buttons["More"].waitForExistence(timeout: 0.5), "No \"More\" Button Found.") app.buttons["More"].tap() @@ -121,7 +121,7 @@ final class MedicationsUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Medications") + app.goTo(tab: "Medications") XCTAssert(app.buttons["More"].waitForExistence(timeout: 0.5), "No \"More\" Button Found.") app.buttons["More"].tap() @@ -142,7 +142,7 @@ final class MedicationsUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Medications") + app.goTo(tab: "Medications") XCTAssert(app.buttons["More"].waitForExistence(timeout: 0.5), "No \"More\" Button Found.") app.buttons["More"].tap() @@ -169,7 +169,7 @@ final class MedicationsUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Medications") + app.goTo(tab: "Medications") XCTAssert(app.buttons["More"].waitForExistence(timeout: 0.5), "No \"More\" Button Found.") app.buttons["More"].tap() @@ -189,7 +189,7 @@ final class MedicationsUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Medications") + app.goTo(tab: "Medications") XCTAssert(app.buttons["More"].waitForExistence(timeout: 0.5), "No \"More\" Button Found.") app.buttons["More"].tap() @@ -207,7 +207,7 @@ final class MedicationsUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Medications") + app.goTo(tab: "Medications") XCTAssert(app.buttons["More"].waitForExistence(timeout: 0.5), "No \"More\" Button Found.") app.buttons["More"].tap() @@ -228,7 +228,7 @@ final class MedicationsUITests: XCTestCase { let app = XCUIApplication() _ = app.staticTexts["Home"].waitForExistence(timeout: 5) - try app.goTo(tab: "Medications") + app.goTo(tab: "Medications") XCTAssert(app.buttons["More"].waitForExistence(timeout: 0.5), "No \"More\" Button Found.") app.buttons["More"].tap()