import XCTest /// Comprehensive contractor testing suite covering all scenarios, edge cases, and variations /// This test suite is designed to be bulletproof and catch regressions early final class Suite7_ContractorTests: AuthenticatedUITestCase { override var needsAPISession: Bool { true } override var testCredentials: (username: String, password: String) { ("testuser", "TestPass123!") } override var apiCredentials: (username: String, password: String) { ("testuser", "TestPass123!") } // Test data tracking var createdContractorNames: [String] = [] override func setUpWithError() throws { try super.setUpWithError() // Dismiss any open form from previous test let cancelButton = app.buttons[AccessibilityIdentifiers.Contractor.formCancelButton].firstMatch if cancelButton.exists { cancelButton.tap() } navigateToContractors() contractorList.addButton.waitForExistenceOrFail(timeout: navigationTimeout, message: "Contractor add button should appear") } override func tearDownWithError() throws { // Ensure all UI-created contractors are tracked for API cleanup if !createdContractorNames.isEmpty, let allContractors = TestAccountAPIClient.listContractors(token: session.token) { for name in createdContractorNames { if let contractor = allContractors.first(where: { $0.name.contains(name) }) { cleaner.trackContractor(contractor.id) } } } createdContractorNames.removeAll() try super.tearDownWithError() } // MARK: - Page Objects private var contractorList: ContractorListScreen { ContractorListScreen(app: app) } private var contractorForm: ContractorFormScreen { ContractorFormScreen(app: app) } private var contractorDetail: ContractorDetailScreen { ContractorDetailScreen(app: app) } // MARK: - Helper Methods private func openContractorForm(file: StaticString = #filePath, line: UInt = #line) { let addButton = contractorList.addButton addButton.waitForExistenceOrFail(timeout: defaultTimeout, message: "Contractor add button should exist", file: file, line: line) addButton.tap() contractorForm.nameField.waitForExistenceOrFail(timeout: defaultTimeout, message: "Contractor form should open", file: file, line: line) } private func findAddContractorButton() -> XCUIElement { return contractorList.addButton } private func fillTextField(identifier: String, text: String) { let field = app.textFields[identifier].firstMatch guard field.waitForExistence(timeout: defaultTimeout) else { return } if !field.isHittable { app.swipeUp() _ = field.waitForExistence(timeout: defaultTimeout) } field.focusAndType(text, app: app) } private func selectSpecialty(specialty: String) { let specialtyPicker = app.buttons[AccessibilityIdentifiers.Contractor.specialtyPicker].firstMatch guard specialtyPicker.waitForExistence(timeout: defaultTimeout) else { return } specialtyPicker.tap() // Specialty picker is a sheet with checkboxes let option = app.staticTexts[specialty] if option.waitForExistence(timeout: navigationTimeout) { option.tap() } // Dismiss the sheet by tapping Done let doneButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Done'")).firstMatch if doneButton.waitForExistence(timeout: defaultTimeout) { doneButton.tap() } } private func createContractor( name: String, phone: String? = nil, email: String? = nil, company: String? = nil, specialty: String? = nil ) { openContractorForm() contractorForm.enterName(name) dismissKeyboard() if let phone = phone { fillTextField(identifier: AccessibilityIdentifiers.Contractor.phoneField, text: phone) dismissKeyboard() } if let email = email { fillTextField(identifier: AccessibilityIdentifiers.Contractor.emailField, text: email) dismissKeyboard() } if let company = company { fillTextField(identifier: AccessibilityIdentifiers.Contractor.companyField, text: company) dismissKeyboard() } if let specialty = specialty { selectSpecialty(specialty: specialty) } app.swipeUp() let submitButton = contractorForm.saveButton submitButton.waitForExistenceOrFail(timeout: defaultTimeout, message: "Save button should exist") submitButton.tap() _ = submitButton.waitForNonExistence(timeout: navigationTimeout) createdContractorNames.append(name) if let items = TestAccountAPIClient.listContractors(token: session.token), let created = items.first(where: { $0.name.contains(name) }) { cleaner.trackContractor(created.id) } } private func findContractor(name: String, scrollIfNeeded: Bool = true) -> XCUIElement { let element = contractorList.findContractor(name: name) if element.exists && element.isHittable { return element } guard scrollIfNeeded else { return element } let scrollView = app.scrollViews.firstMatch guard scrollView.exists else { return element } scrollView.swipeDown(velocity: .fast) usleep(30_000) var lastVisibleRow = "" for _ in 0..