Files
halobestie-clone/mitra_app/.maestro/flows/README_section_A.md
Ramadhan Sjamsani 9696eadeaf Mitra §A: pre-home (S3a/S3b/AccountInactive) + design system + Bestie Home
- Port halo_tokens + halo_theme + HaloButton to mitra_app (rose palette,
  Bricolage display, Poppins body, JetBrainsMono).
- Build S3a Input WhatsApp (figma-bestie BestieS3 first half) with
  +62 chip, leading-zero/62 normalization, allow '+' in input.
- Build S3b OTP verification (6-digit, 60s resend timer, attempts hint,
  Focus(canRequestFocus:false) for maestro inputText compat) with full
  error branching (CODE_MISMATCH, OTP_EXPIRED, OTP_USED, ATTEMPTS_EXCEEDED,
  WRONG_FLOW, ACCOUNT_INACTIVE).
- Add AccountInactive terminal screen for is_active=false mitras.
- Typed MitraAuthError with Indonesian-first localized messages +
  retryAfterSeconds passthrough.
- Rebuild home_screen.dart to match figma BestieHome (greeting + status
  card + Ganti Status CTA + Pengingat + 2-tile dark grid).
- Backend: POST /internal/_test/seed-mitra (idempotent) and
  PATCH /internal/mitras/:id (display_name update).
- Control center: inline Edit Nama on mitras row + expandable inline log
  table under clicked mitra (vs old below-table panel).
- 5 maestro flows ts-mitra-A-01/03/04/05/06 covering invalid input, happy
  path, account inactive, phone-format normalization, and the back-to-S3a
  regression. All green.

Plan + memory documented in:
- requirement/phase4-mitra-prehome-plan.md
- requirement/flow_mitra.md / flow_mitra.mermaid.md §A

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 22:01:28 +08:00

117 lines
5.4 KiB
Markdown
Raw 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` (active sessions tab) |
| `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`
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.