# 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) ```bash xcodebuild test -project iosApp.xcodeproj -scheme iosApp \ -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 17' \ -only-testing:CaseraUITests/SmokeTests ``` ### 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 (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 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. Add test data builders to `TestFixtures.swift` if needed. 4. Write the test in the appropriate suite file, or create a new suite if the feature is new. 5. For critical-path tests (must pass on every PR), add to `CriticalPath/SmokeTests.swift`.