chahinebrini 63fae25531 fix(android-protection): explicit specialUse FGS type — Samsung/Android 16 crash loop
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>
2026-06-10 22:33:28 +02:00

341 lines
11 KiB
YAML

# screens/marketing-tour.yaml
#
# Maestro Marketing-Tour — alle 9 Screenshots in einem Lauf
#
# Dieser Flow führt nacheinander alle 9 Marketing-Screens an und macht je einen
# takeScreenshot. Maestro speichert Screenshots unter ~/.maestro/tests/<timestamp>/
# Das capture-marketing.sh Script benennt sie in 01-onboarding.png … 09-geraete.png um.
#
# REIHENFOLGE KRITISCH — entspricht der Reihenfolge auf der Preview-Seite:
# 01 Onboarding → Sign-in-Screen (neuer Nutzer, clearState)
# 02 Blocker → Blocker-Tab mit Schutz-Status
# 03 Blocked → MANUELL (VPN außerhalb App) — Proxy: Header-Dropdown
# 04 SOS/Lyra → SOS-Screen mit Lyra-Antwort + Chips
# 05 Breathing → BreathingDrawer geöffnet
# 06 Mail-Schutz → Mail-Tab
# 07 Community → Home-Feed mit Posts
# 08 Streak → Profil-StreakSection
# 09 Geräte → Devices-Screen
#
# Wichtig: Flow bricht nach dem letzten takeScreenshot ab — keine Assertions danach.
# Jeder Abschnitt setzt den App-State via launchApp + clearState zurück.
#
# HINWEIS 03-blocked:
# Der echte Block-Screen ist außerhalb der App (Safari + VPN). Dieser Flow macht
# einen Proxy-Screenshot des Header-Dropdowns. Für den echten Shot: manuell.
#
# FRAGILE STEPS (brauchen testID-Ergänzung für Robustheit):
# - Avatar-Tap (93%, 6%) in allen Flows → testID="header-avatar-btn"
# - SOS-Send-Button → testID="sos-send-btn"
# - Settings→Geräte Navigation
#
# Env-Vars: E2E_TEST_USER, E2E_TEST_PASSWORD
# Voraussetzung: iOS Simulator mit Status-Bar via xcrun simctl status_bar überschrieben
# (Details in capture-marketing.sh)
appId: org.rebreak.app
---
# ══════════════════════════════════════════════════════════
# 01 — ONBOARDING / WILLKOMMEN
# Sign-in-Screen als "erster Blick" für neue Nutzer
# ══════════════════════════════════════════════════════════
- launchApp:
clearState: true
- waitForAnimationToEnd:
timeout: 6000
- assertVisible:
text: "E-Mail"
- waitForAnimationToEnd:
timeout: 2000
- takeScreenshot: screenshots/01-onboarding
# ══════════════════════════════════════════════════════════
# 02 — BLOCKER AKTIV
# ══════════════════════════════════════════════════════════
- launchApp:
clearState: true
- waitForAnimationToEnd:
timeout: 5000
- 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"
- tapOn:
text: "Blocker"
- waitForAnimationToEnd:
timeout: 4000
- assertVisible:
text: "ReBreak-Schutz"
- scrollUntilVisible:
element:
text: "URL-Filter"
direction: DOWN
timeout: 5000
- waitForAnimationToEnd:
timeout: 1000
- takeScreenshot: screenshots/02-blocker
# ══════════════════════════════════════════════════════════
# 03 — BLOCKED (Proxy: Header-Dropdown als "Schutz greift")
# HINWEIS: Echter Block-Screen → manuell via Safari + VPN
# ══════════════════════════════════════════════════════════
- launchApp:
clearState: true
- waitForAnimationToEnd:
timeout: 5000
- 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"
# Blocker-Tab zeigt den aktivsten "Schutz greift"-Screen ohne VPN
- tapOn:
text: "Blocker"
- waitForAnimationToEnd:
timeout: 4000
- assertVisible:
text: "ReBreak-Schutz"
- takeScreenshot: screenshots/03-blocked
# ══════════════════════════════════════════════════════════
# 04 — SOS / LYRA
# ══════════════════════════════════════════════════════════
- launchApp:
clearState: true
- waitForAnimationToEnd:
timeout: 5000
- 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"
# FRAGILE: Avatar-Tap Koordinaten bis testID="header-avatar-btn" gesetzt
- tapOn:
id: "header-avatar-btn"
- waitForAnimationToEnd:
timeout: 2000
- assertVisible:
text: "SOS"
- tapOn:
text: "SOS"
- waitForAnimationToEnd:
timeout: 12000
- assertVisible:
text: "Was beschäftigt dich?"
- tapOn:
text: "Was beschäftigt dich?"
- waitForAnimationToEnd:
timeout: 500
- inputText: "Ich weiß nicht mehr weiter."
- pressKey: Return
- waitForAnimationToEnd:
timeout: 20000
- assertVisible:
text: "Atemübung"
- waitForAnimationToEnd:
timeout: 1000
- takeScreenshot: screenshots/04-sos-lyra
# ══════════════════════════════════════════════════════════
# 05 — ATEMÜBUNG
# BreathingDrawer bleibt nach 04-sos-lyra noch offen →
# kein launchApp nötig, direkt tippen
# ══════════════════════════════════════════════════════════
- tapOn:
text: "Atemübung"
- waitForAnimationToEnd:
timeout: 3000
- assertVisible:
text: "Atemübung"
- waitForAnimationToEnd:
timeout: 1500
- takeScreenshot: screenshots/05-breathing
# ══════════════════════════════════════════════════════════
# 06 — MAIL-SCHUTZ
# ══════════════════════════════════════════════════════════
- launchApp:
clearState: true
- waitForAnimationToEnd:
timeout: 5000
- 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"
- tapOn:
text: "Mail"
- waitForAnimationToEnd:
timeout: 4000
- assertVisible:
text: "Mail-Schutz"
- waitForAnimationToEnd:
timeout: 1000
- takeScreenshot: screenshots/06-mail-schutz
# ══════════════════════════════════════════════════════════
# 07 — COMMUNITY-FEED
# ══════════════════════════════════════════════════════════
- launchApp:
clearState: true
- waitForAnimationToEnd:
timeout: 5000
- 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"
- scrollUntilVisible:
element:
text: "Was bewegt dich gerade?"
direction: UP
timeout: 4000
- assertVisible:
text: "Was bewegt dich gerade?"
- scroll:
direction: DOWN
duration: 800
- waitForAnimationToEnd:
timeout: 1500
- takeScreenshot: screenshots/07-community
# ══════════════════════════════════════════════════════════
# 08 — STREAK / PROFIL-STATS
# ══════════════════════════════════════════════════════════
- launchApp:
clearState: true
- waitForAnimationToEnd:
timeout: 5000
- 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"
# FRAGILE: Koordinaten-Tap
- tapOn:
id: "header-avatar-btn"
- waitForAnimationToEnd:
timeout: 2000
- assertVisible:
text: "Profil"
- tapOn:
text: "Profil"
- waitForAnimationToEnd:
timeout: 4000
- assertVisible:
text: "Profil"
- scrollUntilVisible:
element:
text: "SCHUTZ-ABDECKUNG"
direction: DOWN
timeout: 6000
- assertVisible:
text: "SCHUTZ-ABDECKUNG"
- waitForAnimationToEnd:
timeout: 1000
- takeScreenshot: screenshots/08-streak
# ══════════════════════════════════════════════════════════
# 09 — GERÄTE-ÜBERSICHT
# ══════════════════════════════════════════════════════════
- launchApp:
clearState: true
- waitForAnimationToEnd:
timeout: 5000
- 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"
# FRAGILE: Koordinaten-Tap
- tapOn:
id: "header-avatar-btn"
- waitForAnimationToEnd:
timeout: 2000
- assertVisible:
text: "Einstellungen"
- tapOn:
text: "Einstellungen"
- waitForAnimationToEnd:
timeout: 3000
- scrollUntilVisible:
element:
text: "Geräte"
direction: DOWN
timeout: 5000
- tapOn:
text: "Geräte"
- waitForAnimationToEnd:
timeout: 4000
- assertVisible:
text: "Dieses Gerät"
- waitForAnimationToEnd:
timeout: 1000
- takeScreenshot: screenshots/09-geraete