# TS-05 — Payment expired → retry preserves targeting # (requirement/phase4-customer-flow.md → Test Scenarios → TS-05). # # §4 branch covered: PickMethod → PickDuration → PayMethod → WaitPay → # PayStat(timeout 20 min) → PayExpired → Pay(retry) → paid → # PairRoute(lama) → Targeted → S10. # # What this proves: the `targetedMitraId` on the payment draft survives # the expired-retry round trip (Stage 5.1 `resetExceptTarget` invariant). # After the retry pays, the customer lands on /chat/waiting-targeted/ — NOT a fresh blast. # # Pre-reqs (HARD): # - Backend reachable; NODE_ENV != 'production'. # - >= 1 mitra online — they're the targeted mitra throughout. The flow # does NOT drive accept (we stop at the targeted-waiting screen — the # assertion that targeting survived the retry is the goal). # # Run: # maestro test client_app/.maestro/flows/ts-05_payment_expired_retry_preserves_targeting.yaml appId: com.mybestie env: TEST_PHONE: "+6281234567890" BACKEND_INTERNAL_URL: http://localhost:3001 --- # --- Cold-start reset + onboarding prelude --- - runScript: file: ../scripts/reset_phone.js env: TEST_PHONE: ${TEST_PHONE} BACKEND_INTERNAL_URL: ${BACKEND_INTERNAL_URL} - launchApp: clearState: true - runFlow: ../subflows/onboarding_returning_user.yaml # --- Seed history with online M1 (targeted throughout). --- - runScript: file: ../scripts/seed_history_session.js env: TEST_PHONE: ${TEST_PHONE} BACKEND_INTERNAL_URL: ${BACKEND_INTERNAL_URL} - swipe: start: "50%, 30%" end: "50%, 80%" - extendedWaitUntil: visible: text: "(?s).*curhatan sebelumnya.*" timeout: 10000 # --- Walk TS-01 steps 1-6 to reach /payment/waiting for the targeted # attempt against M1. --- - tapOn: text: "(?s).*curhat sama bestie baru.*" retryTapIfNoChange: true - extendedWaitUntil: visible: text: "(?s).*mau curhat sama siapa.*" timeout: 5000 - tapOn: "(?s).*bestie yang udah kenal.*" - extendedWaitUntil: visible: text: "(?s).*bestie kamu sebelumnya.*" timeout: 5000 - tapOn: "(?s).*bestie ${output.MITRA_NAME_RE}.*" # /payment/entry → /payment/method-pick (returning, no discount). - extendedWaitUntil: visible: text: "(?s).*pilih cara curhat.*" timeout: 10000 - tapOn: text: "(?s).*tulis dan baca dengan tenang.*" - extendedWaitUntil: visible: text: "(?s).*pilih durasi.*" timeout: 10000 - tapOn: "(?s).*5 menit.*" - tapOn: "(?s).*bayar Rp.*" - extendedWaitUntil: visible: text: "(?s).*cara bayar.*" timeout: 10000 - tapOn: text: "(?s).*bayar Rp.*" retryTapIfNoChange: true - extendedWaitUntil: visible: text: "scan QRIS untuk bayar" timeout: 10000 # --- Force-expire the latest pending payment --- - runScript: file: ../scripts/force_expire_latest_payment.js env: BACKEND_INTERNAL_URL: ${BACKEND_INTERNAL_URL} # --- Poller picks `expired` within ~3s → /payment/expired/:paymentId --- - extendedWaitUntil: visible: text: "(?s).*pembayaran kedaluwarsa.*" timeout: 10000 - assertVisible: "(?s).*coba lagi.*" # --- Tap retry → /payment/method (NOT /payment/method-pick — the draft # was preserved via resetExceptTarget, so we skip mode + duration). --- - tapOn: "(?s).*coba lagi.*" - extendedWaitUntil: visible: text: "(?s).*cara bayar.*" timeout: 10000 # Sanity check: we did NOT bounce back to the mode picker. - assertNotVisible: "(?s).*pilih cara curhat.*" # --- Re-pay --- - tapOn: text: "(?s).*bayar Rp.*" retryTapIfNoChange: true - extendedWaitUntil: visible: text: "scan QRIS untuk bayar" timeout: 10000 - runScript: file: ../scripts/mark_latest_payment_paid.js env: BACKEND_INTERNAL_URL: ${BACKEND_INTERNAL_URL} # --- Notif-gate → targeted-waiting screen for the SAME mitra (M1). # This is the load-bearing assertion: if targetedMitraId had been wiped # by the expired-retry round trip, the customer would land on # /chat/searching (blast) and we'd see "lagi nyari bestie" instead. --- - runFlow: when: visible: text: "(?s).*biar nggak ketinggalan.*" commands: - tapOn: text: "(?s).*nanti aja.*" - extendedWaitUntil: visible: text: "(?s).*MENUNGGU JAWABAN.*" timeout: 20000 - assertVisible: "(?s).*lagi nungguin ${output.MITRA_NAME_RE}.*"