Skip to content

iOS: add an XCUI test suite with a fixture/mock test mode #24

Description

@roznet

The iOS app has unit tests (flyfun-formsTests) but no UI tests, and no iOS job runs in CI. This issue adds an XCUI suite built on the pattern flyfun-weather already uses (flyfun-weatherUITests), carrying over the lessons that suite learned the hard way.

Test mode (DEBUG only, launch environment)

  • FLYFUN_UITEST=1
    • Skips the auth gate by using an in-memory BearerTokenStore, never the keychain.
    • Uses an in-memory ModelContainer with no CloudKit, seeded from fixtures: people with multi-country and expired documents, aircraft, upcoming and past flights, and a two-leg trip. This is the one non-negotiable change: without it, tests would write fake passports into whatever iCloud account the simulator is signed into, and state would leak between runs.
    • Resets the UserDefaults the UI reads, such as emailLanguage and document overrides.
  • FLYFUN_MOCK=1
    • Registers a URLProtocol stub that serves canned responses for /airports, /airports/{icao}, /validate, /generate, /prefill, /email-text and the maps.flyfun.aero notifications.
    • Every call in the app goes through URLSession.shared, including RollingBearerSession, so the stub needs no refactor of FormService.
    • Any URL without a stub fails loudly instead of reaching the network.
  • FLYFUN_UITEST_CAPTURE_DIR=<path>
    • The stub writes each request body it receives to this directory, so a journey can check the request the app generated (the server's output is already covered by the Python snapshot tests).

Tier 1 journeys (iPhone; macOS out of scope for now)

  1. Launch smoke: the People, Aircraft and Flights tabs show the seeded data.
  2. New flight flow: route, then people with the "Same crew as…" suggestion, and the flight is created.
  3. Paste an ICAO flight plan: the fields fill, and an unknown registration creates a new aircraft.
  4. A schedule edit that crosses UTC midnight moves the day (guards the departureDate/departureInstant dual-write).
  5. Generate a form: the captured /generate request carries the flight, aircraft and people, and the preview appears.
  6. A stubbed 422 shows human-readable validation errors ("Crew 1 — ID Number").
  7. Person and document CRUD: add a person and a passport; the expiry state shows.
  8. Return flight, next leg and duplicate each open the new flight with the right route.

Out of scope: system UI (Apple/Google sign-in, contacts picker, camera/MRZ, mail composer) and CloudKit sync.

Lessons carried over from flyfun-weather

  • Pin portrait: the CI simulator boots in landscape, and lazy Form rows then drop out of the accessibility tree.
  • Swipe rows into view before asserting on them.
  • Never launch without mocks.
  • Select pickers by identifier.

CI

  • A shared scheme.
  • A PR gate that runs the unit tests only.
  • A nightly workflow that runs the UI suite, fails when zero tests ran, and uploads the .xcresult.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions