GenerateSaaS

Testing

The mobile app's two test lanes - the jest-expo unit suite and the Maestro device flows - and how each one gates.

apps/mobile is the one tree in this repo that runs jest, not vitest: React Native components need the jest-expo preset and its native module mocks. Everything else in the repo stays on vitest.

Unit and component tests

FactValue
Commandpnpm --filter mobile test
Locationapps/mobile/tests/**
Presetjest-expo, with Testing Library for React Native
In CIRuns inside the repo-wide test task, like every other workspace

The suite follows the same zero-skip policy as the rest of the repo: gate on config with a plain if, never test.skip - see Writing tests.

apps/mobile/jest.config.js maps the packages jest cannot load on its own - the ESM-only ones, and the native modules that throw on first use. Add a mapping there when a new dependency fails at import rather than reaching for transformIgnorePatterns.

Dependency health

expo-doctor is a maintenance advisor you run by hand. It is in no test lane and no battery leg, because it grades the project against the newest npm patches of the installed SDK, so its verdict moves with every Expo release.

cd apps/mobile && npx expo-doctor
FindingMeaning
SDK patch versions out of dateUpgrade when you choose to: npx expo install --check
eas-cli installed locallyBy design - the release workflow runs it through pnpm exec, pinned

Device flows

FactValue
Commandpnpm --filter mobile e2e:mobile:ci
Same run, simulator left openpnpm --filter mobile e2e:mobile
ToolMaestro, driving a real simulator or emulator
Locationapps/mobile/e2e/flows/**
Inventoryapps/mobile/e2e/tags.ts - the one list of flows and the tags each carries

Tag gating

A flow carries zero or more tags, and your project enables zero or more. A flow runs only when every tag it carries is enabled, so a flow for a feature this build does not ship is never registered.

TagEnabled whenGates
aithe project ships the AI surfacechat, attachments, the thread list, automations
orgthe project is multi-tenantthe organization switch and the member invite
projectsthe project ships projectsthe project switch
pushthe project ships notifications and mobile pushthe notification tap, the deep link and the OS permission prompt
purchasesthe project ships store purchasesthe paywall, a sandbox purchase, restore

Untagged flows - sign-in, sign-up, the settings journeys, the offline banner - run in every project.

A run prints not run: <flow> — <reason> for every flow it skipped, then exits 0. A missing simulator, emulator or maestro binary is a reason, not a failure: the run never passes silently, and never reddens a project that cannot drive a device.

Selectors

Flows select by testID, never by copy, so a translation or a reword never breaks one. apps/mobile/e2e/README.md is the contract: it lists every id a flow may use, and a new screen adds its rows there before a flow can touch it.

Device flows need a built app, not just Metro. Compile the development build first - see Setup.

Metro's file map does not register a file created after it started, so a flow that reaches new code keeps running against the last good bundle. Restart Metro with --clear after adding a file.

On this page