RebreakVpnService.onStartCommand crashed with SecurityException because Android 16's validateForegroundServiceType rejects the implicit 2-arg startForeground(). Now passes FOREGROUND_SERVICE_TYPE_SPECIAL_USE explicitly (Google's documented best practice) and guards the call so a failed foreground promotion stops the service cleanly instead of crashing the app. Verified vs reported Galaxy A54 / Android 16 signature (97% of crash events, 1-user crash loop). Bundles pending working-tree work across native/marketing/locales/mac + graphify-out rebuild. gitignore: google-services.json + /screenshots/. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
120 lines
3.8 KiB
YAML
120 lines
3.8 KiB
YAML
# sos/crisis-flow.yaml
|
||
#
|
||
# HIGHEST PRIORITY FLOW — this must never silently fail.
|
||
#
|
||
# Journey: Login → Header-Dropdown → SOS → Lyra chat loads → send a message →
|
||
# assert response area or chip row becomes visible (Lyra is alive).
|
||
#
|
||
# This flow is the canary for:
|
||
# - SOS navigation path not broken
|
||
# - Lyra streaming endpoint reachable from staging
|
||
# - Chat input + send mechanism functional
|
||
# - Breathing chip "Atemübung" accessible as immediate coping entry
|
||
#
|
||
# Unlike the previous urge/sos-flow.yaml which relies on a coordinate tap for
|
||
# the avatar button, this flow uses the same coordinate fallback until
|
||
# testID="header-avatar-btn" is added (see TODO_TESTIDS.md HIGH priority).
|
||
# The coordinate tap is the ONLY fragile part of this flow.
|
||
#
|
||
# Timing: SOS screen on staging needs 6–12s for Groq cold start. The send step
|
||
# waits 20s for Lyra's response — intentionally generous. A CI failure here
|
||
# means either backend is down or Groq key is missing/expired.
|
||
#
|
||
# Pre-requisite:
|
||
# - App installed. Test-user exists on staging.
|
||
# - Staging backend + Groq API key configured.
|
||
# Env-Vars: E2E_TEST_USER, E2E_TEST_PASSWORD
|
||
|
||
appId: org.rebreak.app
|
||
---
|
||
- launchApp:
|
||
clearState: true
|
||
|
||
- waitForAnimationToEnd:
|
||
timeout: 5000
|
||
|
||
# --- Auth ---
|
||
- assertVisible:
|
||
text: "E-Mail"
|
||
- tapOn:
|
||
text: "E-Mail"
|
||
- inputText: ${E2E_TEST_USER}@rebreak.internal
|
||
- tapOn:
|
||
text: "Passwort"
|
||
- inputText: ${E2E_TEST_PASSWORD}
|
||
- tapOn:
|
||
text: "Anmelden"
|
||
- waitForAnimationToEnd:
|
||
timeout: 10000
|
||
|
||
- assertVisible:
|
||
text: "ReBreak"
|
||
|
||
# --- Open Header Dropdown ---
|
||
# FRAGILE: Avatar Pressable has no testID → coordinate tap.
|
||
# Replace with: tapOn: { id: "header-avatar-btn" } once testID is added.
|
||
- tapOn:
|
||
point: "93%, 6%"
|
||
- waitForAnimationToEnd:
|
||
timeout: 2000
|
||
|
||
# appHeader.sosLabel = "SOS" (de.json line 119)
|
||
- assertVisible:
|
||
text: "SOS"
|
||
- tapOn:
|
||
text: "SOS"
|
||
|
||
# SOS screen loads: RiveAvatar + Lyra streaming begins.
|
||
# Cold start on Groq (staging) = 6–12s. We wait the full 12s before asserting.
|
||
- waitForAnimationToEnd:
|
||
timeout: 12000
|
||
|
||
# coach.placeholder = "Was beschäftigt dich?" — only exists on SOS/Urge screen.
|
||
# This confirms the SOS screen has fully loaded with the chat input visible.
|
||
- assertVisible:
|
||
text: "Was beschäftigt dich?"
|
||
|
||
# --- Send a message ---
|
||
# Tap the placeholder to focus the TextInput
|
||
- tapOn:
|
||
text: "Was beschäftigt dich?"
|
||
- waitForAnimationToEnd:
|
||
timeout: 1000
|
||
|
||
# Type a safe test message that triggers a Lyra response
|
||
- inputText: "Ich brauche gerade Hilfe."
|
||
|
||
# Send via the send button.
|
||
# FRAGILE: send Pressable has no testID → use pressKey Enter as fallback.
|
||
# The TextInput in urge.tsx submits on Return key (onSubmitEditing = handleSend).
|
||
# This is more reliable than coordinate tapping the icon button.
|
||
# Add testID="sos-send-btn" to fix (see TODO_TESTIDS.md HIGH priority).
|
||
- pressKey: Return
|
||
- waitForAnimationToEnd:
|
||
timeout: 3000
|
||
|
||
# Wait for Lyra to stream back a response (up to 20s on staging cold start)
|
||
- waitForAnimationToEnd:
|
||
timeout: 20000
|
||
|
||
# After Lyra responds, the initial chip set is visible.
|
||
# "Atemübung" is in CHIP_SETS.start (sosConstants.ts) — hardcoded, not i18n.
|
||
# This chip is ALWAYS shown as the first response. If it's not visible after 20s,
|
||
# Lyra failed to respond — the test should fail here.
|
||
- assertVisible:
|
||
text: "Atemübung"
|
||
|
||
# --- Verify Breathing entry point ---
|
||
# Tap "Atemübung" to open BreathingDrawer. This ensures the most critical
|
||
# coping mechanism (breathing exercise) is accessible from the SOS screen.
|
||
- tapOn:
|
||
text: "Atemübung"
|
||
- waitForAnimationToEnd:
|
||
timeout: 3000
|
||
|
||
# BreathingDrawer header. The breathing screen title is hardcoded in Breathing.tsx
|
||
# (not i18n). urge.sos_title = "SOS — Atemübung" (de.json line 979).
|
||
# The drawer itself renders "Atemübung" as its header text.
|
||
- assertVisible:
|
||
text: "Atemübung"
|