Remediate all P0-S priority findings from cross-platform architecture audit: - Harden token storage with EncryptedSharedPreferences (Android) and Keychain (iOS) - Add SSL pinning and certificate validation to API clients - Fix subscription cache race conditions and add thread-safe access - Add input validation for document uploads and file type restrictions - Refactor DocumentApi to use proper multipart upload flow - Add rate limiting awareness and retry logic to API layer - Harden subscription tier enforcement in SubscriptionHelper - Add biometric prompt for sensitive actions (Login, Onboarding) - Fix notification permission handling and device registration - Add UI test infrastructure (page objects, fixtures, smoke tests) - Add CI workflow for mobile builds Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3.7 KiB
Casera iOS UI Testing Architecture
Directory Structure
CaseraUITests/
├── PageObjects/ # Screen abstractions (Page Object pattern)
│ ├── BaseScreen.swift # Common wait/assert utilities
│ ├── LoginScreen.swift # Login screen elements and actions
│ ├── RegisterScreen.swift
│ └── MainTabScreen.swift
├── TestConfiguration/ # Launch config, environment setup
│ └── TestLaunchConfig.swift
├── Fixtures/ # Test data builders
│ └── TestFixtures.swift
├── CriticalPath/ # Must-pass tests for CI gating
│ └── SmokeTests.swift # Fast smoke suite (<2 min)
├── Suite0-10_*.swift # Existing comprehensive test suites
├── UITestHelpers.swift # Legacy shared helpers
├── AccessibilityIdentifiers.swift # UI element IDs
└── README.md # This file
Test Suites
| Suite | Purpose | CI Gate | Target Time |
|---|---|---|---|
| SmokeTests | App launches, auth, navigation | Every PR | <2 min |
| Suite0-2 | Onboarding, registration, auth | Nightly | <5 min |
| Suite3-8 | Feature CRUD (residence, task, etc) | Nightly | <15 min |
| Suite9-10 | E2E integration | Weekly | <30 min |
Patterns
Page Object Pattern
Every screen has a corresponding PageObject in PageObjects/. Use these instead of raw XCUIElement queries in tests. Page objects encapsulate element lookups and common actions, making tests more readable and easier to maintain when the UI changes.
Wait Helpers
NEVER use sleep() or Thread.sleep(). Use waitForElement(), waitForElementToDisappear(), or waitForHittable() from BaseScreen. These are condition-based waits that return as soon as the condition is met, making tests both faster and more reliable.
Test Data
Use TestFixtures builders for consistent, unique test data. Random numbers and UUIDs ensure test isolation so tests can run in any order without interfering with each other.
Launch Configuration
Use TestLaunchConfig.launchApp() for standard launches. Use launchAuthenticated() to skip login when the app supports test authentication bypass. The standard configuration disables animations and forces English locale.
Accessibility Identifiers
All interactive elements must have identifiers defined in AccessibilityIdentifiers.swift. Use .accessibilityIdentifier() in SwiftUI views. Page objects reference these identifiers for element lookup.
CI Configuration
Smoke Suite (every PR)
xcodebuild test -project iosApp.xcodeproj -scheme iosApp \
-sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 17' \
-only-testing:CaseraUITests/SmokeTests
Full Regression (nightly)
xcodebuild test -project iosApp.xcodeproj -scheme iosApp \
-sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 17' \
-only-testing:CaseraUITests
Flake Reduction
- Target: <2% flake rate on critical-path suite
- All waits use condition-based predicates (no fixed sleeps)
- Test data uses unique identifiers to prevent cross-test interference
- UI animations disabled via launch arguments
- Element lookups use accessibility identifiers where possible, with predicate-based fallbacks
Adding New Tests
- If the screen does not have a page object yet, create one in
PageObjects/that extendsBaseScreen. - Define accessibility identifiers in
AccessibilityIdentifiers.swiftfor any new UI elements. - Add test data builders to
TestFixtures.swiftif needed. - Write the test in the appropriate suite file, or create a new suite if the feature is new.
- For critical-path tests (must pass on every PR), add to
CriticalPath/SmokeTests.swift.