import XCTest /// Page object for the main tab view that appears after login. /// /// The app has 4 tabs: Residences, Tasks, Contractors, Documents. /// Profile is accessed via the settings button on the Residences screen. /// /// Tab bar buttons are matched by label because SwiftUI's `.accessibilityIdentifier()` /// on tab content does not propagate to the tab bar button itself. class MainTabScreen: BaseScreen { // MARK: - Tab Elements var residencesTab: XCUIElement { app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Residences'")).firstMatch } var tasksTab: XCUIElement { app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Tasks'")).firstMatch } var contractorsTab: XCUIElement { app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Contractors'")).firstMatch } var documentsTab: XCUIElement { app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Doc'")).firstMatch } /// Settings button on the Residences tab (leads to profile/settings). var settingsButton: XCUIElement { app.buttons[AccessibilityIdentifiers.Navigation.settingsButton] } override var isDisplayed: Bool { residencesTab.waitForExistence(timeout: timeout) } // MARK: - Navigation @discardableResult func goToResidences() -> Self { waitForElement(residencesTab).tap() return self } @discardableResult func goToTasks() -> Self { waitForElement(tasksTab).tap() return self } @discardableResult func goToContractors() -> Self { waitForElement(contractorsTab).tap() return self } @discardableResult func goToDocuments() -> Self { waitForElement(documentsTab).tap() return self } /// Navigates to settings/profile via the settings button on Residences tab. @discardableResult func goToSettings() -> Self { goToResidences() waitForElement(settingsButton).tap() return self } // MARK: - Logout /// Logs out by navigating to settings and tapping the logout button. /// Handles the confirmation alert automatically. func logout() { goToSettings() // The profile sheet uses a SwiftUI List (lazy CollectionView). // The logout button is near the bottom and may not exist in the // accessibility tree until scrolled into view. let logoutButton = app.buttons[AccessibilityIdentifiers.Profile.logoutButton] if !logoutButton.waitForExistence(timeout: 3) { // Scroll down in the sheet to reveal the logout button let collectionView = app.collectionViews.firstMatch if collectionView.exists { for _ in 0..<5 { collectionView.swipeUp() if logoutButton.waitForExistence(timeout: 1) { break } } } } guard logoutButton.waitForExistence(timeout: 5) else { XCTFail("Logout button not found in settings sheet after scrolling") return } if logoutButton.isHittable { logoutButton.tap() } else { logoutButton.forceTap() } // Handle confirmation alert let alert = app.alerts.firstMatch if alert.waitForExistence(timeout: 5) { let confirmLogout = alert.buttons["Log Out"] if confirmLogout.waitForExistence(timeout: 3) { confirmLogout.tap() } } } }