# 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 # Registration screen │ └── MainTabScreen.swift # Main tab navigation + settings + logout ├── 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) │ ├── AuthCriticalPathTests.swift # Auth flow validation │ └── NavigationCriticalPathTests.swift # Tab + navigation validation ├── UITestHelpers.swift # Shared login/logout/navigation helpers ├── AccessibilityIdentifiers.swift # UI element IDs (synced with app-side copy) └── README.md # This file ``` ## Test Suites | Suite | Purpose | CI Gate | Target Time | |-------|---------|---------|-------------| | SmokeTests | App launches, basic auth, tab existence | Every PR | <2 min | | AuthCriticalPathTests | Login, logout, registration entry, forgot password | Every PR | <3 min | | NavigationCriticalPathTests | Tab navigation, settings, add buttons | Every PR | <3 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()`, `waitForHittable()`, or `waitForCondition()` 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. The test-side copy must stay in sync with the app-side copy at `iosApp/Helpers/AccessibilityIdentifiers.swift`. ## CI Configuration ### Critical Path (every PR) ```bash xcodebuild test -project iosApp.xcodeproj -scheme iosApp \ -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 17' \ -only-testing:CaseraUITests/SmokeTests \ -only-testing:CaseraUITests/AuthCriticalPathTests \ -only-testing:CaseraUITests/NavigationCriticalPathTests ``` ### Full Regression (nightly) ```bash 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 (zero fixed sleeps) - Test data uses unique identifiers to prevent cross-test interference - UI animations disabled via launch arguments - Element lookups use accessibility identifiers exclusively ## Adding New Tests 1. If the screen does not have a page object yet, create one in `PageObjects/` that extends `BaseScreen`. 2. Define accessibility identifiers in `AccessibilityIdentifiers.swift` for any new UI elements. 3. Sync the app-side copy of `AccessibilityIdentifiers.swift` with matching identifiers. 4. Add test data builders to `TestFixtures.swift` if needed. 5. Write the test in `CriticalPath/` for must-pass CI tests. 6. Verify zero `sleep()` calls before merging.