Skip to content

WIP

WIP #64

name: e2e-tests-hybrid
# on:
# workflow_dispatch:
# pull_request:
env:
TERM: xterm-256color
FORCE_COLOR: 1
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# Job 1: Run regtest infrastructure on Ubuntu with tunneling
regtest-infrastructure:
runs-on: ubuntu-latest
outputs:
electrum-url: ${{ env.ELECTRUM_URL }}
lnd-url: ${{ env.LND_URL }}
steps:
- name: Clone E2E tests
uses: actions/checkout@v4
with:
repository: synonymdev/bitkit-e2e-tests
path: bitkit-e2e-tests
ref: ${{ github.event.inputs.e2e_branch || 'ios' }}
- name: Run regtest setup
working-directory: bitkit-e2e-tests
id: setup-regtest
run: |
cd docker
mkdir lnd && chmod 777 lnd
docker compose pull --quiet
docker compose up -d
echo "Waiting for electrum server..."
while ! nc -z '127.0.0.1' 60001; do sleep 1; done
echo "Electrum server is ready!"
echo "Waiting for LND to initialize..."
# Wait for LND container to be running
timeout 60 bash -c 'until docker ps | grep lnd | grep -q "Up"; do echo "Waiting for LND container..."; sleep 2; done'
# Fix permissions for LND data directory
echo "Fixing LND data directory permissions..."
sudo chown -R runner:runner lnd/
chmod -R 755 lnd/
# Check if LND directory exists and wait for admin.macaroon
echo "Checking LND data directory..."
ls -la lnd/ || echo "LND directory not found"
ls -la lnd/data/ || echo "LND data directory not found"
ls -la lnd/data/chain/ || echo "LND chain directory not found"
ls -la lnd/data/chain/bitcoin/ || echo "LND bitcoin directory not found"
ls -la lnd/data/chain/bitcoin/regtest/ || echo "LND regtest directory not found"
# Wait for admin.macaroon with timeout and better error handling
echo "Waiting for admin.macaroon..."
timeout 120 bash -c 'until [ -f lnd/data/chain/bitcoin/regtest/admin.macaroon ]; do echo "Waiting for admin.macaroon... ($(date))"; sleep 5; done' || {
echo "Timeout waiting for admin.macaroon. Checking LND logs:"
docker logs lnd
echo "LND directory contents:"
find lnd -name "*.macaroon" -type f 2>/dev/null || echo "No macaroon files found"
exit 1
}
echo "Admin macaroon found! Setting permissions..."
chmod -R 777 lnd
echo "Regtest infrastructure ready locally"
- name: Setup cloudflared
uses: AnimMouse/setup-cloudflared@v2
- name: Expose Electrum with Cloudflare Tunnel
id: tunnel-electrum
run: |
echo "=== Creating Electrum tunnel ==="
# Create tunnel manually using cloudflared
nohup cloudflared tunnel --url http://localhost:60001 > electrum-tunnel.log 2>&1 &
ELECTRUM_TUNNEL_PID=$!
echo "ELECTRUM_TUNNEL_PID=$ELECTRUM_TUNNEL_PID" >> $GITHUB_ENV
echo "Electrum tunnel PID: $ELECTRUM_TUNNEL_PID"
# Wait for tunnel to be ready and extract URL
sleep 10
if [ -f electrum-tunnel.log ]; then
ELECTRUM_URL=$(grep -o 'https://[^[:space:]]*\.trycloudflare\.com' electrum-tunnel.log | head -1)
if [ -n "$ELECTRUM_URL" ]; then
echo "Electrum tunnel URL: $ELECTRUM_URL"
echo "ELECTRUM_URL=$ELECTRUM_URL" >> $GITHUB_ENV
else
echo "Failed to extract Electrum tunnel URL"
cat electrum-tunnel.log
fi
fi
continue-on-error: true
- name: Expose LND with Cloudflare Tunnel
id: tunnel-lnd
run: |
echo "=== Creating LND tunnel ==="
# Create tunnel manually using cloudflared
nohup cloudflared tunnel --url http://localhost:9735 > lnd-tunnel.log 2>&1 &
LND_TUNNEL_PID=$!
echo "LND_TUNNEL_PID=$LND_TUNNEL_PID" >> $GITHUB_ENV
echo "LND tunnel PID: $LND_TUNNEL_PID"
# Wait for tunnel to be ready and extract URL
sleep 10
if [ -f lnd-tunnel.log ]; then
LND_URL=$(grep -o 'https://[^[:space:]]*\.trycloudflare\.com' lnd-tunnel.log | head -1)
if [ -n "$LND_URL" ]; then
echo "LND tunnel URL: $LND_URL"
echo "LND_URL=$LND_URL" >> $GITHUB_ENV
else
echo "Failed to extract LND tunnel URL"
cat lnd-tunnel.log
fi
fi
continue-on-error: true
- name: Keep regtest running
run: |
# Start background processes to keep regtest and tunnels alive
echo "Starting regtest keep-alive process..."
nohup bash -c 'while true; do echo "Regtest infrastructure running... $(date)"; sleep 60; done' &
KEEPALIVE_PID=$!
echo "Keep-alive PID: $KEEPALIVE_PID"
# Start tunnel keep-alive process
echo "Starting tunnel keep-alive process..."
nohup bash -c 'while true; do echo "Tunnels running... $(date)"; sleep 60; done' &
TUNNEL_PID=$!
echo "Tunnel keep-alive PID: $TUNNEL_PID"
echo "Regtest infrastructure and tunnels are running in background"
# Verify the processes are running
sleep 5
if ps -p $KEEPALIVE_PID > /dev/null; then
echo "✅ Keep-alive process is running"
else
echo "❌ Keep-alive process failed to start"
exit 1
fi
if ps -p $TUNNEL_PID > /dev/null; then
echo "✅ Tunnel keep-alive process is running"
else
echo "❌ Tunnel keep-alive process failed to start"
exit 1
fi
# Show what's running
echo "Current processes:"
ps aux | grep -E "(docker|electrum|lnd|keep|tunnel)" | head -10
# Test if services are actually accessible
echo "Testing local connectivity..."
if nc -z localhost 60001; then
echo "✅ Electrum server is accessible locally"
else
echo "❌ Electrum server is NOT accessible locally"
fi
if nc -z localhost 9735; then
echo "✅ LND is accessible locally"
else
echo "❌ LND is NOT accessible locally"
fi
# Show Docker containers
echo "Docker containers:"
docker ps
# Show tunnel URLs and verify they exist
echo "Tunnel URLs:"
echo "Electrum: $ELECTRUM_URL"
echo "LND: $LND_URL"
# Verify tunnel URLs are not empty
if [ -z "$ELECTRUM_URL" ] || [ -z "$LND_URL" ]; then
echo "❌ ERROR: Tunnel URLs are empty!"
echo "This means the Cloudflare tunnel setup failed."
echo "Continuing anyway - E2E tests will handle the error..."
else
echo "✅ Tunnel URLs are available"
fi
# Job is complete - background processes will keep everything running
echo "✅ Regtest infrastructure and tunnels are running in background"
echo "Job 1 completed - E2E tests can now start"
# Job 2: Build iOS app and run E2E tests on macOS
e2e-tests:
runs-on: macos-latest
needs: regtest-infrastructure
strategy:
fail-fast: false
matrix:
shard:
- { name: onboarding, grep: "@onboarding" }
name: e2e-tests - ${{ matrix.shard.name }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '16.4'
- name: Build iOS app
env:
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CHATWOOT_API: ${{ secrets.CHATWOOT_API }}
E2E: true
# Override regtest URLs to point to the Ubuntu runner
E2E_ELECTRUM_SERVER: ${{ needs.regtest-infrastructure.outputs.electrum-url }}
E2E_LND_URL: ${{ needs.regtest-infrastructure.outputs.lnd-url }}
run: |
xcodebuild -workspace Bitkit.xcodeproj/project.xcworkspace \
-scheme Bitkit \
-configuration Debug \
-destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' \
-derivedDataPath DerivedData \
build
- name: Prepare app for E2E tests
run: |
mkdir -p e2e-app
cp -r DerivedData/Build/Products/Debug-iphonesimulator/Bitkit.app e2e-app/bitkit.app
- name: Clone E2E tests
uses: actions/checkout@v4
with:
repository: synonymdev/bitkit-e2e-tests
path: bitkit-e2e-tests
ref: ${{ github.event.inputs.e2e_branch || 'ios' }}
- name: Copy app to E2E tests directory
run: |
# Create the aut directory in the E2E tests
mkdir -p bitkit-e2e-tests/aut
# Copy the app to the expected location
cp -r e2e-app/bitkit.app bitkit-e2e-tests/aut/bitkit.app
# Verify the app was copied
ls -la bitkit-e2e-tests/aut/
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install dependencies
working-directory: bitkit-e2e-tests
run: npm ci
- name: Install Appium
run: |
# Install Appium globally
npm install -g appium@latest
# Install XCUITest driver for iOS
appium driver install xcuitest
# Verify installation
appium driver list
- name: Setup iOS Simulator
run: |
# Set simulator name (easy to change if needed)
SIMULATOR_NAME="iPhone 16"
echo "SIMULATOR_NAME=$SIMULATOR_NAME" >> $GITHUB_ENV
# List available simulators
xcrun simctl list devices available
# Kill any existing simulators to start fresh
xcrun simctl shutdown all || true
sleep 5
# Boot the specified simulator
echo "Booting $SIMULATOR_NAME..."
xcrun simctl boot "$SIMULATOR_NAME" || true
# Wait for simulator to boot
echo "Waiting for simulator to boot..."
for i in {1..30}; do
if xcrun simctl list devices | grep "$SIMULATOR_NAME" | grep -q "Booted"; then
echo "$SIMULATOR_NAME is booted!"
break
fi
echo "Waiting for $SIMULATOR_NAME boot... ($i/30)"
sleep 5
done
# Additional wait for simulator to be fully ready
sleep 15
# Install the app
xcrun simctl install "$SIMULATOR_NAME" e2e-app/bitkit.app
# Verify app installation
xcrun simctl listapps "$SIMULATOR_NAME" | grep -i bitkit || echo "App not found in simulator"
# Check simulator status
xcrun simctl list devices | grep "$SIMULATOR_NAME"
# Launch simulator app to ensure it's visible
open -a Simulator
sleep 5
- name: Start Appium Server
run: |
# Start Appium server in background with more verbose logging
appium --port 4723 --log-level debug --relaxed-security --session-override &
APPIUM_PID=$!
echo "APPIUM_PID=$APPIUM_PID" >> $GITHUB_ENV
# Wait for Appium to be ready (macOS doesn't have timeout command)
echo "Waiting for Appium to start..."
for i in {1..30}; do
if curl -s http://localhost:4723/status >/dev/null 2>&1; then
echo "Appium server is ready!"
break
fi
echo "Waiting for Appium... ($i/30)"
sleep 2
done
# Final check
if ! curl -s http://localhost:4723/status >/dev/null 2>&1; then
echo "Appium failed to start after 60 seconds"
exit 1
fi
# Test Appium with a simple request
echo "Testing Appium connection..."
curl -s http://localhost:4723/status | head -20
# Check if simulator is accessible to Appium
echo "Checking simulator accessibility..."
xcrun simctl list devices | grep "iPhone 16"
# Try to get simulator logs to debug
echo "Recent simulator logs:"
xcrun simctl spawn "iPhone 16" log show --last 1m --predicate 'process == "SpringBoard"' | head -10
- name: Run E2E Tests (${{ matrix.shard.name }})
timeout-minutes: 30
run: |
cd bitkit-e2e-tests
# Setup logging
LOGDIR="./artifacts"
mkdir -p "$LOGDIR"
LOGFILE="$LOGDIR/simulator.log"
# Start simulator logging
xcrun simctl spawn "$SIMULATOR_NAME" log stream --predicate 'process == "Bitkit"' --style compact > "$LOGFILE" &
LOG_PID=$!
# Cleanup function
cleanup() {
echo "Cleaning up processes..."
kill "$LOG_PID" 2>/dev/null || true
wait "$LOG_PID" 2>/dev/null || true
kill "$APPIUM_PID" 2>/dev/null || true
wait "$APPIUM_PID" 2>/dev/null || true
echo "Cleanup complete"
}
trap cleanup EXIT INT TERM
# Configure WebDriverIO to use our Appium server
echo "Configuring WebDriverIO to use our Appium server..."
# Add hostname if missing
if ! grep -q "hostname:" wdio.conf.ts; then
echo "Adding hostname configuration..."
sed -i '' 's/port: 4723,/port: 4723,\n hostname: '\''localhost'\'',/' wdio.conf.ts
fi
# Remove appium service to use our manually started server
echo "Removing appium service from services array..."
sed -i '' "s/services: \['appium'\]/services: []/" wdio.conf.ts
# Update timeouts for better reliability
echo "Updating timeout configurations..."
sed -i '' 's/waitforTimeout: 30000/waitforTimeout: 60000/' wdio.conf.ts
sed -i '' 's/connectionRetryTimeout: 120000/connectionRetryTimeout: 300000/' wdio.conf.ts
# Get the UDID of the booted simulator
SIMULATOR_UDID=$(xcrun simctl list devices | grep "$SIMULATOR_NAME" | grep "Booted" | grep -o '[A-F0-9-]\{36\}')
echo "Booted simulator UDID: $SIMULATOR_UDID"
# Device name is already set to "iPhone 16" in wdio.conf.ts
echo "Using simulator: $SIMULATOR_NAME (UDID: $SIMULATOR_UDID)"
# Check the full capabilities section
echo "Full capabilities section:"
grep -A 20 -B 5 "capabilities:" wdio.conf.ts
# Update UDID to match the booted simulator
echo "Updating UDID to match booted simulator: $SIMULATOR_UDID"
sed -i '' "s/'appium:udid': '[^']*'/'appium:udid': '$SIMULATOR_UDID'/" wdio.conf.ts
# Verify the UDID was updated
echo "Updated UDID in capabilities:"
grep -A 5 -B 5 "appium:udid" wdio.conf.ts
# Verify the bundle ID was updated
echo "Updated bundle ID in capabilities:"
grep -A 5 -B 5 "appium:bundleId" wdio.conf.ts
# Don't manually launch the app - let Appium handle it
# Just ensure simulator is ready and app is installed
echo "Ensuring simulator is ready..."
xcrun simctl list devices | grep "$SIMULATOR_UDID"
# Verify app is installed
echo "Verifying app installation..."
xcrun simctl listapps "$SIMULATOR_UDID" | grep -i bitkit || echo "App not found"
# Make sure simulator is fully booted and ready
echo "Waiting for simulator to be fully ready..."
sleep 15
# Pre-build WebDriverAgent to avoid timeout during session creation
echo "Pre-building WebDriverAgent..."
xcrun simctl spawn "$SIMULATOR_UDID" xcrun simctl list devices || echo "Simulator check failed"
# Ensure simulator is fully responsive
echo "Testing simulator responsiveness..."
for i in {1..10}; do
if xcrun simctl list devices | grep "$SIMULATOR_UDID" | grep -q "Booted"; then
echo "Simulator is responsive (attempt $i/10)"
break
fi
echo "Waiting for simulator responsiveness... ($i/10)"
sleep 5
done
# Check simulator status one more time
echo "Final simulator status:"
xcrun simctl list devices | grep "$SIMULATOR_UDID"
# Re-check UDID in case simulator was reset
echo "Re-checking simulator UDID..."
CURRENT_UDID=$(xcrun simctl list devices | grep "$SIMULATOR_NAME" | grep "Booted" | grep -o '[A-F0-9-]\{36\}')
if [ "$CURRENT_UDID" != "$SIMULATOR_UDID" ]; then
echo "UDID changed from $SIMULATOR_UDID to $CURRENT_UDID, updating..."
sed -i '' "s/'appium:udid': '[^']*'/'appium:udid': '$CURRENT_UDID'/" wdio.conf.ts
SIMULATOR_UDID="$CURRENT_UDID"
fi
# Test regtest connectivity
echo "Testing regtest connectivity..."
echo "E2E_ELECTRUM_SERVER: $E2E_ELECTRUM_SERVER"
echo "E2E_LND_URL: $E2E_LND_URL"
# Check if we have valid regtest URLs
if [ -z "$E2E_ELECTRUM_SERVER" ] || [ -z "$E2E_LND_URL" ]; then
echo "❌ ERROR: Regtest URLs are empty!"
echo "This means the regtest-infrastructure job failed or didn't provide outputs."
echo "Check the regtest-infrastructure job logs for errors."
exit 1
fi
echo "✅ Using Cloudflare Tunnel URLs for regtest connectivity"
# Test electrum server connectivity via tunnel
if [ -n "$E2E_ELECTRUM_SERVER" ]; then
echo "Testing electrum server at $E2E_ELECTRUM_SERVER..."
# Test with curl (tunnel URLs are HTTPS)
echo "Testing with curl..."
if curl -s --connect-timeout 10 "$E2E_ELECTRUM_SERVER" >/dev/null 2>&1; then
echo "✅ Electrum server is reachable via tunnel"
else
echo "❌ Electrum server is not reachable via tunnel"
fi
fi
# Test LND connectivity via tunnel
if [ -n "$E2E_LND_URL" ]; then
echo "Testing LND at $E2E_LND_URL..."
if curl -s --connect-timeout 10 "$E2E_LND_URL" >/dev/null 2>&1; then
echo "✅ LND is reachable via tunnel"
else
echo "❌ LND is not reachable via tunnel"
echo "This will cause tests to fail. Check if the regtest infrastructure job is still running."
fi
fi
# Check if we should continue with tests despite connectivity issues
if [ -n "$E2E_ELECTRUM_SERVER" ] && [ -n "$E2E_LND_URL" ]; then
if ! curl -s --connect-timeout 5 "$E2E_ELECTRUM_SERVER" >/dev/null 2>&1 || ! curl -s --connect-timeout 5 "$E2E_LND_URL" >/dev/null 2>&1; then
echo "⚠️ WARNING: Regtest services are not reachable via tunnel. Tests will likely fail."
echo "This could be due to:"
echo "1. The regtest infrastructure job has stopped"
echo "2. Cloudflare tunnel issues"
echo "3. Network connectivity issues"
echo ""
echo "🔧 SOLUTIONS TO TRY:"
echo "1. Check if the regtest-infrastructure job is still running"
echo "2. Check Cloudflare tunnel logs"
echo "3. Use a different regtest service (e.g., Blockstream's regtest API)"
echo ""
echo "Continuing with tests anyway..."
else
echo "✅ Regtest services are reachable via Cloudflare tunnels"
fi
fi
# Add more debugging before running tests
echo "=== Pre-test Debugging ==="
echo "Current directory: $(pwd)"
echo "Simulator status:"
xcrun simctl list devices | grep "$SIMULATOR_NAME"
echo "Appium status:"
curl -s http://localhost:4723/status | head -5
echo "App status on simulator:"
xcrun simctl listapps "$SIMULATOR_UDID" | grep -i bitkit || echo "App not found"
echo "WebDriverIO config:"
head -20 wdio.conf.ts
echo "Test files:"
find test/specs -name "*.e2e.ts" | head -5
echo "Package.json scripts:"
grep -A 5 -B 5 "e2e:ios" package.json
echo "========================="
# Pass everything through to WDIO/Mocha with more verbose output
echo "Starting WebDriverIO tests..."
echo "Running tests with grep pattern: ${{ matrix.shard.grep }}"
# Try different parameter formats for WebDriverIO/Mocha
if ! npm run e2e:ios -- --grep "${{ matrix.shard.grep }}" 2>&1 | tee "$LOGDIR/webdriverio.log"; then
echo "Grep parameter failed, trying without filter..."
if ! npm run e2e:ios 2>&1 | tee "$LOGDIR/webdriverio.log"; then
echo "WebDriverIO failed with exit code $?"
echo "Checking for error logs..."
if [ -f "$LOGDIR/webdriverio.log" ]; then
echo "WebDriverIO log contents:"
cat "$LOGDIR/webdriverio.log"
fi
exit 1
fi
fi
env:
RECORD_VIDEO: true
# Use the regtest infrastructure from Ubuntu runner
E2E_ELECTRUM_SERVER: ${{ needs.regtest-infrastructure.outputs.electrum-url }}
E2E_LND_URL: ${{ needs.regtest-infrastructure.outputs.lnd-url }}
- name: Upload E2E Artifacts (${{ matrix.shard.name }})
if: failure()
uses: actions/upload-artifact@v4
with:
name: e2e-artifacts_${{ matrix.shard.name }}_${{ github.run_number }}
path: bitkit-e2e-tests/artifacts/
- name: Shutdown Cloudflare Tunnels
if: always()
run: |
echo "Attempting to shutdown Cloudflare tunnels..."
# Try to kill any cloudflared processes
pkill -f cloudflared || echo "No cloudflared processes found"
# Try to remove PID file if it exists
rm -f cloudflared.pid || echo "No PID file found"
echo "Cloudflare tunnel cleanup completed"