Testing
NativeExpress has two layers of tests: a small Jest unit suite, and a set of Maestro end-to-end flows that drive the real app. The E2E flows are the substantial part.
Unit tests
Jest with jest-expo and React Native Testing Library.
yarn test
yarn test:watchThe shipped suite is deliberately small: a handful of tests covering pure helpers and one component. Its job is to leave the harness configured so you can add your own tests without setting one up first. Treat it as a starting point rather than a measure of coverage.
jest.config.js extends jest-expo’s transformIgnorePatterns to cover packages that publish untranspiled source, including heroui-native, uniwind and react-native-marked. If you add a dependency and Jest fails with a syntax error inside node_modules, add it to that list.
End-to-end flows
Six Maestro flows live in .maestro/:
| Flow | Covers |
|---|---|
smoke.yaml | Launch → all five tabs reachable, one stable element asserted per tab |
auth-signup.yaml | Welcome → create account → email/password → lands in the app |
chat-send.yaml | Chats → new chat → type → send → assistant response appears |
create-generate.yaml | Create → prompt → generate → result or Pro-required state |
scan-history.yaml | Scan → history visible (list or empty state) |
screenshots.yaml | Store-screenshot driver |
Running them
Install Maestro
curl -Ls "https://get.maestro.mobile.dev" | bashBuild a preview or release build
eas build --profile previewRun flows against a preview or release build, not a dev client. A dev-client build shows a floating dev-menu button that can overlap top-right controls, so taps on header actions and close buttons may silently miss.
Run one flow, or all of them
maestro test .maestro/smoke.yaml
maestro test .maestro/.maestro/config.yaml sets the default appId for the workspace. Change it to your own bundle identifier.
Verify
smoke.yaml passes — it’s the fastest signal that the app shell is intact. chat-send, create-generate and scan-history all assume a signed-in session, so run auth-signup.yaml first or use a build with a persisted session.
Why the flows use testIDs
Every assertion targets a testID, never visible text. Text assertions break as soon as a screen is localized, and NativeExpress ships English and German (see Internationalization).
When you add a screen, add testIDs to the elements a flow would need to reach:
<Button testID="onboarding-next" variant="primary">
<Button.Label>{i18n.t('onboarding.continue_button')}</Button.Label>
</Button>The same testID surface drives the App Store and Play Store screenshots, so keep the naming consistent.