# Android native E2E tests name: E2E Tests - Android on: push: branches: [ "main" ] paths: - 'apps/mobile-app/**' - 'core/**' - '.github/workflows/e2e-tests-android.yml' pull_request: branches: [ "main" ] paths: - 'apps/mobile-app/**' - 'core/**' - '.github/workflows/e2e-tests-android.yml' workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: android-e2e-tests: if: false # Temporarily disabled - will re-test in separate PR timeout-minutes: 60 runs-on: ubuntu-latest defaults: run: working-directory: apps/mobile-app steps: - name: Free disk space uses: jlumbroso/free-disk-space@main with: tool-cache: false android: false # Keep Android SDK dotnet: false # Keep .NET (we need it) haskell: true large-packages: true docker-images: true swap-storage: true - name: Checkout repository uses: actions/checkout@v4 - name: Setup Java uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '17' - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' cache-dependency-path: apps/mobile-app/package-lock.json - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: 9.0.x - name: Setup Rust toolchain uses: dtolnay/rust-toolchain@stable with: targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android,i686-linux-android - name: Cache Rust dependencies uses: Swatinem/rust-cache@v2 with: workspaces: core/rust - name: Setup Android SDK uses: android-actions/setup-android@v3 - name: Accept Android licenses run: yes | sdkmanager --licenses || true - name: Install Android NDK run: sdkmanager --install "ndk;26.1.10909125" - name: Build core libraries for Android run: | cd ../../core chmod +x build-and-distribute.sh ./build-and-distribute.sh --android - name: Verify core library distribution run: | TARGET_DIRS=( "utils/dist/core/identity-generator" "utils/dist/core/password-generator" "utils/dist/core/models" "utils/dist/core/vault" ) for dir in "${TARGET_DIRS[@]}"; do if [ ! -d "$dir" ]; then echo "Error: Directory $dir does not exist" exit 1 fi done echo "Core library distribution verified" - name: Install mobile app dependencies run: npm ci - name: Build API server working-directory: apps/server run: dotnet build AliasVault.Api - name: Start PostgreSQL run: | sudo systemctl start postgresql sudo -u postgres createuser -s aliasvault || true sudo -u postgres psql -c "ALTER USER aliasvault WITH PASSWORD 'password';" || true sudo -u postgres createdb -O aliasvault aliasdb_e2e_android || true - name: Start API server working-directory: apps/server/AliasVault.Api run: | dotnet run --no-build > /tmp/api-server.log 2>&1 & # Wait for API to be ready echo "Waiting for API to start..." for i in {1..30}; do if curl -s http://localhost:5092/v1/ > /dev/null 2>&1; then echo "API is ready!" break fi echo "Attempt $i: API not ready yet..." sleep 2 done env: ConnectionStrings__AliasServerDbContext: "Host=localhost;Port=5432;Database=aliasdb_e2e_android;Username=aliasvault;Password=password" JWT_KEY: "12345678901234567890123456789012" DATA_PROTECTION_CERT_PASS: "Development" PUBLIC_REGISTRATION_ENABLED: "true" ADMIN_PASSWORD_HASH: "AQAAAAIAAYagAAAAEKWfKfa2gh9Z72vjAlnNP1xlME7FsunRznzyrfqFte40FToufRwa3kX8wwDwnEXZag==" ADMIN_PASSWORD_GENERATED: "2024-01-01T00:00:00Z" # Bind to all interfaces (IPv4 and IPv6) ASPNETCORE_URLS: "http://0.0.0.0:5092" - name: Enable KVM run: | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules sudo udevadm control --reload-rules sudo udevadm trigger --name-match=kvm - name: Cache AVD uses: actions/cache@v4 id: avd-cache with: path: | ~/.android/avd/* ~/.android/adb* key: avd-api-34-x86_64-${{ runner.os }} - name: Create AVD and generate snapshot for caching if: steps.avd-cache.outputs.cache-hit != 'true' uses: reactivecircus/android-emulator-runner@v2 with: api-level: 34 arch: x86_64 target: google_apis force-avd-creation: false emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none disable-animations: true script: echo "Generated AVD snapshot for caching." - name: Start Metro bundler run: | # Start Metro bundler in background (non-interactive mode for CI) npx expo start --offline > /tmp/metro.log 2>&1 & METRO_PID=$! echo $METRO_PID > /tmp/metro.pid # Wait for Metro to be ready echo "Waiting for Metro bundler to start..." for i in {1..30}; do if curl -s http://localhost:8081/status 2>/dev/null | grep -q "packager-status:running"; then echo "Metro bundler is ready!" break fi echo "Attempt $i: Metro not ready yet..." # Show recent log for debugging tail -3 /tmp/metro.log 2>/dev/null || true sleep 2 done - name: Build and Run Android E2E Tests uses: reactivecircus/android-emulator-runner@v2 with: api-level: 34 arch: x86_64 target: google_apis force-avd-creation: false emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none disable-animations: true working-directory: apps/mobile-app/android script: | # Wait for emulator to be fully booted adb wait-for-device adb shell input keyevent 82 # Unlock screen # Set up port forwarding so emulator can reach host's API server and Metro bundler adb reverse tcp:5092 tcp:5092 # API server adb reverse tcp:8081 tcp:8081 # Metro bundler # Build and install the debug APK ./gradlew :app:assembleDebug adb install -r app/build/outputs/apk/debug/app-debug.apk # Run the instrumented tests ./gradlew :app:connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.API_URL=http://10.0.2.2:5092 --stacktrace env: API_URL: "http://10.0.2.2:5092" ANDROID_NDK_HOME: ${{ env.ANDROID_SDK_ROOT }}/ndk/26.1.10909125 - name: Upload test results if: always() uses: actions/upload-artifact@v4 with: name: android-e2e-test-results path: apps/mobile-app/android/app/build/reports/androidTests/ retention-days: 14 - name: Upload test outputs if: always() uses: actions/upload-artifact@v4 with: name: android-e2e-test-outputs path: apps/mobile-app/android/app/build/outputs/androidTest-results/ retention-days: 14 - name: Upload logs on failure if: failure() uses: actions/upload-artifact@v4 with: name: android-e2e-test-logs path: | /tmp/api-server.log /tmp/metro.log retention-days: 14 if-no-files-found: ignore