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>
54 lines
1.2 KiB
Swift
54 lines
1.2 KiB
Swift
//
|
|
// CollectionScreen.swift
|
|
// PlantGuideUITests
|
|
//
|
|
// Screen object for the Collection tab.
|
|
//
|
|
|
|
import XCTest
|
|
|
|
struct CollectionScreen {
|
|
let app: XCUIApplication
|
|
|
|
// MARK: - Elements
|
|
|
|
var navigationBar: XCUIElement { app.navigationBars["My Plants"] }
|
|
|
|
var searchField: XCUIElement { app.searchFields.firstMatch }
|
|
|
|
var viewModeToggle: XCUIElement {
|
|
app.buttons[UITestID.Collection.viewModeToggle]
|
|
}
|
|
|
|
var filterButton: XCUIElement {
|
|
app.buttons[UITestID.Collection.filterButton]
|
|
}
|
|
|
|
var emptyStateView: XCUIElement {
|
|
app.otherElements[UITestID.Collection.emptyStateView]
|
|
}
|
|
|
|
var scrollView: XCUIElement { app.scrollViews.firstMatch }
|
|
|
|
var tableView: XCUIElement { app.tables.firstMatch }
|
|
|
|
// MARK: - State Checks
|
|
|
|
@discardableResult
|
|
func waitForLoad(timeout: TimeInterval = 10) -> Bool {
|
|
navigationBar.waitForExistence(timeout: timeout)
|
|
}
|
|
|
|
var hasPlants: Bool {
|
|
scrollView.exists || tableView.exists
|
|
}
|
|
|
|
// MARK: - Actions
|
|
|
|
func search(_ text: String) {
|
|
XCTAssertTrue(searchField.waitForExistence(timeout: 5), "Search field missing")
|
|
searchField.tap()
|
|
searchField.typeText(text)
|
|
}
|
|
}
|