Background
The mobile app has 95 Jest files under mobile/__tests__/, all unit/component level. They are genuinely good — jest.setup.js provides an in-memory expo-file-system mock reachable as globalThis.mockFileSystem, so the real src/utils/fs.ts logic runs rather than being stubbed out. What none of them do is exercise the app on a device or emulator.
For comparison, the webapp has 21 Playwright specs (webapp/e2e/tests/) covering complete browser workflows. Mobile has zero equivalent coverage, and neither Detox nor Maestro appears in mobile/package.json.
There is already an artifact to test against: .github/workflows/mobile-apk.yml builds a release APK on master pushes and v* tags, using expo prebuild + gradle on ubuntu-latest with NDK/CMake pre-installed. A device-level suite could run against that build rather than standing up its own.
Why it matters here
The mobile app carries the most state-heavy logic in the repo, and it is exactly the logic that unit tests model rather than exercise:
- Offline write queue and replay —
src/db/syncQueue.ts (1,018 lines), src/db/noteQueries.ts (1,045 lines). Correctness depends on real SQLite, real connectivity transitions, and app backgrounding/restart, all of which are simulated today.
- Multi-server connect flow —
src/screens/ConnectToServerScreen.tsx (756 lines) plus src/store/upgradeToServer.ts (525 lines), covering local-only → server-backed migration.
- Share-to-note intents (Android/iOS) and app-icon quick actions — both are OS-level integrations that cannot be tested in Jest at all.
- SSE reconnect behaviour across network loss and process lifecycle.
See docs/specs/mobile-connectivity-handling.md for the invariants that currently have no end-to-end verification.
Scope of the investigation
- Compare Maestro vs Detox for this app: setup cost against an Expo prebuild project, flakiness, maintenance, and whether either can drive the OS-level share sheet and app-icon shortcuts.
- Determine CI cost: Android emulator boot + test time on
ubuntu-latest, and whether that fits a PR gate or belongs on a nightly/master-only schedule. (iOS runners are considerably more expensive — decide whether the investigation covers iOS at all or Android only for now.)
- Pick the smallest set of flows worth covering first. Candidate smoke path: connect to server → create note → go offline → edit → come back online → verify the edit replayed.
- Decide how it hangs off the existing tooling — a
task test-mobile-e2e sibling to task test-e2e, and whether it reuses the APK from mobile-apk.yml or builds its own.
- Decide the fixture story: the webapp e2e suite spins up a throwaway Go server via Playwright's
webServer. A mobile suite needs the same thing reachable from an emulator.
Acceptance
- A written recommendation (framework, CI placement, initial flow set, expected runtime), with the option of concluding that the emulator cost is not worth it right now.
- If adopted: one smoke flow green in CI, plus documentation in
mobile/CLAUDE.md and the README following the pattern task test-e2e uses (including how to install prerequisites, mirroring scripts/check-playwright-browser.sh).
Out of scope
- Replacing any existing Jest coverage — device-level tests are additive, aimed at the flows Jest structurally cannot reach.
- App store / release automation.
Background
The mobile app has 95 Jest files under
mobile/__tests__/, all unit/component level. They are genuinely good —jest.setup.jsprovides an in-memoryexpo-file-systemmock reachable asglobalThis.mockFileSystem, so the realsrc/utils/fs.tslogic runs rather than being stubbed out. What none of them do is exercise the app on a device or emulator.For comparison, the webapp has 21 Playwright specs (
webapp/e2e/tests/) covering complete browser workflows. Mobile has zero equivalent coverage, and neither Detox nor Maestro appears inmobile/package.json.There is already an artifact to test against:
.github/workflows/mobile-apk.ymlbuilds a release APK onmasterpushes andv*tags, usingexpo prebuild+ gradle onubuntu-latestwith NDK/CMake pre-installed. A device-level suite could run against that build rather than standing up its own.Why it matters here
The mobile app carries the most state-heavy logic in the repo, and it is exactly the logic that unit tests model rather than exercise:
src/db/syncQueue.ts(1,018 lines),src/db/noteQueries.ts(1,045 lines). Correctness depends on real SQLite, real connectivity transitions, and app backgrounding/restart, all of which are simulated today.src/screens/ConnectToServerScreen.tsx(756 lines) plussrc/store/upgradeToServer.ts(525 lines), covering local-only → server-backed migration.See
docs/specs/mobile-connectivity-handling.mdfor the invariants that currently have no end-to-end verification.Scope of the investigation
ubuntu-latest, and whether that fits a PR gate or belongs on a nightly/master-only schedule. (iOS runners are considerably more expensive — decide whether the investigation covers iOS at all or Android only for now.)task test-mobile-e2esibling totask test-e2e, and whether it reuses the APK frommobile-apk.ymlor builds its own.webServer. A mobile suite needs the same thing reachable from an emulator.Acceptance
mobile/CLAUDE.mdand the README following the patterntask test-e2euses (including how to install prerequisites, mirroringscripts/check-playwright-browser.sh).Out of scope