Customer chat screen:
- Voice-call header pill (mode == 'call' renders accent-colored pill;
chat mode renders no pill).
- HaloSnackbar fires once per session at 180s remaining ('sisa 3 menit
lagi ya 🤍'), driven by the backend session_warning WS event.
- Last-2-min danger styling: timer pill flips to HaloTokens.danger +
bold JetBrainsMono when remaining <= 120s.
- Floating ChatExpiredBanner widget injected above the input bar when
remaining hits 0 in closing-grace state. perpanjang -> existing
pricing bottom sheet.
- pricing_bottom_sheet.dart rewritten to the 5-option layout with
chat|call mode toggle (mirrors duration-pick from Stage 3).
Mitra chat screen: voice-call header pill only (no countdown UX per PRD).
Backend:
- session.service.js getSessionById JOINs payment_sessions so mode +
expires_at ship in /api/shared/chat/:id/info.
- session-timer.service.js onThreeMinuteWarning now emits expires_at +
remaining_seconds for client resync.
- Dev-only POST /internal/_test/force-session-expires-at clears the
3-min flag, reschedules the timer, and broadcasts WS resync. Lets
the Maestro flow drive 175s -> 90s -> 0s without waiting live.
New chatRemainingSeconds StreamProvider derived from expiresAt, fed by
session_warning / session_timer / session_expired resync messages
(plan referenced a secondsLeftProvider that didn't actually exist).
Maestro 06_chat_countdown.yaml + force_session_expires_at.js helper.
Out of scope: meet.google.com URL launching - url_launcher isn't a
client_app dependency and message bubbles render plain Text. Defer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
75 lines
2.4 KiB
YAML
75 lines
2.4 KiB
YAML
# Stage 6 acceptance: drive a live chat session through the countdown UX
|
|
# in one run.
|
|
#
|
|
# Flow:
|
|
# 1. Customer is on the chat screen of an ACTIVE session (run flow 03 first).
|
|
# 2. Force expires_at = now + 175s → backend fires `session_warning` at 175s
|
|
# (180s threshold, fudge 5s for clock drift) within ~1s.
|
|
# 3. Verify the 3-min snackbar copy renders.
|
|
# 4. Force expires_at = now + 90s → timer pill flips to danger styling at
|
|
# remaining <= 120s (well within the 90s window).
|
|
# 5. Force expires_at = now + 0s → expired banner appears above input bar.
|
|
#
|
|
# Pre-req:
|
|
# 1. A live chat session is on screen (paired + active). The simplest way is
|
|
# to chain this after flow 03_payment_to_chat_happy.yaml.
|
|
# 2. Backend reachable at BACKEND_INTERNAL_URL with NODE_ENV != 'production'.
|
|
#
|
|
# Run (chained):
|
|
# maestro test client_app/.maestro/flows/03_payment_to_chat_happy.yaml \
|
|
# client_app/.maestro/flows/06_chat_countdown.yaml
|
|
appId: ${APP_ID_ANDROID}
|
|
env:
|
|
BACKEND_INTERNAL_URL: http://localhost:3001
|
|
---
|
|
- launchApp:
|
|
clearState: false
|
|
|
|
# Step 0: assert we're already on the chat screen (input hint is the landmark).
|
|
- extendedWaitUntil:
|
|
visible:
|
|
text: "Ketik Pesan"
|
|
timeout: 10000
|
|
|
|
# Step 1: force expires_at to 175s — fires the 3-min warning within ~1s.
|
|
- runScript:
|
|
file: ../scripts/force_session_expires_at.js
|
|
env:
|
|
BACKEND_INTERNAL_URL: ${BACKEND_INTERNAL_URL}
|
|
SECONDS_FROM_NOW: "175"
|
|
|
|
# Step 2: verify the 3-min snackbar.
|
|
- extendedWaitUntil:
|
|
visible:
|
|
text: "sisa 3 menit lagi"
|
|
timeout: 5000
|
|
|
|
# Step 3: force expires_at to 90s — last-2-min danger pill territory.
|
|
- runScript:
|
|
file: ../scripts/force_session_expires_at.js
|
|
env:
|
|
BACKEND_INTERNAL_URL: ${BACKEND_INTERNAL_URL}
|
|
SECONDS_FROM_NOW: "90"
|
|
|
|
# Step 4: assert the danger-styled timer pill renders. The pill content is a
|
|
# minutes-and-seconds string ("1m Xd"); we only assert the unit suffix here
|
|
# because the exact seconds drift between assertion and render.
|
|
- extendedWaitUntil:
|
|
visible:
|
|
text: "1m"
|
|
timeout: 5000
|
|
|
|
# Step 5: force expires_at to 0s — expired banner appears.
|
|
- runScript:
|
|
file: ../scripts/force_session_expires_at.js
|
|
env:
|
|
BACKEND_INTERNAL_URL: ${BACKEND_INTERNAL_URL}
|
|
SECONDS_FROM_NOW: "0"
|
|
|
|
# Step 6: verify the floating expired banner + perpanjang CTA.
|
|
- extendedWaitUntil:
|
|
visible:
|
|
text: "waktu curhat habis"
|
|
timeout: 8000
|
|
- assertVisible: "perpanjang"
|