import XCTest /// Task management tests /// Uses UITestHelpers for consistent login/logout behavior /// IMPORTANT: Tasks require at least one residence to exist /// /// Test Order (least to most complex): /// 1. Error/incomplete data tests /// 2. Creation tests /// 3. Edit/update tests /// 4. Delete/remove tests (none currently) /// 5. Navigation/view tests final class Suite5_TaskTests: XCTestCase { var app: XCUIApplication! override func setUpWithError() throws { continueAfterFailure = false app = XCUIApplication() app.launch() // Ensure user is logged in UITestHelpers.ensureLoggedIn(app: app) // CRITICAL: Ensure at least one residence exists // Tasks are disabled if no residences exist ensureResidenceExists() // Now navigate to Tasks tab navigateToTasksTab() } override func tearDownWithError() throws { app = nil } // MARK: - Helper Methods /// Ensures at least one residence exists (required for tasks to work) private func ensureResidenceExists() { // Navigate to Residences tab let residencesTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Residences'")).firstMatch if residencesTab.waitForExistence(timeout: 5) { residencesTab.tap() sleep(2) // Check if we have any residences // Look for the add button - if we see "Add a property" text or empty state, create one let emptyStateText = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'No properties' OR label CONTAINS[c] 'No residences'")).firstMatch if emptyStateText.exists { // No residences exist, create a quick one let addButton = app.buttons[AccessibilityIdentifiers.Residence.addButton] if addButton.waitForExistence(timeout: 5) { addButton.tap() sleep(2) // Fill minimal required fields let nameField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'Name'")).firstMatch if nameField.waitForExistence(timeout: 5) { nameField.tap() nameField.typeText("Test Home for Tasks") // Scroll to address fields app.swipeUp() sleep(1) // Fill required address fields let streetField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'Street'")).firstMatch if streetField.exists { streetField.tap() streetField.typeText("123 Test St") } let cityField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'City'")).firstMatch if cityField.exists { cityField.tap() cityField.typeText("TestCity") } let stateField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'State'")).firstMatch if stateField.exists { stateField.tap() stateField.typeText("TS") } let postalField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'Postal' OR placeholderValue CONTAINS[c] 'Zip'")).firstMatch if postalField.exists { postalField.tap() postalField.typeText("12345") } // Save let saveButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Save'")).firstMatch if saveButton.exists { saveButton.tap() sleep(3) // Wait for save to complete } } } } } } private func navigateToTasksTab() { let tasksTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Tasks'")).firstMatch if tasksTab.waitForExistence(timeout: 5) { if !tasksTab.isSelected { tasksTab.tap() sleep(3) // Give it time to load } } } /// Finds the Add Task button using multiple strategies /// The button exists in two places: /// 1. Toolbar (always visible when residences exist) /// 2. Empty state (visible when no tasks exist) private func findAddTaskButton() -> XCUIElement { sleep(2) // Wait for screen to fully render // Strategy 1: Try accessibility identifier let addButtonById = app.buttons[AccessibilityIdentifiers.Task.addButton].firstMatch if addButtonById.exists && addButtonById.isEnabled { return addButtonById } // Strategy 2: Look for toolbar add button (navigation bar plus button) let navBarButtons = app.navigationBars.buttons for i in 0..