5.8 KiB
Maestro E2E — apps/user-ui (Android only)
Maestro flows that drive the user-ui app through placing an order: home → product → add to cart (choose delivery slot) → cart → checkout (address if needed, Cash on Delivery) → Place Order → confirmation.
Everything here targets Android only (appId: in.freshyo.app), and run.sh
always passes --platform android — it will never use an iOS simulator.
Prerequisites
-
Maestro CLI installed (
maestro --version; installed here at~/.maestro/bin/maestro). -
A running Android emulator/device with the user-ui dev build installed:
cd apps/user-ui npx expo run:android -
A test account with a password set. OTP login can't be automated (the OTP is server-issued, no dev bypass). The flow reaches the login screen by tapping Checkout as a guest — checkout is behind
useAuthenticatedRoute, so the app bounces there — then signs in viaother ways to login→Username+Passwordand returns to the cart. Alternatively, sign in once by hand and the subflow becomes a no-op;launchAppusesclearState: falseso the session and the first-run onboarding survive. -
A catalog with at least one in-stock product that has a future delivery slot.
Note: in a dev build the "Open debugger to view warnings" toast overlays the bottom
of the screen and swallows taps on the bottom tab bar — the flows avoid the tab bar
and use centerElement: true where a control can land under it.
Running
Recommended — one command, values from a gitignored file. Create e2e/.env
(see .env.example) and then, from the repo root:
bun run e2e # or: bash e2e/run.sh
e2e/run.sh sources e2e/.env, runs maestro test e2e --platform android, and
falls back to ~/.maestro/bin/maestro when maestro isn't on PATH. Extra flags
pass through, and flow paths are relative to e2e/:
bash e2e/run.sh flows/place-order.yaml # one flow
bash e2e/run.sh --format junit # junit report
bash e2e/run.sh --device emulator-5554 # pick an emulator (needed if several are connected)
To see which emulators are available: adb devices -l.
How values are supplied
Maestro resolves ${VAR} from three places:
-
config.yaml→env:— committed, non-secret defaults shared by all flows (ADDRESS_NAME,ADDRESS_PHONE,ADDRESS_LINE1live here). -
e2e/.env(gitignored) — loaded byrun.sh; putTEST_IDENTIFIER/TEST_PASSWORDhere so secrets stay out of git and off the command line. -
Shell / CLI —
MAESTRO_-prefixed env vars or-e KEY=VALUE, good for CI:MAESTRO_TEST_IDENTIFIER=... MAESTRO_TEST_PASSWORD=... bash e2e/run.sh # or maestro test e2e -e TEST_IDENTIFIER=... -e TEST_PASSWORD=...
Need more than one setup (e.g. CI vs local)? Maestro supports alternate configs:
maestro test --config e2e/ci-config.yaml e2e.
Useful flags: --include-tags, --format junit, --test-output-dir.
Reports/screenshots land in e2e/.maestro-output/.
Layout
e2e/
config.yaml # workspace config (flows, execution order, env defaults)
subflows/login.yaml # reusable login (runs only if the login screen is visible)
flows/smoke.yaml # app launches, home renders
flows/place-order.yaml # the order flow
What place-order covers
- Home → add a product from the card's cart icon → pick a delivery slot in the "Select Delivery Slot" dialog → add to cart. (The icon sits inside the card's touchable, so the tap can fall through to the card; the flow then adds from the product detail page instead.)
- Cart → checkout. A guest is bounced to login (checkout is behind
useAuthenticatedRoute), signs in, and the flow re-enters the cart. - Address (only when the account has none) → Cash on Delivery → Place Order.
- Home glimpse:
Continue Shoppingreturns home; the flow scrolls to theNextOrderGlimpsecard and captures it withtakeScreenshot, so each run leaves a picture of the upcoming-order card. Maestro writes it under~/.maestro/tests/<timestamp>/place-order/takeScreenshot/home-upcoming-order-glimpse.png. - Cancel: taps the glimpse card (which routes to
/(drawer)/(tabs)/me/my-orders/{id}), scrolls to Cancel Order, fills the reason, confirms, dismisses the success alert, and asserts the Cancellation Reason panel renders.
Notes
- These flows place real orders on whatever backend the app points at, and then cancel the one they placed. Point the app at a non-production backend / use a throwaway account.
- The product is chosen as the first card (
id: product-card,index: 0). If that item is out of stock or has no future slot, change the index or seed a known product. - The address step is conditional: it only runs when
No addresses foundis visible, so an account with a saved address skips it. - Payment defaults to Cash on Delivery, so the flow just asserts it's visible.
- Controls below the fold need
scrollUntilVisible; the home glimpse sits below Our Stores, and Cancel Order below the bill summary. launchAppusesclearState: false, so the session and (on the first run) the onboarding survive. The trade-off is that the app resumes wherever the previous run left it — the flow taps the Home tab (optional: true) to get back.- Maestro taps can be eaten by a dismissing keyboard or the dev-warning toast, so the flow waits for animations and retries the confirm tap once.
- Stable
testIDanchors used by the flows live in user-ui:product-card,add-to-cart-icon,slot-option,add-to-cart-confirm,go-to-cart,checkout-button,address-name,address-phone,address-line1,address-submit,place-order-button,cancel-reason.