Files
PlantGuide/PlantGuideUITests/Screens/PlantDetailScreen.swift
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

62 lines
1.4 KiB
Swift

//
// PlantDetailScreen.swift
// PlantGuideUITests
//
// Screen object for the Plant Detail view.
//
import XCTest
struct PlantDetailScreen {
let app: XCUIApplication
// MARK: - Elements
var detailView: XCUIElement {
app.otherElements[UITestID.PlantDetail.detailView]
}
var plantName: XCUIElement {
app.staticTexts[UITestID.PlantDetail.plantName]
}
var favoriteButton: XCUIElement {
app.buttons[UITestID.PlantDetail.favoriteButton]
}
var editButton: XCUIElement {
app.buttons[UITestID.PlantDetail.editButton]
}
var deleteButton: XCUIElement {
app.buttons[UITestID.PlantDetail.deleteButton]
}
var careSection: XCUIElement {
app.otherElements[UITestID.PlantDetail.careSection]
}
var tasksSection: XCUIElement {
app.otherElements[UITestID.PlantDetail.tasksSection]
}
/// The back button in navigation bar (leads back to Collection).
var backButton: XCUIElement {
app.navigationBars.buttons.firstMatch
}
// MARK: - State Checks
@discardableResult
func waitForLoad(timeout: TimeInterval = 5) -> Bool {
// Wait for any navigation bar to appear (title is dynamic plant name)
app.navigationBars.firstMatch.waitForExistence(timeout: timeout)
}
// MARK: - Actions
func tapBack() {
backButton.tapWhenReady()
}
}