import XCTest /// Reusable helper functions for UI tests struct UITestHelpers { private static func loginUsernameField(app: XCUIApplication) -> XCUIElement { app.textFields[AccessibilityIdentifiers.Authentication.usernameField] } // MARK: - Authentication Helpers /// Logs out the user if they are currently logged in /// - Parameter app: The XCUIApplication instance static func logout(app: XCUIApplication) { sleep(1) // Already on login screen. let usernameField = loginUsernameField(app: app) if usernameField.waitForExistence(timeout: 2) { return } // In onboarding flow, navigate to login. let onboardingRoot = app.otherElements[UITestID.Root.onboarding] if onboardingRoot.waitForExistence(timeout: 2) { ensureOnLoginScreen(app: app) return } // Check if we have a tab bar (logged in state) let tabBar = app.tabBars.firstMatch guard tabBar.exists else { return } // Navigate to Residences tab first let residencesTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Residences'")).firstMatch if residencesTab.exists { residencesTab.tap() sleep(1) } // Tap settings button let settingsButton = app.buttons[AccessibilityIdentifiers.Navigation.settingsButton] if settingsButton.waitForExistence(timeout: 3) && settingsButton.isHittable { settingsButton.tap() sleep(1) } // Find and tap logout button let logoutButton = app.buttons[AccessibilityIdentifiers.Profile.logoutButton] if logoutButton.waitForExistence(timeout: 3) { logoutButton.tap() sleep(1) // Confirm logout in alert if present - specifically target the alert's button let alert = app.alerts.firstMatch if alert.waitForExistence(timeout: 2) { let confirmLogout = alert.buttons["Log Out"] if confirmLogout.exists { confirmLogout.tap() } } } sleep(2) XCTAssertTrue( usernameField.waitForExistence(timeout: 8), "Failed to log out - login username field should appear" ) } /// Logs in a user with the provided credentials /// - Parameters: /// - app: The XCUIApplication instance /// - username: The username/email to use for login /// - password: The password to use for login static func login(app: XCUIApplication, username: String, password: String) { // Find username field by accessibility identifier let usernameField = app.textFields[AccessibilityIdentifiers.Authentication.usernameField] XCTAssertTrue(usernameField.waitForExistence(timeout: 5), "Username field should exist") usernameField.tap() usernameField.typeText(username) // Find password field - it could be TextField (if visible) or SecureField var passwordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.passwordField] if !passwordField.exists { passwordField = app.textFields[AccessibilityIdentifiers.Authentication.passwordField] } XCTAssertTrue(passwordField.waitForExistence(timeout: 3), "Password field should exist") passwordField.tap() passwordField.typeText(password) // Find and tap login button let loginButton = app.buttons[AccessibilityIdentifiers.Authentication.loginButton] XCTAssertTrue(loginButton.waitForExistence(timeout: 3), "Login button should exist") loginButton.tap() // Wait for login to complete sleep(3) } /// Ensures the user is logged out before running a test /// - Parameter app: The XCUIApplication instance static func ensureLoggedOut(app: XCUIApplication) { sleep(1) logout(app: app) ensureOnLoginScreen(app: app) } /// Ensures the user is logged in with test credentials before running a test /// - Parameter app: The XCUIApplication instance /// - Parameter username: Optional username (defaults to "testuser") /// - Parameter password: Optional password (defaults to "TestPass123!") static func ensureLoggedIn(app: XCUIApplication, username: String = "testuser", password: String = "TestPass123!") { sleep(1) // Check if already logged in (tab bar visible) let tabBar = app.tabBars.firstMatch if tabBar.exists { return // Already logged in } ensureOnLoginScreen(app: app) let usernameField = app.textFields[AccessibilityIdentifiers.Authentication.usernameField] if usernameField.waitForExistence(timeout: 5) { login(app: app, username: username, password: password) // Wait for main screen to appear _ = tabBar.waitForExistence(timeout: 10) } } static func ensureOnLoginScreen(app: XCUIApplication) { let usernameField = loginUsernameField(app: app) if usernameField.waitForExistence(timeout: 2) { return } // Handle persisted authenticated sessions first. let mainTabsRoot = app.otherElements[UITestID.Root.mainTabs] if mainTabsRoot.exists || app.tabBars.firstMatch.exists { logout(app: app) if usernameField.waitForExistence(timeout: 8) { return } } // Wait for a stable root state before interacting. let loginRoot = app.otherElements[UITestID.Root.login] let onboardingRoot = app.otherElements[UITestID.Root.onboarding] _ = loginRoot.waitForExistence(timeout: 5) || onboardingRoot.waitForExistence(timeout: 5) if onboardingRoot.exists { // Handle both pure onboarding and onboarding + login sheet. let onboardingLoginButton = app.buttons[AccessibilityIdentifiers.Onboarding.loginButton] if onboardingLoginButton.waitForExistence(timeout: 5) { if onboardingLoginButton.isHittable { onboardingLoginButton.tap() } else { onboardingLoginButton.forceTap() } } else { let welcome = OnboardingWelcomeScreen(app: app) welcome.waitForLoad() welcome.tapAlreadyHaveAccount() } } XCTAssertTrue( usernameField.waitForExistence(timeout: 20), "Expected to reach login screen from current app state" ) } }