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>
2.0 KiB
2.0 KiB
UI Test Generation Prompt Template
Use this prompt when asking an AI to generate a new UI test for PlantGuide.
Prompt
Write a UI test for [FEATURE DESCRIPTION] in PlantGuide.
Requirements
- Inherit from
BaseUITestCase(notXCTestCase) - Import only
XCTest - Mark test methods
@MainActor - Launch with
launchClean(),launchWithMockData(), orlaunchOffline()as appropriate - Navigate using screen objects:
TabBarScreen(app: app).tapCollection() - Locate elements via
UITestID.*identifiers, not localized strings - Wait with
waitForExistence(timeout:),waitUntilHittable(),waitUntilGone() - Never use
sleep() - One assertion focus per test method
- Use Given/When/Then comments for clarity
File Structure
import XCTest
final class [Feature]UITests: BaseUITestCase {
@MainActor
func test[Behavior]() throws {
// Given
launchWithMockData()
let screen = TabBarScreen(app: app).tap[Tab]()
XCTAssertTrue(screen.waitForLoad())
// When
screen.[element].tapWhenReady()
// Then
XCTAssertTrue([assertion])
}
}
Available Screen Objects
TabBarScreen--tapCamera(),tapCollection(),tapToday(),tapSettings()CameraScreen--captureButton,hasValidState()CollectionScreen--searchField,filterButton,viewModeToggle,emptyStateViewTodayScreen--todaySection,overdueSection,emptyStateViewSettingsScreen--clearCacheButton,notificationsToggle,versionInfoPlantDetailScreen--plantName,favoriteButton,editButton,deleteButton
Available Identifiers
See PlantGuideUITests/Foundation/UITestID.swift for the full list.
All identifiers mirror PlantGuide/Core/Utilities/AccessibilityIdentifiers.swift.
If an identifier is missing
- Add it to
AccessibilityIdentifiers.swiftin the app - Add
.accessibilityIdentifier(...)to the view - Mirror it in
UITestID.swiftin the test target