Files
honeyDueKMP/iosApp/CaseraUITests
treyt 4679764fdf Fix test build errors: isCacheValid ttlMs parameter and screen object name conflicts
SKIE doesn't expose Kotlin default parameters to Swift, so isCacheValid calls
need explicit ttlMs argument. Renamed struct-based screen objects to avoid
ambiguity with class-based PageObjects (LoginScreenObject, RegisterScreenObject,
MainTabScreenObject).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 15:59:48 -06:00
..
2025-11-28 21:10:38 -06:00

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)

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)

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.