Phase 4 Stage 10 Maestro: 09_chat_tab.yaml + seed-pending-payment endpoint
Closes the Stage 10 acceptance criterion §10.11 #13 (Maestro coverage). - New dev-only `POST /internal/_test/seed-pending-payment` — inserts a payment_sessions row in `pending` status with expires_at 20m out, so the Pembayaran sub-tab has a deterministic row to render. Body accepts { phone, isExtension?, amount?, durationMinutes?, mode? }. Gated on NODE_ENV != 'production' like the other test routes. - New Maestro helper script `seed_pending_payment.js` mirrors the existing seed_history_session pattern. - New flow `09_chat_tab.yaml`: cold-start onboarding → home (returning view) → seed completed session + seed pending payment → tap "💬 chat" bottom-nav → lands on /chat/aktif via redirect → assert "aktif" / "pembayaran" / "selesai" pills + empty-state copy → tap pembayaran → assert "menunggu pembayaran sesi" + "bayar Rp..." → tap selesai → assert "X menit" duration row → tap row → assert "Transkrip Chat" appbar → back → still on /chat/selesai. Maestro parsed the YAML cleanly and started executing against the device; full run requires backend + online mitra in dev DB (same pre-reqs as flows 03/05/06/08). - TECH_DEBT entry: Stage 10 retired the standalone bestie-history list screen, which means (a) the "curhat lagi" targeted-payment entry point has no UI affordance anywhere in the app — its plumbing in payment_notifier / payment_screen is now orphaned, and (b) the Stage 8 flow `08_returning_targeted.yaml` will fail at `assertVisible: "Riwayat Chat"` because it expects the deleted screen. Three fix paths listed in the entry for product to pick. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
161
client_app/.maestro/flows/09_chat_tab.yaml
Normal file
161
client_app/.maestro/flows/09_chat_tab.yaml
Normal file
@@ -0,0 +1,161 @@
|
||||
# Stage 10 acceptance: Chat tab (3 sub-tabs).
|
||||
#
|
||||
# Flow:
|
||||
# 1. Cold-start onboarding (abbreviated; mirrors 01_smoke) → home with a
|
||||
# phone-verified customer so the dev seed endpoints can find them.
|
||||
# 2. Seed a completed chat_sessions row → Selesai sub-tab has data.
|
||||
# 3. Seed a pending payment_sessions row → Pembayaran sub-tab has data
|
||||
# AND the bottom-nav chat tab should render a red dot (visual; not
|
||||
# asserted because Maestro can't reliably introspect small pixel state).
|
||||
# 4. Tap the "💬 chat" bottom-nav icon → /chat redirects to /chat/aktif.
|
||||
# 5. Aktif sub-tab: no active session, so empty state copy shows.
|
||||
# 6. Tap "pembayaran" pill → row with preview "menunggu pembayaran sesi"
|
||||
# and the "bayar Rp..." chip.
|
||||
# 7. Tap "selesai" pill → row with seeded mitra name. Tap the row →
|
||||
# transcript screen opens.
|
||||
# 8. Back → still on /chat/selesai (URL is source of truth for the sub-tab).
|
||||
#
|
||||
# Pre-req: client_app debug APK installed, backend reachable on
|
||||
# BACKEND_INTERNAL_URL with NODE_ENV != 'production' so /internal/_test/*
|
||||
# routes are registered, AND at least one mitra is online in the dev DB.
|
||||
#
|
||||
# Run:
|
||||
# maestro test client_app/.maestro/flows/09_chat_tab.yaml
|
||||
appId: com.halobestie.client.client_app
|
||||
env:
|
||||
TEST_PHONE: "+628155556678"
|
||||
BACKEND_INTERNAL_URL: http://localhost:3001
|
||||
---
|
||||
# Wipe prior state for TEST_PHONE so the run is hermetic.
|
||||
- runScript:
|
||||
file: ../scripts/reset_phone.js
|
||||
env:
|
||||
TEST_PHONE: ${TEST_PHONE}
|
||||
BACKEND_INTERNAL_URL: ${BACKEND_INTERNAL_URL}
|
||||
- launchApp:
|
||||
clearState: true
|
||||
|
||||
# Onboarding carousel → splash → home (1st time view).
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
text: "Mulai"
|
||||
timeout: 15000
|
||||
- tapOn:
|
||||
text: "Mulai"
|
||||
retryTapIfNoChange: true
|
||||
|
||||
# Verif choice sheet → "Lanjut sebagai Tamu" → name → phone OTP
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
text: "Lanjut sebagai Tamu"
|
||||
timeout: 10000
|
||||
- tapOn:
|
||||
text: "Lanjut sebagai Tamu"
|
||||
retryTapIfNoChange: true
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
text: "Nama panggilan"
|
||||
timeout: 10000
|
||||
- tapOn:
|
||||
text: "Nama panggilan"
|
||||
- inputText: "Maestro"
|
||||
- hideKeyboard
|
||||
- tapOn:
|
||||
text: "Lanjut"
|
||||
retryTapIfNoChange: true
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
text: "Verifikasi Akun"
|
||||
timeout: 15000
|
||||
- tapOn:
|
||||
text: "Nomor HP"
|
||||
- inputText: ${TEST_PHONE}
|
||||
- hideKeyboard
|
||||
- tapOn:
|
||||
text: "Kirim OTP"
|
||||
retryTapIfNoChange: true
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
text: "Masukkan OTP"
|
||||
timeout: 15000
|
||||
- runScript:
|
||||
file: ../scripts/peek_otp.js
|
||||
env:
|
||||
TEST_PHONE: ${TEST_PHONE}
|
||||
BACKEND_INTERNAL_URL: ${BACKEND_INTERNAL_URL}
|
||||
- inputText: ${output.OTP}
|
||||
- extendedWaitUntil:
|
||||
notVisible:
|
||||
text: "Masukkan OTP"
|
||||
timeout: 15000
|
||||
|
||||
# Returning users land on SHomeReturning with "Mulai Curhat".
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
text: "Mulai Curhat"
|
||||
timeout: 20000
|
||||
|
||||
# Seed a completed session → Selesai sub-tab populated.
|
||||
- runScript:
|
||||
file: ../scripts/seed_history_session.js
|
||||
env:
|
||||
TEST_PHONE: ${TEST_PHONE}
|
||||
BACKEND_INTERNAL_URL: ${BACKEND_INTERNAL_URL}
|
||||
|
||||
# Seed a pending payment → Pembayaran sub-tab populated + red dot eligibility.
|
||||
- runScript:
|
||||
file: ../scripts/seed_pending_payment.js
|
||||
env:
|
||||
TEST_PHONE: ${TEST_PHONE}
|
||||
BACKEND_INTERNAL_URL: ${BACKEND_INTERNAL_URL}
|
||||
|
||||
# Tap "💬 chat" in the bottom nav → /chat → redirected to /chat/aktif.
|
||||
- tapOn:
|
||||
text: "chat"
|
||||
retryTapIfNoChange: true
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
text: "aktif"
|
||||
timeout: 10000
|
||||
# Sub-tab pills visible; the heading "chat" duplicates with the nav label
|
||||
# so assert what's unique to the chat-tab shell.
|
||||
- assertVisible: "aktif"
|
||||
- assertVisible: "pembayaran"
|
||||
- assertVisible: "selesai"
|
||||
|
||||
# Aktif default body: empty state since no active session was seeded.
|
||||
- assertVisible: "belum ada chat di sini"
|
||||
|
||||
# Pembayaran sub-tab → seeded pending initial payment is visible.
|
||||
- tapOn:
|
||||
text: "pembayaran"
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
text: "menunggu pembayaran sesi"
|
||||
timeout: 5000
|
||||
- assertVisible:
|
||||
text: "bayar Rp.*"
|
||||
|
||||
# Selesai sub-tab → seeded completed session is visible.
|
||||
- tapOn:
|
||||
text: "selesai"
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
text: ".* menit"
|
||||
timeout: 5000
|
||||
|
||||
# Tap the row → opens the read-only transcript screen.
|
||||
- tapOn:
|
||||
text: ".* menit"
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
text: "Transkrip Chat"
|
||||
timeout: 10000
|
||||
|
||||
# Back returns us to /chat/selesai (URL preserves sub-tab state).
|
||||
- back
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
text: "selesai"
|
||||
timeout: 5000
|
||||
- assertVisible: "selesai"
|
||||
21
client_app/.maestro/scripts/seed_pending_payment.js
Normal file
21
client_app/.maestro/scripts/seed_pending_payment.js
Normal file
@@ -0,0 +1,21 @@
|
||||
// Seed a `pending` payment_sessions row for TEST_PHONE so the Pembayaran
|
||||
// sub-tab has a row to render without walking the multi-screen S6 paywall →
|
||||
// method-pick → duration-pick → method flow.
|
||||
//
|
||||
// Hits the dev-only /internal/_test/seed-pending-payment endpoint.
|
||||
const phone = TEST_PHONE
|
||||
const url = BACKEND_INTERNAL_URL || 'http://localhost:3001'
|
||||
const body = {
|
||||
phone,
|
||||
isExtension: PENDING_KIND === 'extension',
|
||||
amount: PENDING_AMOUNT ? Number(PENDING_AMOUNT) : 5000,
|
||||
}
|
||||
const resp = http.post(`${url}/internal/_test/seed-pending-payment`, {
|
||||
body: JSON.stringify(body),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
})
|
||||
if (resp.status !== 200) {
|
||||
throw new Error(`seed-pending-payment failed (${resp.status}): ${resp.body}`)
|
||||
}
|
||||
const data = json(resp.body)
|
||||
output.PAYMENT_ID = data.payment_id
|
||||
Reference in New Issue
Block a user