diff --git a/.eas/workflows/e2e-test-android.yml b/.eas/workflows/e2e-test-android.yml new file mode 100644 index 00000000..d76e5aca --- /dev/null +++ b/.eas/workflows/e2e-test-android.yml @@ -0,0 +1,24 @@ +name: e2e-test-android + +on: + # Only manual trigger - saves EAS build quota + workflow_dispatch: {} + # Uncomment below to auto-run on PRs (uses build quota): + # pull_request: + # branches: ['main', 'expo-e2e'] + +jobs: + build_android_for_e2e: + name: Build Android APK for E2E + type: build + params: + platform: android + profile: e2e-test + + maestro_test: + name: Run Maestro E2E Tests + needs: [build_android_for_e2e] + type: maestro + params: + build_id: ${{ needs.build_android_for_e2e.outputs.build_id }} + flow_path: ['.maestro/demo-flow.yaml'] diff --git a/.maestro/.gitignore b/.maestro/.gitignore new file mode 100644 index 00000000..eff720b6 --- /dev/null +++ b/.maestro/.gitignore @@ -0,0 +1,12 @@ +# Ignore Maestro recordings and temporary files +*.mp4 +*.mov +*.gif +*.avi + +# But keep the demo flow and documentation +!README.md +!QUICKSTART.md +!*.yaml +!*.sh +!*.bat diff --git a/.maestro/QUICKSTART.md b/.maestro/QUICKSTART.md new file mode 100644 index 00000000..f2af25ea --- /dev/null +++ b/.maestro/QUICKSTART.md @@ -0,0 +1,213 @@ +# E2E Testing - Quick Start + +Run CrewSplit E2E tests on EAS cloud or generate demo GIFs locally! 🎬 + +## Option A: Run Tests on EAS Cloud (Recommended) + +**No setup required!** Tests run automatically on pull requests. + +### Manual Trigger + +```bash +# Run E2E workflow on EAS cloud +npx eas-cli@latest workflow:run .eas/workflows/e2e-test-android.yml +``` + +**Benefits:** +- ✅ No Docker, no local builds +- ✅ Runs entirely in the cloud +- ✅ Automatic on PRs to `main` or `expo-e2e` + +--- + +## Option B: Generate Demo GIF Locally + +### Prerequisites + +1. **Install Maestro CLI** + ```bash + curl -Ls "https://get.maestro.mobile.dev" | bash + ``` + +2. **Install ffmpeg** (for GIF conversion) + ```bash + # macOS + brew install ffmpeg + + # Ubuntu/Debian + sudo apt install ffmpeg + + # Windows + choco install ffmpeg + ``` + +3. **Optional: Install gifsicle** (for optimization) + ```bash + brew install gifsicle # macOS + sudo apt install gifsicle # Ubuntu + ``` + +## Generate Demo (2 Steps) + +### Step 1: Run Against Dev Build + +```bash +# Start Expo +npm start +``` + +### Step 2: Run Demo Flow with Recording + +```bash +# In another terminal, run Maestro flow +maestro test .maestro/demo-flow.yaml --format mp4 +``` + +This runs the demo flow and records a video. The recording is saved to `~/.maestro/tests/[timestamp]/recording.mp4`. + +### Step 3: Convert to GIF + +#### Option A: Using the helper script (recommended) + +```bash +# macOS/Linux +./.maestro/convert-to-gif.sh ~/.maestro/tests/latest/recording.mp4 + +# Windows +.maestro\convert-to-gif.bat %USERPROFILE%\.maestro\tests\latest\recording.mp4 +``` + +#### Option B: Manual conversion with ffmpeg + +```bash +ffmpeg -i recording.mp4 \ + -vf "fps=15,scale=720:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \ + -loop 0 \ + demo.gif +``` + +**Done!** Your demo GIF is ready 🎉 + +--- + +## Quick Commands + +```bash +# Run E2E tests on EAS cloud +npx eas-cli@latest workflow:run .eas/workflows/e2e-test-android.yml + +# Generate demo GIF locally +npm start # Terminal 1 +maestro test .maestro/demo-flow.yaml --format mp4 && ./.maestro/convert-to-gif.sh # Terminal 2 +``` + +--- + +## Tips + +### Reduce GIF Size + +If your GIF is >10MB (GitHub limit): + +```bash +# Lower resolution (600px width) +./.maestro/convert-to-gif.sh recording.mp4 demo.gif 600 + +# Lower resolution (480px width) +./.maestro/convert-to-gif.sh recording.mp4 demo.gif 480 + +# OR trim the video first +ffmpeg -i recording.mp4 -ss 5 -t 20 trimmed.mp4 +./.maestro/convert-to-gif.sh trimmed.mp4 +``` + +### Speed Up/Slow Down + +```bash +# Speed up 1.5x +ffmpeg -i recording.mp4 -filter:v "setpts=0.67*PTS" -an recording-fast.mp4 +./.maestro/convert-to-gif.sh recording-fast.mp4 + +# Slow down 0.75x +ffmpeg -i recording.mp4 -filter:v "setpts=1.33*PTS" recording-slow.mp4 +./.maestro/convert-to-gif.sh recording-slow.mp4 +``` + +### Different Quality Levels + +```bash +# High quality (larger file) +ffmpeg -i recording.mp4 -vf "fps=20,scale=1080:-1" demo-hq.gif + +# Medium quality (recommended) +ffmpeg -i recording.mp4 -vf "fps=15,scale=720:-1" demo.gif + +# Low quality (smaller file) +ffmpeg -i recording.mp4 -vf "fps=10,scale=480:-1" demo-small.gif +``` + +--- + +## Troubleshooting + +### "maestro: command not found" + +Add Maestro to your PATH: + +```bash +export PATH="$PATH:$HOME/.maestro/bin" + +# Add to ~/.bashrc or ~/.zshrc to make permanent +echo 'export PATH="$PATH:$HOME/.maestro/bin"' >> ~/.zshrc +``` + +### "App not installed" + +Specify the APK path explicitly: + +```bash +maestro test .maestro/demo-flow.yaml --app ./path/to/app.apk +``` + +### "Element not found" errors + +The flow might need adjustment for your UI. Edit [.maestro/demo-flow.yaml](.maestro/demo-flow.yaml) and: +- Add `optional: true` to non-critical taps +- Increase `timeout` values +- Adjust element text/IDs to match your app + +### Recording file not found + +Check Maestro recordings directory: + +```bash +# Find latest recording +ls -lt ~/.maestro/tests/ | head -5 + +# On Windows +dir %USERPROFILE%\.maestro\tests /o-d +``` + +--- + +## Next Steps + +- **Customize the flow**: Edit [.maestro/demo-flow.yaml](.maestro/demo-flow.yaml) +- **Create variations**: Copy and modify for different scenarios +- **Full documentation**: See [README.md](README.md) for advanced options + +--- + +## Quick Reference + +| Task | Command | +|------|---------| +| Build E2E app | `npm run e2e:build:android` | +| Run demo flow | `npm run e2e:demo` | +| Convert to GIF | `./.maestro/convert-to-gif.sh recording.mp4` | +| Optimize GIF | `gifsicle -O3 --lossy=80 -o out.gif in.gif` | +| Test locally | `maestro test .maestro/demo-flow.yaml` | + +--- + +**🎬 Happy Demo-ing!** diff --git a/.maestro/README.md b/.maestro/README.md new file mode 100644 index 00000000..76f74f8b --- /dev/null +++ b/.maestro/README.md @@ -0,0 +1,397 @@ +# CrewSplit E2E Testing & Demo Generation + +This directory contains Maestro flows for end-to-end testing and generating demo GIFs/videos of the CrewSplit app. + +## Overview + +- **Tool**: [Maestro](https://maestro.dev/) - Mobile UI testing framework +- **Purpose**: Automated E2E tests + Demo video/GIF generation +- **Platform Support**: Android (APK) & iOS (Simulator) + +## Quick Start + +### Option A: Run on EAS Cloud (Recommended - No Docker!) + +**Automatic on Pull Requests:** +E2E tests run automatically when you create a PR to `main` or `expo-e2e` branches. + +**Manual Trigger:** +```bash +# Run E2E test workflow on EAS cloud +npx eas-cli@latest workflow:run .eas/workflows/e2e-test-android.yml +``` + +EAS handles everything: builds the APK, runs Maestro tests, and provides results. No local setup needed! + +--- + +### Option B: Run Locally (For Demo Recording) + +**1. Install Maestro CLI** + +```bash +# macOS/Linux +curl -Ls "https://get.maestro.mobile.dev" | bash + +# Windows (via WSL) +curl -Ls "https://get.maestro.mobile.dev" | bash + +# Verify installation +maestro --version +``` + +**2. Run Against Dev Build** + +```bash +# Start Expo dev server +npm start + +# In another terminal, run Maestro flow +maestro test .maestro/demo-flow.yaml +``` + +**3. Record Demo Video** + +Maestro automatically records screen during test execution: + +```bash +# Run with video recording (default format: .mp4) +maestro test .maestro/demo-flow.yaml --format mp4 + +# Recording will be saved to: ~/.maestro/tests/[timestamp]/recording.mp4 +``` + +## Available Flows + +### `demo-flow.yaml` + +**Purpose**: Complete feature showcase for marketing/documentation + +**Duration**: ~30 seconds at 1x speed + +**Showcases**: +- ✅ Quick trip creation UX +- ✅ Browsing expenses with sample data (Weekend Ski Trip) +- ✅ Settlement recommendations +- ✅ Statistics with pie charts & breakdowns +- ✅ Participant detail view +- ✅ Settlement detail modal + +**Ideal for**: README demos, social media, onboarding videos + +--- + +## Converting Video to GIF + +Maestro outputs `.mp4` videos by default. To create GIFs for GitHub/documentation: + +### Option 1: Using `ffmpeg` (Recommended) + +```bash +# Install ffmpeg +# macOS +brew install ffmpeg + +# Ubuntu/Debian +sudo apt install ffmpeg + +# Windows (via Chocolatey) +choco install ffmpeg + +# Convert MP4 to optimized GIF +ffmpeg -i recording.mp4 \ + -vf "fps=15,scale=720:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \ + -loop 0 \ + demo.gif + +# For smaller file size (lower quality) +ffmpeg -i recording.mp4 \ + -vf "fps=10,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \ + -loop 0 \ + demo-small.gif +``` + +**Parameters explained**: +- `fps=15` - Frame rate (15fps = smooth, smaller than 30fps) +- `scale=720:-1` - Width 720px, height auto-scaled +- `loop=0` - Infinite loop +- `palettegen/paletteuse` - High-quality color optimization + +### Option 2: Using Online Tools + +1. **Ezgif.com** - https://ezgif.com/video-to-gif + - Upload `.mp4` + - Adjust size, frame rate, speed + - Download optimized GIF + +2. **CloudConvert** - https://cloudconvert.com/mp4-to-gif + - Batch conversion + - Quality presets + +### Option 3: Using Gifski (Best Quality) + +```bash +# Install gifski +brew install gifski + +# Convert with highest quality +gifski -o demo.gif recording.mp4 --fps 20 --quality 90 --width 720 +``` + +--- + +## GIF Optimization Tips + +### Target Specs for Demos + +| Platform | Max Size | Recommended Resolution | FPS | Duration | +|----------|----------|----------------------|-----|----------| +| GitHub README | 10MB | 720px width | 15-20 | <30s | +| Twitter/X | 15MB | 720px width | 20 | <30s | +| Discord | 8MB | 600px width | 15 | <20s | +| Documentation | 5MB | 600px width | 10-15 | <30s | + +### Reducing File Size + +**1. Lower FPS**: 10-15fps instead of 20+ +```bash +ffmpeg -i recording.mp4 -vf "fps=10,scale=720:-1" demo.gif +``` + +**2. Reduce dimensions**: 600px or 480px width +```bash +ffmpeg -i recording.mp4 -vf "fps=15,scale=600:-1" demo.gif +``` + +**3. Trim duration**: Cut to only essential parts +```bash +# Extract 5-25 second segment +ffmpeg -i recording.mp4 -ss 5 -t 20 -vf "fps=15,scale=720:-1" demo.gif +``` + +**4. Optimize with gifsicle**: +```bash +brew install gifsicle +gifsicle -O3 --lossy=80 -o demo-optimized.gif demo.gif +``` + +--- + +## Adjusting Demo Speed + +If the flow is too slow/fast: + +### Speed Up Video Before Converting + +```bash +# 1.5x speed +ffmpeg -i recording.mp4 -filter:v "setpts=0.67*PTS" -an recording-fast.mp4 + +# 2x speed +ffmpeg -i recording.mp4 -filter:v "setpts=0.5*PTS" -an recording-2x.mp4 + +# Then convert to GIF +ffmpeg -i recording-fast.mp4 -vf "fps=15,scale=720:-1" demo.gif +``` + +### Slow Down Video + +```bash +# 0.75x speed (slower) +ffmpeg -i recording.mp4 -filter:v "setpts=1.33*PTS" recording-slow.mp4 +``` + +--- + +## Running on EAS Cloud + +### Workflow Configuration + +EAS workflows are defined in [`.eas/workflows/e2e-test-android.yml`](../.eas/workflows/e2e-test-android.yml): + +```yaml +name: e2e-test-android + +on: + pull_request: + branches: ['main', 'expo-e2e'] + workflow_dispatch: # Allow manual trigger + +jobs: + build_android_for_e2e: + name: Build Android APK for E2E + type: build + params: + platform: android + profile: e2e-test + + maestro_test: + name: Run Maestro E2E Tests + needs: [build_android_for_e2e] + type: maestro + params: + build_id: ${{ needs.build_android_for_e2e.outputs.build_id }} + flow_path: ['.maestro/demo-flow.yaml'] +``` + +### How It Works + +1. **Trigger**: Workflow runs automatically on PRs or manually via CLI +2. **Build**: EAS builds the Android APK using the `e2e-test` profile +3. **Test**: Maestro runs `demo-flow.yaml` on EAS cloud infrastructure +4. **Results**: Test results appear in the EAS dashboard + +### Manual Execution + +```bash +# Run workflow manually +npx eas-cli@latest workflow:run .eas/workflows/e2e-test-android.yml + +# View workflow status +npx eas-cli@latest workflow:list +``` + +### Benefits of EAS Cloud + +- ✅ **No Docker required** - EAS handles infrastructure +- ✅ **No local builds** - Runs entirely in the cloud +- ✅ **Automatic on PRs** - Catches regressions before merge +- ✅ **Parallel execution** - Can run multiple tests simultaneously +- ✅ **Built-in reporting** - Results integrated with EAS dashboard + +--- + +## Customizing Flows + +### Adding New Test Scenarios + +Create new `.yaml` files in `.maestro/`: + +```bash +.maestro/ +├── demo-flow.yaml # Main demo (30s) +├── quick-demo.yaml # Short version (15s) +├── settlement-focus.yaml # Only settlement features +└── multi-currency-demo.yaml # Multi-currency showcase +``` + +### Maestro Flow Syntax Reference + +```yaml +# Launch app +- launchApp + +# Tap on element (by text) +- tapOn: "Button Text" + +# Tap on element (by ID) +- tapOn: + id: "element-id" + +# Input text +- inputText: "Text to type" + +# Scroll +- scroll + +# Wait (milliseconds) +- wait: 1000 + +# Assert element is visible +- assertVisible: "Expected Text" + +# Navigate back +- back +``` + +**Full docs**: https://maestro.dev/reference/commands + +--- + +## Troubleshooting + +### "App not installed" error + +```bash +# Make sure app is built and path is correct +maestro test .maestro/demo-flow.yaml --app ./path/to/app.apk +``` + +### "Element not found" errors + +- Check element IDs/text in flow match actual UI +- Add `optional: true` for non-critical taps +- Increase `timeout` values for slow screens + +### Recording not saved + +```bash +# Check Maestro recordings directory +ls ~/.maestro/tests/ + +# Latest recording +ls -lt ~/.maestro/tests/ | head -5 +``` + +### GIF too large + +- Reduce resolution: `scale=480:-1` instead of `720:-1` +- Lower FPS: `fps=10` instead of `fps=15` +- Trim duration: Use `-ss` and `-t` flags +- Use lossy compression: `gifsicle --lossy=80` + +--- + +## Sample Data Setup + +The demo flow expects the **Weekend Ski Trip** sample to be loaded. To ensure consistent demos: + +### Auto-load Sample Data on App Launch (for E2E builds) + +Add to app initialization (e.g., `app/_layout.tsx`): + +```typescript +// Only for E2E/demo builds +if (process.env.DETOX_ENABLED === 'true') { + const { loadSampleTrip } = useSampleData(); + + useEffect(() => { + loadSampleTrip('weekend_ski_trip'); + }, []); +} +``` + +This ensures fresh sample data on every E2E run. + +--- + +## Resources + +- **Maestro Docs**: https://maestro.dev/ +- **Maestro CLI Reference**: https://maestro.dev/reference/cli +- **FFmpeg Docs**: https://ffmpeg.org/documentation.html +- **Gifski**: https://gif.ski/ +- **EAS Workflows**: https://docs.expo.dev/eas/workflows/ + +--- + +## Quick Reference: End-to-End Workflow + +```bash +# 1. Build E2E app +eas build --profile e2e-test --platform android --local + +# 2. Run Maestro flow with recording +maestro test .maestro/demo-flow.yaml --app app.apk + +# 3. Convert to GIF +ffmpeg -i ~/.maestro/tests/latest/recording.mp4 \ + -vf "fps=15,scale=720:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \ + -loop 0 \ + demo.gif + +# 4. Optimize GIF +gifsicle -O3 --lossy=80 -o demo-optimized.gif demo.gif + +# Done! 🎉 +``` diff --git a/.maestro/convert-to-gif.bat b/.maestro/convert-to-gif.bat new file mode 100644 index 00000000..2952e666 --- /dev/null +++ b/.maestro/convert-to-gif.bat @@ -0,0 +1,82 @@ +@echo off +REM Convert Maestro recording to optimized GIF (Windows version) +REM Usage: convert-to-gif.bat [output.gif] [width] + +setlocal EnableDelayedExpansion + +REM Check if ffmpeg is installed +where ffmpeg >nul 2>&1 +if %errorlevel% neq 0 ( + echo [Warning] ffmpeg not found. Please install it: + echo choco install ffmpeg + echo OR download from: https://ffmpeg.org/download.html + exit /b 1 +) + +REM Parse arguments +set INPUT=%~1 +set OUTPUT=%~2 +set WIDTH=%~3 + +if "%OUTPUT%"=="" set OUTPUT=demo.gif +if "%WIDTH%"=="" set WIDTH=720 + +if "%INPUT%"=="" ( + echo Usage: %~nx0 ^ [output.gif] [width] + echo. + echo Examples: + echo %~nx0 recording.mp4 + echo %~nx0 recording.mp4 my-demo.gif + echo %~nx0 recording.mp4 my-demo.gif 600 + exit /b 1 +) + +if not exist "%INPUT%" ( + echo [Error] Input file not found: %INPUT% + exit /b 1 +) + +echo [Converting] Video to GIF... +echo Input: %INPUT% +echo Output: %OUTPUT% +echo Width: %WIDTH%px +echo. + +REM Convert with high-quality palette +ffmpeg -i "%INPUT%" -vf "fps=15,scale=%WIDTH%:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 "%OUTPUT%" -y + +if %errorlevel% neq 0 ( + echo [Error] Conversion failed + exit /b 1 +) + +REM Check if gifsicle is available +where gifsicle >nul 2>&1 +if %errorlevel% equ 0 ( + echo [Optimizing] GIF with gifsicle... + set TEMP_GIF=%OUTPUT%.tmp + move "%OUTPUT%" "!TEMP_GIF!" >nul + gifsicle -O3 --lossy=80 -o "%OUTPUT%" "!TEMP_GIF!" + del "!TEMP_GIF!" +) + +REM Get file size +for %%A in ("%OUTPUT%") do set SIZE=%%~zA +set /a SIZE_MB=!SIZE! / 1024 / 1024 + +echo. +echo [Success] GIF created successfully! +echo File: %OUTPUT% +echo Size: !SIZE_MB!MB + +if !SIZE_MB! gtr 10 ( + echo. + echo [Warning] GIF is !SIZE_MB!MB (GitHub limit is 10MB^) + echo. + echo To reduce size, try: + echo * Lower width: %~nx0 %INPUT% %OUTPUT% 600 + echo * Lower width: %~nx0 %INPUT% %OUTPUT% 480 +) + +echo. +echo [Done] Conversion complete! diff --git a/.maestro/convert-to-gif.sh b/.maestro/convert-to-gif.sh new file mode 100644 index 00000000..9947c5a7 --- /dev/null +++ b/.maestro/convert-to-gif.sh @@ -0,0 +1,103 @@ +#!/bin/bash +# Convert Maestro recording to optimized GIF +# Usage: ./convert-to-gif.sh [output.gif] [width] + +set -e + +# Colors for output +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Check if ffmpeg is installed +if ! command -v ffmpeg &> /dev/null; then + echo -e "${YELLOW}⚠️ ffmpeg not found. Please install it:${NC}" + echo " macOS: brew install ffmpeg" + echo " Ubuntu: sudo apt install ffmpeg" + echo " Windows: choco install ffmpeg" + exit 1 +fi + +# Parse arguments +INPUT="${1}" +OUTPUT="${2:-demo.gif}" +WIDTH="${3:-720}" + +if [ -z "$INPUT" ]; then + echo -e "${YELLOW}Usage: $0 [output.gif] [width]${NC}" + echo "" + echo "Examples:" + echo " $0 recording.mp4" + echo " $0 recording.mp4 my-demo.gif" + echo " $0 recording.mp4 my-demo.gif 600" + exit 1 +fi + +if [ ! -f "$INPUT" ]; then + echo -e "${YELLOW}❌ Input file not found: $INPUT${NC}" + + # Try to find latest Maestro recording + LATEST_RECORDING=$(find ~/.maestro/tests -name "recording.mp4" -type f -print0 2>/dev/null | xargs -0 ls -t 2>/dev/null | head -1) + + if [ -n "$LATEST_RECORDING" ]; then + echo -e "${BLUE}💡 Found latest Maestro recording:${NC}" + echo " $LATEST_RECORDING" + echo "" + read -p "Use this file? (y/n) " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + INPUT="$LATEST_RECORDING" + else + exit 1 + fi + else + exit 1 + fi +fi + +echo -e "${BLUE}🎬 Converting video to GIF...${NC}" +echo " Input: $INPUT" +echo " Output: $OUTPUT" +echo " Width: ${WIDTH}px" +echo "" + +# Convert with high-quality palette +ffmpeg -i "$INPUT" \ + -vf "fps=15,scale=${WIDTH}:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \ + -loop 0 \ + "$OUTPUT" \ + -y + +# Check if gifsicle is available for optimization +if command -v gifsicle &> /dev/null; then + echo -e "${BLUE}🔧 Optimizing GIF with gifsicle...${NC}" + TEMP_GIF="${OUTPUT}.tmp" + mv "$OUTPUT" "$TEMP_GIF" + gifsicle -O3 --lossy=80 -o "$OUTPUT" "$TEMP_GIF" + rm "$TEMP_GIF" +fi + +# Get file size +SIZE=$(du -h "$OUTPUT" | cut -f1) + +echo "" +echo -e "${GREEN}✅ GIF created successfully!${NC}" +echo " File: $OUTPUT" +echo " Size: $SIZE" +echo "" + +# Check if size is too large +SIZE_BYTES=$(stat -f%z "$OUTPUT" 2>/dev/null || stat -c%s "$OUTPUT" 2>/dev/null) +SIZE_MB=$((SIZE_BYTES / 1024 / 1024)) + +if [ $SIZE_MB -gt 10 ]; then + echo -e "${YELLOW}⚠️ Warning: GIF is ${SIZE_MB}MB (GitHub limit is 10MB)${NC}" + echo "" + echo "To reduce size, try:" + echo " • Lower width: $0 $INPUT $OUTPUT 600" + echo " • Lower width: $0 $INPUT $OUTPUT 480" + echo " • Trim video: ffmpeg -i $INPUT -ss 5 -t 20 trimmed.mp4" +fi + +echo -e "${BLUE}🎉 Done!${NC}" diff --git a/.maestro/demo-flow.yaml b/.maestro/demo-flow.yaml new file mode 100644 index 00000000..8a555ced --- /dev/null +++ b/.maestro/demo-flow.yaml @@ -0,0 +1,123 @@ +appId: com.crewsplit.app +--- +# CrewSplit Demo Flow - Full Feature Showcase +# Duration: ~30 seconds at 1x speed +# This flow demonstrates: trip creation, expense tracking, settlements, statistics, and participant details + +# ============================================ +# SCENE 1: Quick Create (5 seconds) +# ============================================ +- launchApp: + clearState: true + +# Wait for app to fully load with sample data +- waitForAnimationToEnd: + timeout: 5000 + +# Tap the "Create Trip" button in the footer +- tapOn: "Create Trip" + +# Wait for create trip screen to appear +- waitForAnimationToEnd + +# Tap the trip name input field (should auto-focus but tap to ensure) +- tapOn: "Trip name" + +# Type trip name +- inputText: "Coffee Run" + +# Tap the "Create Trip" button at bottom +- tapOn: "Create Trip" + +# Wait for navigation back to home +- waitForAnimationToEnd + +# ============================================ +# SCENE 2: Browse Rich Sample Data (15 seconds) +# ============================================ + +# Tap into the Weekend Ski Trip sample +- tapOn: "Weekend Ski Trip" + +# Wait for trip dashboard to load +- waitForAnimationToEnd + +# Tap the "Expenses" action card +- tapOn: "Expenses" + +# Wait for expenses list to load +- waitForAnimationToEnd + +# Scroll to show variety of expenses +- scroll + +# Tap into first expense to show detail +- tapOn: + index: 0 + +# Go back to expenses list +- back +- waitForAnimationToEnd + +# Go back to trip dashboard +- back +- waitForAnimationToEnd + +# Navigate to Settlement card +- tapOn: "Settlement" + +# Wait for settlement screen +- waitForAnimationToEnd + +# ============================================ +# SCENE 3: Deep Insights - Statistics (8 seconds) +# ============================================ + +# Go back to dashboard +- back +- waitForAnimationToEnd + +# Navigate to Statistics card +- tapOn: "Statistics" + +# Wait for statistics screen with charts +- waitForAnimationToEnd + +# Scroll down to see participant breakdown +- scroll + +# Tap into first participant detail +- tapOn: + index: 0 + +# Wait for participant detail screen +- waitForAnimationToEnd + +# Toggle between views +- tapOn: "Part Of" +- tapOn: "Paid By" + +# ============================================ +# SCENE 4: Settlement Detail (2 seconds) +# ============================================ + +# Navigate back to statistics +- back +- waitForAnimationToEnd + +# Go back to dashboard +- back +- waitForAnimationToEnd + +# Go to Settlement card again +- tapOn: "Settlement" +- waitForAnimationToEnd + +# Tap on first settlement card +- tapOn: + index: 0 + +# Dismiss modal +- back + +# End of demo flow diff --git a/.maestro/local-setup.md b/.maestro/local-setup.md new file mode 100644 index 00000000..2f25e4ec --- /dev/null +++ b/.maestro/local-setup.md @@ -0,0 +1,101 @@ +# Local Maestro Testing Setup + +## One-Time Setup + +### 1. Build the APK + +```bash +# Generate native Android project +npx expo prebuild --platform android --clean + +# Build release APK +cd android && ./gradlew assembleRelease +``` + +The APK will be at: `android/app/build/outputs/apk/release/app-release.apk` + +### 2. Install on Your Phone + +```bash +# Install via ADB +adb install android/app/build/outputs/apk/release/app-release.apk + +# Or manually: +# - Copy APK to phone +# - Open file manager on phone +# - Tap APK to install +``` + +--- + +## Running Tests Locally + +Once the APK is installed: + +```bash +# Make sure phone is connected via ADB +adb devices + +# Run Maestro test +npm run e2e:local + +# Record demo video +npm run e2e:demo +``` + +--- + +## Rebuilding After Code Changes + +When you change the app code: + +```bash +# Quick rebuild (if android/ folder exists) +cd android && ./gradlew assembleRelease + +# Then reinstall +adb install -r android/app/build/outputs/apk/release/app-release.apk +``` + +**Note:** The `-r` flag reinstalls without uninstalling first, preserving app data. + +--- + +## Troubleshooting + +### "App not found" + +Make sure the APK is installed: +```bash +adb shell pm list packages | grep crewsplit +``` + +Should show: `package:com.crewsplit.app` + +### "Device not found" + +Enable USB debugging on your phone and connect via ADB: +```bash +adb devices +``` + +### Build errors + +Clean and rebuild: +```bash +cd android +./gradlew clean +./gradlew assembleRelease +``` + +--- + +## Alternative: Use EAS Cloud + +To save local build time, use EAS cloud (manual trigger only to save quota): + +```bash +npm run e2e:cloud +``` + +This builds and tests on EAS infrastructure (uses 1 build from your quota). diff --git a/CLAUDE.md b/CLAUDE.md index 1c55d47d..ca82d2c1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -52,6 +52,30 @@ The project uses automated GitHub workflows for versioning and releases: **Important**: Only version bumps trigger builds. Merging without changing version won't create tags or builds. +### E2E Testing & Demo Generation + +The project uses Maestro for end-to-end testing on EAS cloud (no Docker required!): + +```bash +npm run e2e:cloud # Run E2E tests on EAS cloud (recommended) +npm run e2e:local # Run Maestro flow locally +npm run e2e:demo # Run locally with video recording +``` + +**EAS Cloud Testing** (automatic on PRs): +- Tests run automatically on pull requests to `main` or `expo-e2e` +- No local setup, Docker, or builds needed +- Results appear in EAS dashboard + +**Local Demo Generation**: +```bash +npm start # Terminal 1 +npm run e2e:demo # Terminal 2 +./.maestro/convert-to-gif.sh # Convert to GIF +``` + +See [.maestro/QUICKSTART.md](.maestro/QUICKSTART.md) for full guide. + ## Architecture Principles ### 1. Agent-Based Role System diff --git a/eas.json b/eas.json index 0e3b94a5..48fb84ea 100644 --- a/eas.json +++ b/eas.json @@ -16,6 +16,18 @@ "android": { "buildType": "apk" } + }, + "e2e-test": { + "withoutCredentials": true, + "android": { + "buildType": "apk" + }, + "ios": { + "simulator": true + }, + "env": { + "E2E_ENABLED": "true" + } } }, "submit": { diff --git a/package.json b/package.json index 40f5c465..554519c6 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,10 @@ "verify-migrations": "node scripts/verify-migrations.js", "arch-test": "dependency-cruiser --config .dependency-cruiser.js --output-type err src", "arch-test:graph": "dependency-cruiser --config .dependency-cruiser.js --output-type dot src | dot -T svg > architecture-graph.svg", - "arch-test:html": "dependency-cruiser --config .dependency-cruiser.js --output-type err-html --output-to architecture-report.html src" + "arch-test:html": "dependency-cruiser --config .dependency-cruiser.js --output-type err-html --output-to architecture-report.html src", + "e2e:cloud": "npx eas-cli@latest workflow:run .eas/workflows/e2e-test-android.yml", + "e2e:local": "maestro test .maestro/demo-flow.yaml", + "e2e:demo": "maestro test .maestro/demo-flow.yaml --format mp4" }, "dependencies": { "@expo/vector-icons": "^15.0.3",