Add comprehensive UI test suite with XCUITest
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>
This commit is contained in:
212
iosApp/MyCribUITests/AccessibilityIdentifiers.swift
Normal file
212
iosApp/MyCribUITests/AccessibilityIdentifiers.swift
Normal file
@@ -0,0 +1,212 @@
|
||||
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)"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user