Files
Trey t 1ae9c884c8 Rebuild UI test foundation with page objects, wait helpers, and screen objects
Replace brittle localized-string selectors and broken wait helpers with a
robust, identifier-first UI test infrastructure. All 41 UI tests pass on
iOS 26.2 simulator (iPhone 17).

Foundation:
- BaseUITestCase with deterministic launch helpers (launchClean, launchOffline)
- WaitHelpers (waitUntilHittable, waitUntilGone, tapWhenReady) replacing sleep()
- UITestID enum mirroring AccessibilityIdentifiers from the app target
- Screen objects: TabBarScreen, CameraScreen, CollectionScreen, TodayScreen,
  SettingsScreen, PlantDetailScreen

Key fixes:
- Tab navigation uses waitForExistence+tap instead of isHittable (unreliable
  in iOS 26 simulator)
- Tests handle real app state (empty collection, no camera permission)
- Increased timeouts for parallel clone execution
- Added NetworkMonitorProtocol and protocol-typed DI for testability
- Fixed actor-isolation issues in unit test mocks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:36:54 -06:00

128 lines
5.5 KiB
Swift

//
// UITestID.swift
// PlantGuideUITests
//
// Centralized UI test identifiers mirroring AccessibilityIdentifiers in the app.
// Always use these constants to locate elements in UI tests.
//
import Foundation
/// Mirrors `AccessibilityIdentifiers` from the main app target.
/// Keep in sync with PlantGuide/Core/Utilities/AccessibilityIdentifiers.swift
enum UITestID {
// MARK: - Tab Bar
enum TabBar {
static let tabBar = "main_tab_bar"
static let camera = "Camera" // Tab label used by SwiftUI
static let collection = "Collection"
static let today = "Today"
static let settings = "Settings"
}
// MARK: - Camera
enum Camera {
static let captureButton = "camera_capture_button"
static let previewView = "camera_preview_view"
static let switchCameraButton = "camera_switch_button"
static let flashToggleButton = "camera_flash_toggle_button"
static let photoLibraryButton = "camera_photo_library_button"
static let closeButton = "camera_close_button"
static let permissionDeniedView = "camera_permission_denied_view"
static let openSettingsButton = "camera_open_settings_button"
}
// MARK: - Collection
enum Collection {
static let collectionView = "collection_view"
static let searchField = "collection_search_field"
static let viewModeToggle = "collection_view_mode_toggle"
static let filterButton = "collection_filter_button"
static let gridView = "collection_grid_view"
static let listView = "collection_list_view"
static let emptyStateView = "collection_empty_state_view"
static let loadingIndicator = "collection_loading_indicator"
static let plantGridItem = "collection_plant_grid_item"
static let plantListRow = "collection_plant_list_row"
static let favoriteButton = "collection_favorite_button"
static let deleteAction = "collection_delete_action"
}
// MARK: - Identification
enum Identification {
static let identificationView = "identification_view"
static let imagePreview = "identification_image_preview"
static let loadingIndicator = "identification_loading_indicator"
static let resultsContainer = "identification_results_container"
static let predictionRow = "identification_prediction_row"
static let confidenceIndicator = "identification_confidence_indicator"
static let retryButton = "identification_retry_button"
static let returnToCameraButton = "identification_return_to_camera_button"
static let saveToCollectionButton = "identification_save_to_collection_button"
static let identifyAgainButton = "identification_identify_again_button"
static let closeButton = "identification_close_button"
static let errorView = "identification_error_view"
}
// MARK: - Plant Detail
enum PlantDetail {
static let detailView = "plant_detail_view"
static let headerSection = "plant_detail_header_section"
static let plantImage = "plant_detail_plant_image"
static let plantName = "plant_detail_plant_name"
static let scientificName = "plant_detail_scientific_name"
static let familyName = "plant_detail_family_name"
static let favoriteButton = "plant_detail_favorite_button"
static let careSection = "plant_detail_care_section"
static let wateringInfo = "plant_detail_watering_info"
static let lightInfo = "plant_detail_light_info"
static let humidityInfo = "plant_detail_humidity_info"
static let tasksSection = "plant_detail_tasks_section"
static let editButton = "plant_detail_edit_button"
static let deleteButton = "plant_detail_delete_button"
}
// MARK: - Care Schedule
enum CareSchedule {
static let scheduleView = "care_schedule_view"
static let todaySection = "care_schedule_today_section"
static let upcomingSection = "care_schedule_upcoming_section"
static let overdueSection = "care_schedule_overdue_section"
static let taskRow = "care_schedule_task_row"
static let completeAction = "care_schedule_complete_action"
static let snoozeAction = "care_schedule_snooze_action"
static let addTaskButton = "care_schedule_add_task_button"
static let emptyStateView = "care_schedule_empty_state_view"
}
// MARK: - Settings
enum Settings {
static let settingsView = "settings_view"
static let notificationsToggle = "settings_notifications_toggle"
static let appearanceSection = "settings_appearance_section"
static let dataSection = "settings_data_section"
static let clearCacheButton = "settings_clear_cache_button"
static let versionInfo = "settings_version_info"
}
// MARK: - Common
enum Common {
static let loadingIndicator = "common_loading_indicator"
static let errorView = "common_error_view"
static let retryButton = "common_retry_button"
static let closeButton = "common_close_button"
static let backButton = "common_back_button"
static let doneButton = "common_done_button"
static let cancelButton = "common_cancel_button"
}
}