freshyo/e2e/README.md
2026-09-14 23:34:10 +05:30

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 via other ways to loginUsername + Password and returns to the cart. Alternatively, sign in once by hand and the subflow becomes a no-op; launchApp uses clearState: false so 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:

  1. config.yamlenv: — committed, non-secret defaults shared by all flows (ADDRESS_NAME, ADDRESS_PHONE, ADDRESS_LINE1 live here).

  2. e2e/.env (gitignored) — loaded by run.sh; put TEST_IDENTIFIER / TEST_PASSWORD here so secrets stay out of git and off the command line.

  3. Shell / CLIMAESTRO_-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

  1. 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.)
  2. Cart → checkout. A guest is bounced to login (checkout is behind useAuthenticatedRoute), signs in, and the flow re-enters the cart.
  3. Address (only when the account has none) → Cash on Delivery → Place Order.
  4. Home glimpse: Continue Shopping returns home; the flow scrolls to the NextOrderGlimpse card and captures it with takeScreenshot, 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.
  5. 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 found is 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.
  • launchApp uses clearState: 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 testID anchors 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.