Skip to content

iOS E2E

iOS E2E #7

Workflow file for this run

# =============================================================================
# iOS E2E - integration tests (pre-publish)
# =============================================================================
#
# Stage: RC-E2E in the RC release pipeline.
#
# Runs the .af-e2e/test-plan.json scenarios against the plugin source
# (example linked to the plugin via path). Drives
# the unified scripts/af-scenario-runner.sh on an iOS simulator.
#
# Triggers:
# - workflow_call from rc-release.yml (the gate before publish-rc)
# - workflow_dispatch for manual reruns and ad-hoc validation
# - Weekly cron (Sunday 02:00 UTC) to catch SDK / RN drift
#
# Reports land in .af-e2e/reports/ and are uploaded as ios-e2e-<run> artifacts.
# =============================================================================
name: iOS E2E
on:
workflow_call:
inputs:
ref:
description: 'Branch, tag, or SHA to check out. Defaults to the calling workflow''s ref.'
required: false
type: string
default: ''
workflow_dispatch:
inputs:
ref:
description: 'Branch, tag, or SHA to check out (default: workflow ref)'
required: false
type: string
default: ''
schedule:
- cron: '0 2 * * 0'
concurrency:
group: e2e-ios-${{ github.ref }}
cancel-in-progress: true
jobs:
e2e-ios:
name: E2E Tests (iOS)
runs-on: macos-15
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
ref: ${{ inputs.ref != '' && inputs.ref || github.ref }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Select Xcode version
run: |
XCODE=$(ls /Applications | grep -E "^Xcode_[0-9]" | sort -V | tail -1)
sudo xcode-select -s "/Applications/$XCODE"
xcodebuild -version
- name: Boot iOS simulator (newest iOS runtime)
run: |
# Pick a plain iPhone (15/16/17) from the newest available iOS 17/18
# runtime. macos-15 ships iOS 18.x simulators; we filter to iOS 17/18
# for deep-link phases. iOS 17.4+ and iOS 18.x deliver URLs straight
# to application:openURL:options: without a confirmation prompt.
DEVICES_JSON=$(xcrun simctl list -j devices available)
UDID=$(echo "$DEVICES_JSON" | jq -r '
.devices
| to_entries
| map(select(.key | test("iOS-1[78]")))
| sort_by(.key) | reverse
| map(
. as $rt
| $rt.value
| map(select(.name | test("^iPhone 1[5-7]$")))
| first
| (if . then {udid, runtime: $rt.key, name} else empty end)
)
| first // empty
| .udid // empty
')
if [[ -z "$UDID" ]]; then
echo "No iPhone 15/16/17 on iOS 17/18 found; falling back to first available iPhone."
UDID=$(echo "$DEVICES_JSON" | jq -r '.devices[][] | select(.name | test("^iPhone")) | .udid' | head -1)
fi
[[ -z "$UDID" ]] && { echo "::error::No iOS simulator available"; exit 1; }
echo "Selected simulator UDID: $UDID"
echo "$DEVICES_JSON" | jq -r --arg u "$UDID" '
.devices | to_entries[] | . as $rt
| $rt.value[] | select(.udid == $u)
| "Runtime: \($rt.key) Device: \(.name)"
'
xcrun simctl boot "$UDID"
xcrun simctl bootstatus "$UDID" -b
echo "IOS_SIMULATOR_UDID=$UDID" >> "$GITHUB_ENV"
- name: Cache CocoaPods
uses: actions/cache@v5
with:
path: example/ios/Pods
key: pods-${{ hashFiles('example/ios/Podfile', 'react-native-appsflyer.podspec') }}
restore-keys: pods-
- name: Install example app dependencies
working-directory: example
run: npm install
- name: Install CocoaPods
working-directory: example/ios
run: pod install
- name: Write example app .env
env:
ENV_FILE: ${{ secrets.ENV_FILE }}
run: |
echo "$ENV_FILE" | base64 -d > example/.env
echo "::group::.env keys (values redacted)"
sed 's/=.*/=***/' example/.env
echo "::endgroup::"
- name: Cache iOS build output
uses: actions/cache@v5
with:
path: example/ios/build
key: ios-build-${{ hashFiles('ios/**', 'index.js', 'example/ios/**', 'example/.env') }}
restore-keys: ios-build-
- name: Build iOS simulator app (debug)
working-directory: example/ios
env:
FORCE_BUNDLING: "1"
run: |
xcodebuild build \
-workspace example.xcworkspace \
-scheme example \
-configuration Debug \
-destination "platform=iOS Simulator,id=$IOS_SIMULATOR_UDID" \
-derivedDataPath build \
| xcpretty
- name: Run E2E (af-scenario-runner against .af-e2e/test-plan.json)
run: ./scripts/af-scenario-runner.sh --platform ios --plan .af-e2e/test-plan.json
- name: Dump iOS QA log file (debug)
if: always()
run: |
SIM_DATA="$HOME/Library/Developer/CoreSimulator/Devices/$IOS_SIMULATOR_UDID/data"
LOG=$(find "$SIM_DATA/Containers/Data/Application" -name "af_qa_logs.txt" 2>/dev/null | head -1)
echo "=== QA log: ${LOG:-not found} ==="
cat "$LOG" 2>/dev/null | head -200 || echo "(empty)"
- name: Dump simulator state and deep-link events (debug)
if: failure()
run: |
echo "=== Booted simulators ==="
xcrun simctl list devices booted || true
echo "=== Installed apps for $IOS_SIMULATOR_UDID ==="
xcrun simctl listapps "$IOS_SIMULATOR_UDID" 2>/dev/null \
| grep -E "com.appsflyer|CFBundleURLSchemes|example" || true
echo "=== Last 90s of simctl URL/launchservices events ==="
xcrun simctl spawn "$IOS_SIMULATOR_UDID" log show \
--last 90s --style compact 2>/dev/null \
| grep -E "Opening URL|openURL|launchservices|com.appsflyer|FrontBoard:Process" \
| tail -200 || true
- name: Upload E2E reports
if: always()
uses: actions/upload-artifact@v5
with:
name: ios-e2e-${{ github.run_number }}
path: .af-e2e/reports/
retention-days: 30