Added complete UI test suite covering authentication, residences, tasks, and contractors. Tests follow best practices with helper methods, proper waits, and accessibility identifier usage. New test files: - UITestHelpers.swift: Shared helper methods for login, navigation, waits - AuthenticationTests.swift: Login, registration, logout flow tests - ComprehensiveResidenceTests.swift: Full residence CRUD and validation tests - ComprehensiveTaskTests.swift: Task creation, editing, completion tests - ComprehensiveContractorTests.swift: Contractor management and edge case tests - ResidenceTests.swift: Additional residence-specific scenarios - TaskTests.swift: Additional task scenarios - SimpleLoginTest.swift: Basic smoke test for CI/CD - MyCribUITests.swift: Base test class setup - AccessibilityIdentifiers.swift: Test target copy of identifiers Test coverage: - Authentication: Login, registration, logout, error handling - Residences: Create, edit, delete, validation, multi-field scenarios - Tasks: Create, complete, edit, cancel, status changes - Contractors: Create with minimal/full data, phone formats, specialties All tests use accessibility identifiers for reliable element location and include proper waits for asynchronous operations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
213 lines
9.3 KiB
Swift
213 lines
9.3 KiB
Swift
import Foundation
|
|
|
|
/// Centralized accessibility identifiers for UI testing
|
|
/// These identifiers are used by XCUITests to locate and interact with UI elements
|
|
struct AccessibilityIdentifiers {
|
|
|
|
// MARK: - Authentication
|
|
struct Authentication {
|
|
static let usernameField = "Login.UsernameField"
|
|
static let passwordField = "Login.PasswordField"
|
|
static let loginButton = "Login.LoginButton"
|
|
static let signUpButton = "Login.SignUpButton"
|
|
static let forgotPasswordButton = "Login.ForgotPasswordButton"
|
|
static let passwordVisibilityToggle = "Login.PasswordVisibilityToggle"
|
|
|
|
// Registration
|
|
static let registerUsernameField = "Register.UsernameField"
|
|
static let registerEmailField = "Register.EmailField"
|
|
static let registerPasswordField = "Register.PasswordField"
|
|
static let registerConfirmPasswordField = "Register.ConfirmPasswordField"
|
|
static let registerButton = "Register.RegisterButton"
|
|
static let registerCancelButton = "Register.CancelButton"
|
|
|
|
// Verification
|
|
static let verificationCodeField = "Verification.CodeField"
|
|
static let verifyButton = "Verification.VerifyButton"
|
|
static let resendCodeButton = "Verification.ResendButton"
|
|
}
|
|
|
|
// MARK: - Navigation
|
|
struct Navigation {
|
|
static let residencesTab = "TabBar.Residences"
|
|
static let tasksTab = "TabBar.Tasks"
|
|
static let contractorsTab = "TabBar.Contractors"
|
|
static let documentsTab = "TabBar.Documents"
|
|
static let profileTab = "TabBar.Profile"
|
|
static let backButton = "Navigation.BackButton"
|
|
}
|
|
|
|
// MARK: - Residence
|
|
struct Residence {
|
|
// List
|
|
static let addButton = "Residence.AddButton"
|
|
static let residencesList = "Residence.List"
|
|
static let residenceCard = "Residence.Card"
|
|
static let emptyStateView = "Residence.EmptyState"
|
|
static let emptyStateButton = "Residence.EmptyState.AddButton"
|
|
|
|
// Form
|
|
static let nameField = "ResidenceForm.NameField"
|
|
static let propertyTypePicker = "ResidenceForm.PropertyTypePicker"
|
|
static let streetAddressField = "ResidenceForm.StreetAddressField"
|
|
static let apartmentUnitField = "ResidenceForm.ApartmentUnitField"
|
|
static let cityField = "ResidenceForm.CityField"
|
|
static let stateProvinceField = "ResidenceForm.StateProvinceField"
|
|
static let postalCodeField = "ResidenceForm.PostalCodeField"
|
|
static let countryField = "ResidenceForm.CountryField"
|
|
static let bedroomsField = "ResidenceForm.BedroomsField"
|
|
static let bathroomsField = "ResidenceForm.BathroomsField"
|
|
static let squareFootageField = "ResidenceForm.SquareFootageField"
|
|
static let lotSizeField = "ResidenceForm.LotSizeField"
|
|
static let yearBuiltField = "ResidenceForm.YearBuiltField"
|
|
static let descriptionField = "ResidenceForm.DescriptionField"
|
|
static let isPrimaryToggle = "ResidenceForm.IsPrimaryToggle"
|
|
static let saveButton = "ResidenceForm.SaveButton"
|
|
static let formCancelButton = "ResidenceForm.CancelButton"
|
|
|
|
// Detail
|
|
static let detailView = "ResidenceDetail.View"
|
|
static let editButton = "ResidenceDetail.EditButton"
|
|
static let deleteButton = "ResidenceDetail.DeleteButton"
|
|
static let shareButton = "ResidenceDetail.ShareButton"
|
|
static let manageUsersButton = "ResidenceDetail.ManageUsersButton"
|
|
static let tasksSection = "ResidenceDetail.TasksSection"
|
|
static let addTaskButton = "ResidenceDetail.AddTaskButton"
|
|
}
|
|
|
|
// MARK: - Task
|
|
struct Task {
|
|
// List/Kanban
|
|
static let addButton = "Task.AddButton"
|
|
static let tasksList = "Task.List"
|
|
static let taskCard = "Task.Card"
|
|
static let emptyStateView = "Task.EmptyState"
|
|
static let kanbanView = "Task.KanbanView"
|
|
static let overdueColumn = "Task.Column.Overdue"
|
|
static let upcomingColumn = "Task.Column.Upcoming"
|
|
static let inProgressColumn = "Task.Column.InProgress"
|
|
static let completedColumn = "Task.Column.Completed"
|
|
|
|
// Form
|
|
static let titleField = "TaskForm.TitleField"
|
|
static let descriptionField = "TaskForm.DescriptionField"
|
|
static let categoryPicker = "TaskForm.CategoryPicker"
|
|
static let frequencyPicker = "TaskForm.FrequencyPicker"
|
|
static let priorityPicker = "TaskForm.PriorityPicker"
|
|
static let statusPicker = "TaskForm.StatusPicker"
|
|
static let dueDatePicker = "TaskForm.DueDatePicker"
|
|
static let intervalDaysField = "TaskForm.IntervalDaysField"
|
|
static let estimatedCostField = "TaskForm.EstimatedCostField"
|
|
static let residencePicker = "TaskForm.ResidencePicker"
|
|
static let saveButton = "TaskForm.SaveButton"
|
|
static let formCancelButton = "TaskForm.CancelButton"
|
|
|
|
// Detail
|
|
static let detailView = "TaskDetail.View"
|
|
static let editButton = "TaskDetail.EditButton"
|
|
static let deleteButton = "TaskDetail.DeleteButton"
|
|
static let markInProgressButton = "TaskDetail.MarkInProgressButton"
|
|
static let completeButton = "TaskDetail.CompleteButton"
|
|
static let detailCancelButton = "TaskDetail.CancelButton"
|
|
|
|
// Completion
|
|
static let completionDatePicker = "TaskCompletion.CompletionDatePicker"
|
|
static let actualCostField = "TaskCompletion.ActualCostField"
|
|
static let ratingView = "TaskCompletion.RatingView"
|
|
static let notesField = "TaskCompletion.NotesField"
|
|
static let photosPicker = "TaskCompletion.PhotosPicker"
|
|
static let submitButton = "TaskCompletion.SubmitButton"
|
|
}
|
|
|
|
// MARK: - Contractor
|
|
struct Contractor {
|
|
static let addButton = "Contractor.AddButton"
|
|
static let contractorsList = "Contractor.List"
|
|
static let contractorCard = "Contractor.Card"
|
|
static let emptyStateView = "Contractor.EmptyState"
|
|
|
|
// Form
|
|
static let nameField = "ContractorForm.NameField"
|
|
static let companyField = "ContractorForm.CompanyField"
|
|
static let emailField = "ContractorForm.EmailField"
|
|
static let phoneField = "ContractorForm.PhoneField"
|
|
static let specialtyPicker = "ContractorForm.SpecialtyPicker"
|
|
static let ratingView = "ContractorForm.RatingView"
|
|
static let notesField = "ContractorForm.NotesField"
|
|
static let saveButton = "ContractorForm.SaveButton"
|
|
static let formCancelButton = "ContractorForm.CancelButton"
|
|
|
|
// Detail
|
|
static let detailView = "ContractorDetail.View"
|
|
static let editButton = "ContractorDetail.EditButton"
|
|
static let deleteButton = "ContractorDetail.DeleteButton"
|
|
static let callButton = "ContractorDetail.CallButton"
|
|
static let emailButton = "ContractorDetail.EmailButton"
|
|
}
|
|
|
|
// MARK: - Document
|
|
struct Document {
|
|
static let addButton = "Document.AddButton"
|
|
static let documentsList = "Document.List"
|
|
static let documentCard = "Document.Card"
|
|
static let emptyStateView = "Document.EmptyState"
|
|
|
|
// Form
|
|
static let titleField = "DocumentForm.TitleField"
|
|
static let typePicker = "DocumentForm.TypePicker"
|
|
static let categoryPicker = "DocumentForm.CategoryPicker"
|
|
static let residencePicker = "DocumentForm.ResidencePicker"
|
|
static let filePicker = "DocumentForm.FilePicker"
|
|
static let notesField = "DocumentForm.NotesField"
|
|
static let expirationDatePicker = "DocumentForm.ExpirationDatePicker"
|
|
static let saveButton = "DocumentForm.SaveButton"
|
|
static let formCancelButton = "DocumentForm.CancelButton"
|
|
|
|
// Detail
|
|
static let detailView = "DocumentDetail.View"
|
|
static let editButton = "DocumentDetail.EditButton"
|
|
static let deleteButton = "DocumentDetail.DeleteButton"
|
|
static let shareButton = "DocumentDetail.ShareButton"
|
|
static let downloadButton = "DocumentDetail.DownloadButton"
|
|
}
|
|
|
|
// MARK: - Profile
|
|
struct Profile {
|
|
static let logoutButton = "Profile.LogoutButton"
|
|
static let editProfileButton = "Profile.EditProfileButton"
|
|
static let settingsButton = "Profile.SettingsButton"
|
|
static let notificationsToggle = "Profile.NotificationsToggle"
|
|
static let darkModeToggle = "Profile.DarkModeToggle"
|
|
static let aboutButton = "Profile.AboutButton"
|
|
static let helpButton = "Profile.HelpButton"
|
|
}
|
|
|
|
// MARK: - Alerts & Modals
|
|
struct Alert {
|
|
static let confirmButton = "Alert.ConfirmButton"
|
|
static let cancelButton = "Alert.CancelButton"
|
|
static let deleteButton = "Alert.DeleteButton"
|
|
static let okButton = "Alert.OKButton"
|
|
}
|
|
|
|
// MARK: - Common
|
|
struct Common {
|
|
static let loadingIndicator = "Common.LoadingIndicator"
|
|
static let errorView = "Common.ErrorView"
|
|
static let retryButton = "Common.RetryButton"
|
|
static let searchField = "Common.SearchField"
|
|
static let filterButton = "Common.FilterButton"
|
|
static let sortButton = "Common.SortButton"
|
|
static let refreshControl = "Common.RefreshControl"
|
|
}
|
|
}
|
|
|
|
// MARK: - Helper Extension
|
|
extension String {
|
|
/// Convenience method to generate dynamic identifiers
|
|
/// Example: "Residence.Card.\(residenceId)"
|
|
func withId(_ id: Any) -> String {
|
|
return "\(self).\(id)"
|
|
}
|
|
}
|