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>
This commit is contained in:
Trey t
2026-02-18 10:36:54 -06:00
parent 681476a499
commit 1ae9c884c8
30 changed files with 1362 additions and 2379 deletions

View File

@@ -9,13 +9,19 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
xcodebuild -project PlantGuide.xcodeproj -scheme PlantGuide -configuration Debug
# Run all tests
xcodebuild test -project PlantGuide.xcodeproj -scheme PlantGuide -destination 'platform=iOS Simulator,name=iPhone 16'
xcodebuild test -project PlantGuide.xcodeproj -scheme PlantGuide -destination 'platform=iOS Simulator,name=iPhone 17'
# Run a single test class
xcodebuild test -project PlantGuide.xcodeproj -scheme PlantGuide -destination 'platform=iOS Simulator,name=iPhone 16' -only-testing:PlantGuideTests/HybridIdentificationUseCaseTests
xcodebuild test -project PlantGuide.xcodeproj -scheme PlantGuide -destination 'platform=iOS Simulator,name=iPhone 17' -only-testing:PlantGuideTests/HybridIdentificationUseCaseTests
# Run a single test method
xcodebuild test -project PlantGuide.xcodeproj -scheme PlantGuide -destination 'platform=iOS Simulator,name=iPhone 16' -only-testing:PlantGuideTests/SavePlantUseCaseTests/testSavePlant_Success
xcodebuild test -project PlantGuide.xcodeproj -scheme PlantGuide -destination 'platform=iOS Simulator,name=iPhone 17' -only-testing:PlantGuideTests/SavePlantUseCaseTests/testSavePlant_Success
# Build UI tests only (compile check, no simulator required)
xcodebuild build-for-testing -project PlantGuide.xcodeproj -scheme PlantGuide -destination 'platform=iOS Simulator,name=iPhone 17' -quiet
# Run all UI tests
xcodebuild test -project PlantGuide.xcodeproj -scheme PlantGuide -destination 'platform=iOS Simulator,name=iPhone 17' -only-testing:PlantGuideUITests
```
## App Structure
@@ -109,3 +115,58 @@ Entities (all CloudKit-compatible with optional attributes):
- Test fixtures available for `Plant`, `CareTask`, `PlantCareSchedule`
- Mock services: `MockPlantCollectionRepository`, `MockNetworkService`, etc.
- In-memory Core Data stack for test isolation: `CoreDataStack(inMemory: true)`
### UI Testing
UI tests live in `PlantGuideUITests/` using a page-object foundation. See [Docs/XCUITest-Authoring.md](Docs/XCUITest-Authoring.md) for the full guide.
**Key patterns:**
- Inherit `BaseUITestCase`, not `XCTestCase`
- Launch with `launchClean()`, `launchWithMockData()`, or `launchOffline()`
- Locate elements via `UITestID.*` identifiers (mirrors `AccessibilityIdentifiers` in app)
- Navigate with screen objects: `TabBarScreen`, `CameraScreen`, `CollectionScreen`, `TodayScreen`, `SettingsScreen`, `PlantDetailScreen`
- Wait with `waitForExistence(timeout:)`, `waitUntilHittable()`, `waitUntilGone()` -- never `sleep()`
- One assertion focus per test method
## Claude GitHub App
### Installation
1. Go to [github.com/apps/claude](https://github.com/apps/claude)
2. Click "Install" and select this repository
3. Grant the requested permissions (read/write for code, issues, and pull requests)
4. Authenticate with your Anthropic account when prompted
### How It Works
- The app reads this `CLAUDE.md` file for project context and contribution rules
- Claude responds to `@claude` mentions in issues and PRs
- No API key configuration needed - authentication is handled through the GitHub App integration
### Triggering Claude
- **Issues**: Mention `@claude` in an issue to request implementation help
- **PRs**: Mention `@claude` to request code review or changes
## Claude Contribution Rules
### Scope
- Work ONLY on the issue explicitly assigned to you.
- One issue = one pull request.
- Do not refactor unrelated code.
- Do not change public APIs unless the issue explicitly says so.
### Safety Rules
- Never auto-merge.
- Never force-push to main.
- Never delete code unless instructed.
- Preserve existing behavior unless tests say otherwise.
### iOS Rules
- Respect Swift concurrency (MainActor, async/await).
- Do not introduce Combine unless already used.
- Prefer pure functions for new logic.
- No new dependencies without approval.
### Output Expectations
Each PR must include:
- Clear summary of changes
- Files touched (with rationale)
- Risks and how to test