Files
halobestie-clone/mitra_app/.maestro/flows/README_section_A.md
Ramadhan Sjamsani fbc94daac7 Mitra Bestie §1–§3: shell + Undangan + popup + chat polish
Brings the mitra app to figma-bestie parity for Home (§1), Undangan
inbox with Curhat Baru + Perpanjang tabs (§2), and the incoming-popup
+ active-chat flow (§3). Home now lives inside a StatefulShellRoute
with BestieTabBar so Profil + Undangan + Home share one shell.

- Shell: features/shell/ (StatefulShellRoute, BestieTabBar, 3 branches)
- Undangan: features/undangan/ — Curhat Baru reads
  chatRequestProvider.pendingInvites; row Terima delegates accept to
  the notifier and ChatRequestOverlay owns nav (no double-push).
  Perpanjang tab stubbed (empty state) until backend exposes
  pendingExtensionsProvider.
- Profil: features/profile/ — Bestie-styled stub
- Home: refactored to body-only (shell owns chrome)
- Popup: chat_request_overlay + chat_request_notifier updated to
  serve the list rows, not just the modal
- Chat: mitra_chat_screen polish
- Theme: accentAmber tokens for the Perpanjang tab + halo_orb widget
  (loading spinner used by undangan list states)
- Login: replace broken GoRouterState location guard with
  _expectOtpPush flag — was stacking duplicate /otp pages on OTP
  resend (see project-otp-nav-bug-fixed-2026-05-21)

Maestro:
- 17 new flows under .maestro/flows/ts-mitra-{1,2,3}-* covering home
  online/offline variants, undangan empty/populated/tolak states,
  popup curhat-baru → accept → chat → ended banner, plus popup
  dismiss/expire/cancelled edge cases
- 4 new §A OTP flows (07/08/09/10) for invalid/mismatch/expired/cooldown
- Helper scripts: force_mitra_online/offline, force_pairing_timeout,
  force_session_expires_at, delete_mitra_status_row,
  customer_blast_now (js), customer_cancel_latest_blast
- Backend: POST /internal/_test/delete-mitra-status-row supports the
  "fresh mitra with no status row" test setup

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 11:14:30 +08:00

121 lines
5.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# §A — Mitra Pre-Home (auth) test plan
Spec: [requirement/flow_mitra.mermaid.md §A](../../../requirement/flow_mitra.mermaid.md).
Tests use the naming convention `ts-mitra-<section>-<sub>-<description>.yaml`:
- `<section>` — flow_mitra.mermaid section identifier (`A` for pre-home auth).
- `<sub>` — sub-flow index within the section, zero-padded.
- `<description>` — snake_case summary of the branch under test.
## Implemented
| File | Branch (spec ref) | Expected destination |
|---|---|---|
| `ts-mitra-A-01-phone_invalid_inline_error.yaml` | §A.1 local CTA gate for short phones | "kirim kode" disabled for <9 subscriber digits, enables + navigates on valid input |
| `ts-mitra-A-03-success_login_to_home.yaml` | §A.2 200 + `is_active=true` | `/home` (BestieHome online; asserts "Kamu lagi ONLINE" — Stage 2 removed the Sesi Aktif / Riwayat Chat tiles) |
| `ts-mitra-A-04-account_inactive_screen.yaml` | §A.2 200 + `is_active=false` → 403 `ACCOUNT_INACTIVE` | `/auth/inactive` (AppBar back arrow omitted; system-back interception deferred) |
| `ts-mitra-A-05-phone_format_variants.yaml` | §A.1 phone-format normalization | 5 variants (`8…`, `08…`, `628…`, `+628…`, `0628…`) all reach S3b with `+628200000501` shown |
| `ts-mitra-A-06-back_to_login_and_retry.yaml` | §A.1 regression — back from S3b + re-submit | Second "kirim kode" tap still navigates to S3b after returning to S3a |
## Test infrastructure
JS scripts (live in [`../scripts/`](../scripts/)) called via `runScript:`:
| Script | Purpose | Backend endpoint |
|---|---|---|
| `reset_phone.js` | Clear `otp_requests` rows for `TEST_PHONE` so cooldown / phone-rate-limit don't trip on re-runs. | `POST /internal/_test/reset-phone` |
| `seed_mitra.js` | Upsert a mitra row with given phone + `is_active`. Idempotent. | `POST /internal/_test/seed-mitra` |
| `peek_otp.js` | Read the latest stub-generated OTP code for `TEST_PHONE`. Writes to `output.OTP`. | `GET /internal/_test/peek-otp` |
All three only exist on the **internal** listener (port 3001), so they're
network-isolated from production traffic.
### Phone-number convention
Each test uses a unique `+628200000<NN><SS>` phone to avoid cross-flow
interference:
- A-02 (deferred) → `+628200000201`
- A-03 → `+628200000301`
- A-04 → `+628200000401`
- A-05 → `+628200000501` (one phone, 5 input formats)
- A-06 → `+628200000601`
- §1 Home (ts-mitra-1-*) → `+62820000070{1..3}`
- §2 Undangan (ts-mitra-2-*) → `+62820000080{1..2}` (2-03 piggybacks on a
pre-signed-in device, no fresh OTP)
- §3 Popup + Chat (ts-mitra-3-*) → `+62820000090{1..4}`
If the same phone gets used across multiple flows in one run, the per-IP
rate-limit (10 OTP requests / hour, default) can trip and break A-03 or A-04
mid-suite. A-05 mitigates this by calling `reset_phone` between its 5
variants (each variant is one OTP request).
## Deferred (not yet implemented — see reasons)
### `ts-mitra-A-02-wrong_code_attempts_then_blocked.yaml`
**Branch:** §A.2 5× CODE_MISMATCH → OTP_ATTEMPTS_EXCEEDED → blocked dialog.
**Why deferred:** the 6-separate-TextField OTP pattern with `maxLength: 1`
per box doesn't play well with maestro's `inputText` on Android. Maestro
uses uiautomator2's `setText` under the hood, which delivers chars
non-deterministically across the per-box focus chain — even matching the
customer-app's exact `inputText: "000000"` × 6 pattern. After the 5th
auto-submit succeeds and the boxes clear, focus state races with the
next `inputText` call and digits silently drop. Verified manually: the
"Tersisa N percobaan" hint and blocked dialog both render correctly with
real keyboard typing.
**Possible future approach:** refactor S3b to use a single hidden
`TextField` with custom box decorations (the `pin_code_fields` pattern).
One `inputText: "000000"` per attempt would land all 6 chars in one IME
commit, matching how real users paste OTPs from SMS. Worth doing for
SMS-paste UX anyway.
### `ts-mitra-A-05-otp_cooldown_snackbar.yaml`
**Branch:** §A.1 second OTP request within 60s → `OTP_COOLDOWN` 429 + snackbar.
**Why deferred:** Maestro's `extendedWaitUntil` can't reliably assert a
floating snackbar that auto-dismisses in ~4s — the visible window is too
short for the polling cadence. Possible workaround: drive two consecutive
requests and rely on the CTA label switching to "coba lagi dalam Ns" (which
is non-floating and stable). Worth adding once the resend-cooldown UX
stabilizes.
### `ts-mitra-A-06-rate_limit_phone_popup.yaml`
**Branch:** §A.1 4th OTP request in 1h for same phone → `OTP_RATE_LIMIT_PHONE`
429 popup with `retry_after_seconds`.
**Why deferred:** The popup is asserted in manual testing (screenshot in
phase4-mitra-prehome-plan.md). Driving 4 sequential requests within one
maestro run is brittle if any earlier test bumped the counter. A backend
`_test/reset-phone-rate-limit` helper would make this reliable; not added yet
to keep the test-surface minimal.
### `ts-mitra-A-07-resend_after_cooldown.yaml`
**Branch:** §A.4 resend after 60s cooldown → fresh OTP, attempts counter resets.
**Why deferred:** 60s wall-clock wait per pass is too slow for CI. Drive
this manually until we have a `_test/expire-cooldown` helper that fast-forwards
the cooldown clock.
### `ts-mitra-A-08-otp_expired.yaml`
**Branch:** §A.2 5-minute TTL elapses → `OTP_EXPIRED` 410 → dialog → S3a.
**Why deferred:** Same wall-clock problem. Need a `_test/force-expire-otp`
helper before this is automatable.
## Running
From `mitra_app/`:
```bash
# Single flow
maestro test .maestro/flows/ts-mitra-A-01-phone_invalid_inline_error.yaml
# Whole §A suite
maestro test .maestro/flows/ts-mitra-A-*.yaml
```
The backend must be running with `OTP_STATIC_CODE` **unset** — peek_otp.js
relies on the stub generator returning a fresh code per request, not a
static one.