import XCTest /// Task management tests. /// Precondition: at least one residence must exist (task creation requires it). final class Suite5_TaskTests: AuthenticatedUITestCase { override var needsAPISession: Bool { true } override var testCredentials: (username: String, password: String) { ("testuser", "TestPass123!") } override var apiCredentials: (username: String, password: String) { ("testuser", "TestPass123!") } override func setUpWithError() throws { try super.setUpWithError() // Precondition: residence must exist for task add button ensureResidenceExists() // Dismiss any open form from previous test let cancelButton = app.buttons[AccessibilityIdentifiers.Task.formCancelButton].firstMatch if cancelButton.exists { cancelButton.tap() } navigateToTasks() // Wait for task screen to load let addButton = app.buttons[AccessibilityIdentifiers.Task.addButton].firstMatch addButton.waitForExistenceOrFail(timeout: navigationTimeout, message: "Task add button should appear") } // MARK: - 1. Validation func test01_cancelTaskCreation() { let addButton = app.buttons[AccessibilityIdentifiers.Task.addButton].firstMatch addButton.tap() let titleField = app.textFields[AccessibilityIdentifiers.Task.titleField].firstMatch titleField.waitForExistenceOrFail(timeout: defaultTimeout, message: "Task form should open") let cancelButton = app.buttons[AccessibilityIdentifiers.Task.formCancelButton].firstMatch cancelButton.waitForExistenceOrFail(timeout: defaultTimeout, message: "Cancel button should exist") cancelButton.tap() // Verify we're back on the task list let addButtonAgain = app.buttons[AccessibilityIdentifiers.Task.addButton].firstMatch XCTAssertTrue(addButtonAgain.waitForExistence(timeout: navigationTimeout), "Should be back on tasks list after cancel") } // MARK: - 2. View/List func test02_tasksTabExists() { let tabBar = app.tabBars.firstMatch XCTAssertTrue(tabBar.exists, "Tab bar should exist") let addButton = app.buttons[AccessibilityIdentifiers.Task.addButton].firstMatch XCTAssertTrue(addButton.exists, "Task add button should exist (proves we're on Tasks tab)") } func test03_viewTasksList() { // Tasks screen should show — verified by the add button existence from setUp let addButton = app.buttons[AccessibilityIdentifiers.Task.addButton].firstMatch XCTAssertTrue(addButton.exists, "Tasks screen should be visible with add button") } func test04_addTaskButtonEnabled() { let addButton = app.buttons[AccessibilityIdentifiers.Task.addButton].firstMatch XCTAssertTrue(addButton.isEnabled, "Task add button should be enabled when residence exists") } func test05_navigateToAddTask() { let addButton = app.buttons[AccessibilityIdentifiers.Task.addButton].firstMatch addButton.tap() let titleField = app.textFields[AccessibilityIdentifiers.Task.titleField].firstMatch titleField.waitForExistenceOrFail(timeout: defaultTimeout, message: "Task title field should appear in add form") let saveButton = app.buttons[AccessibilityIdentifiers.Task.saveButton].firstMatch XCTAssertTrue(saveButton.exists, "Save button should exist in add task form") // Clean up: dismiss form let cancelButton = app.buttons[AccessibilityIdentifiers.Task.formCancelButton].firstMatch if cancelButton.exists { cancelButton.tap() } } // MARK: - 3. Creation func test06_createBasicTask() { let addButton = app.buttons[AccessibilityIdentifiers.Task.addButton].firstMatch addButton.tap() let titleField = app.textFields[AccessibilityIdentifiers.Task.titleField].firstMatch titleField.waitForExistenceOrFail(timeout: defaultTimeout, message: "Task title field should appear") let timestamp = Int(Date().timeIntervalSince1970) let taskTitle = "UITest Task \(timestamp)" fillTextField(identifier: AccessibilityIdentifiers.Task.titleField, text: taskTitle) dismissKeyboard() app.swipeUp() let saveButton = app.buttons[AccessibilityIdentifiers.Task.saveButton].firstMatch saveButton.waitForExistenceOrFail(timeout: defaultTimeout, message: "Save button should exist") saveButton.tap() // Wait for form to dismiss _ = saveButton.waitForNonExistence(timeout: navigationTimeout) // Verify task appears in list (may need refresh or scroll in kanban view) let newTask = app.staticTexts.containing(NSPredicate(format: "label CONTAINS %@", taskTitle)).firstMatch if !newTask.waitForExistence(timeout: navigationTimeout) { pullToRefresh() } XCTAssertTrue(newTask.waitForExistence(timeout: navigationTimeout), "New task '\(taskTitle)' should appear in the list") // Track for cleanup if let items = TestAccountAPIClient.listTasks(token: session.token), let created = items.first(where: { $0.title.contains(taskTitle) }) { cleaner.trackTask(created.id) } } // MARK: - 4. View Details func test07_viewTaskDetails() { // Create a task first let timestamp = Int(Date().timeIntervalSince1970) let taskTitle = "UITest Detail \(timestamp)" let addButton = app.buttons[AccessibilityIdentifiers.Task.addButton].firstMatch addButton.tap() fillTextField(identifier: AccessibilityIdentifiers.Task.titleField, text: taskTitle) dismissKeyboard() app.swipeUp() let saveButton = app.buttons[AccessibilityIdentifiers.Task.saveButton].firstMatch saveButton.waitForExistenceOrFail(timeout: defaultTimeout) saveButton.tap() _ = saveButton.waitForNonExistence(timeout: navigationTimeout) // Find and tap the task (may need refresh) let taskCard = app.staticTexts.containing(NSPredicate(format: "label CONTAINS %@", taskTitle)).firstMatch if !taskCard.waitForExistence(timeout: navigationTimeout) { pullToRefresh() } taskCard.waitForExistenceOrFail(timeout: navigationTimeout, message: "Created task should appear in list") if let items = TestAccountAPIClient.listTasks(token: session.token), let created = items.first(where: { $0.title.contains(taskTitle) }) { cleaner.trackTask(created.id) } taskCard.tap() // After tapping a task, the app should show task details or actions. // The navigation bar title or a detail view element should appear. let navBar = app.navigationBars.firstMatch XCTAssertTrue(navBar.waitForExistence(timeout: navigationTimeout), "Task detail view should load after tap") } // MARK: - 5. Navigation func test08_navigateToContractors() { navigateToContractors() let addButton = app.buttons[AccessibilityIdentifiers.Contractor.addButton].firstMatch XCTAssertTrue(addButton.waitForExistence(timeout: navigationTimeout), "Contractors screen should load") } func test09_navigateToDocuments() { navigateToDocuments() let addButton = app.buttons[AccessibilityIdentifiers.Document.addButton].firstMatch XCTAssertTrue(addButton.waitForExistence(timeout: navigationTimeout), "Documents screen should load") } func test10_navigateBetweenTabs() { navigateToResidences() let resAddButton = app.buttons[AccessibilityIdentifiers.Residence.addButton].firstMatch XCTAssertTrue(resAddButton.waitForExistence(timeout: navigationTimeout), "Residences screen should load") navigateToTasks() let taskAddButton = app.buttons[AccessibilityIdentifiers.Task.addButton].firstMatch XCTAssertTrue(taskAddButton.waitForExistence(timeout: navigationTimeout), "Tasks screen should load after navigating back") } }