Remediate all P0-S priority findings from cross-platform architecture audit: - Harden token storage with EncryptedSharedPreferences (Android) and Keychain (iOS) - Add SSL pinning and certificate validation to API clients - Fix subscription cache race conditions and add thread-safe access - Add input validation for document uploads and file type restrictions - Refactor DocumentApi to use proper multipart upload flow - Add rate limiting awareness and retry logic to API layer - Harden subscription tier enforcement in SubscriptionHelper - Add biometric prompt for sensitive actions (Login, Onboarding) - Fix notification permission handling and device registration - Add UI test infrastructure (page objects, fixtures, smoke tests) - Add CI workflow for mobile builds Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
89 lines
2.5 KiB
Swift
89 lines
2.5 KiB
Swift
import XCTest
|
|
|
|
/// Page object for the main tab view that appears after login.
|
|
///
|
|
/// Provides navigation to each tab (Residences, Tasks, Contractors, Documents, Profile)
|
|
/// and a logout flow. Uses predicate-based element lookup to match the existing test patterns.
|
|
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] 'Documents'")).firstMatch
|
|
}
|
|
|
|
var profileTab: XCUIElement {
|
|
app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Profile'")).firstMatch
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
@discardableResult
|
|
func goToProfile() -> Self {
|
|
waitForElement(profileTab).tap()
|
|
return self
|
|
}
|
|
|
|
// MARK: - Logout
|
|
|
|
/// Logs out by navigating to the Profile tab and tapping the logout button.
|
|
/// Handles the confirmation alert automatically.
|
|
func logout() {
|
|
goToProfile()
|
|
|
|
let logoutButton = app.buttons[AccessibilityIdentifiers.Profile.logoutButton]
|
|
if logoutButton.waitForExistence(timeout: 5) {
|
|
waitForHittable(logoutButton).tap()
|
|
|
|
// Handle confirmation alert
|
|
let alert = app.alerts.firstMatch
|
|
if alert.waitForExistence(timeout: 3) {
|
|
let confirmLogout = alert.buttons["Log Out"]
|
|
if confirmLogout.exists {
|
|
confirmLogout.tap()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|