# HoneyDue iOS Tests — How it works Two test targets: | Target | Kind | What it covers | Speed | |--------|------|----------------|-------| | **HoneyDueUITests** | XCUITest | Drives the real app via accessibility IDs. Organized by domain. | slow (per-test app launch + account) | | **HoneyDueAPITests** | standalone unit-test | Pure-API/contract tests (no UI, no app launch). | seconds | ## Running ```bash cd iosApp # Full run: Smoke gate -> Seed -> API -> Parallel UI (8 workers) -> Sweep ./run_ui_tests.sh # Variants ./run_ui_tests.sh "iPhone Air" 6 # device + worker count ./run_ui_tests.sh --smoke # just the smoke gate ./run_ui_tests.sh --only-api # just the fast API target ./run_ui_tests.sh --only-parallel # just the UI suites ``` The parallel phase runs the **whole UI target minus** the four phase-managed suites (`SmokeUITests`, `AppLaunchUITests`, `AAA_SeedTests`, `SuiteZZ_CleanupTests`) via `-skip-testing`. **New suites are picked up automatically** — there is no hand-maintained include list to forget to update. ## Per-test account isolation (the core idea) Every UI test that needs to be logged in subclasses `AuthenticatedUITestCase`, which in `setUp`: 1. mints a **unique, pre-verified Kratos identity** — `uit__@test.honeydue.local` (see `Core/Fixtures/TestAccount.swift`), 2. boots the app **already authenticated** by passing the account's real Kratos token as `--ui-test-session-token` (the app reads it in `UITestRuntime` and calls `DataManager.setAuthToken`). This skips the slow, flaky UI login (~8–12s/test) — the app lands straight on the main tabs. Logged-OUT suites (login/registration/onboarding) still drive the real login UI, since that's what they test, 3. exposes `session` / `cleaner` / `account` for seeding under its **own** token, and in `tearDown` calls `account.delete()`, which **cascades all of the account's data and clears the Kratos identity** in one call. No test shares state with any other, so suites run in parallel at high worker counts with zero data races. (This replaced the old shared-`testuser` model that capped the suite at 2 workers and forced Suite6 to run isolated.) ### Seeding data the UI must SEE A fresh account is **empty at login**. Anything you seed via API *after* login is invisible to the app until a manual refresh. So: - **Creating** a thing through the UI → no setup needed (but note: task and document creation are gated on a residence existing — set `override var requiresResidence: Bool { true }`, which seeds one *before* login and exposes it as `seededResidence`). - **Viewing / editing / deleting an existing** thing → seed it **before login**: ```swift override func seedAccountPreconditions(_ account: TestAccount) { super.seedAccountPreconditions(account) // seeds seededResidence if requiresResidence let r = seededResidence ?? account.seedResidence() myTask = account.seedTask(residenceId: r.id, title: "Edit me") } ``` ## Adding a suite 1. Pick the domain folder (`Task/`, `Residence/`, …). The folder is an Xcode synchronized group, so a new `.swift` file is compiled into the target automatically — no `.pbxproj` editing. 2. Name it `UITests`; subclass `BaseUITestCase` (logged-out surface: login/registration/onboarding) or `AuthenticatedUITestCase` (logged-in feature work). 3. Use accessibility IDs from `iosApp/Helpers/AccessibilityIdentifiers.swift` (shared with the app — the single source of truth). 4. It runs automatically in the parallel phase. Done. For a **pure-API** test (no UI): add it to `HoneyDueAPITests/` instead — it'll run in the fast API phase. ## Known trade-off Per-test account creation costs ~1–2s of setup (Kratos create + login). For a big suite that adds up; if a full run drags, the planned mitigation is a recycled account pool keyed by worker id. Correctness-first today.