Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .agents/skills/mobile-e2e/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ xcodebuild -workspace Folo.xcworkspace \
build
```

### Apple Silicon simulator optimization

When running on an Apple Silicon Mac and building only for the simulator used in the current run, prefer compiling only the active `arm64` simulator architecture:

```bash
xcodebuild ... \
ONLY_ACTIVE_ARCH=YES \
ARCHS=arm64
```

Use this optimization only for local self-test / e2e simulator builds tied to the current machine. Do not use it when you need a universal simulator app for other machines or when running on Intel Macs.

Expected output pattern:

```bash
Expand Down
18 changes: 18 additions & 0 deletions .agents/skills/mobile-self-test/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,15 @@ xcodebuild -workspace Folo.xcworkspace \
clean build
```

On Apple Silicon Macs, when the build is only for the dedicated simulator created for the current self-test run, prefer compiling only the active `arm64` simulator architecture:

```bash
ONLY_ACTIVE_ARCH=YES \
ARCHS=arm64
```

Do not use that optimization when you need a universal simulator bundle for other machines or when the host Mac is Intel.

Expected output pattern:

```bash
Expand Down Expand Up @@ -299,6 +308,15 @@ export E2E_PASSWORD='Password123!'
export E2E_EMAIL="folo-self-test-$(date +%Y%m%d%H%M%S)@example.com"
```

For non-auth iOS self-tests, bootstrap auth through the standard iOS runner mode after the app has been installed and launched once:

```bash
cd apps/mobile
pnpm run e2e:ios:bootstrap
```

This bootstrap path is the default for `prod` and `local` self-tests. Only skip it when the feature under test is login, registration, sign-out, session restoration, or another auth-specific flow that must be validated visually end-to-end.

#### iOS registration bootstrap

```bash
Expand Down
22 changes: 22 additions & 0 deletions apps/mobile/e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
pnpm run e2e:doctor
pnpm run e2e:android
pnpm run e2e:ios
pnpm run e2e:ios:bootstrap
```

## iOS Notes
Expand All @@ -22,6 +23,27 @@ pnpm run e2e:ios
- `content.yaml`: ensure onboarding feed unfollowed -> follow -> timeline/read-unread -> unfollow
- The iOS runner resets the simulator, disables password autofill prompts, installs the provided app bundle, then executes Maestro.

## Prod iOS auth bootstrap

When non-auth iOS self-tests need a signed-in simulator quickly, bootstrap auth through the standard iOS runner mode:

```bash
pnpm run e2e:ios:bootstrap
```

This mode uses the auth bootstrap helper on iOS when `EXPO_PUBLIC_E2E_ENV_PROFILE=prod` or `EXPO_PUBLIC_E2E_ENV_PROFILE=local`, and falls back to the normal iOS registration flow for other environments.

Optional environment variables:

- `E2E_EMAIL`
- `E2E_PASSWORD`
- `MAESTRO_IOS_DEVICE_ID`
- `E2E_API_URL`
- `E2E_CALLBACK_URL`
- `E2E_BUNDLE_ID`

The bootstrap script signs in against prod using the mobile fallback token header, writes the auth cookie into the simulator's `ExpoSQLiteStorage` fallback store, and relaunches the app.

## Environment

- `E2E_EMAIL`
Expand Down
69 changes: 65 additions & 4 deletions apps/mobile/e2e/run-maestro.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
set -eu

platform="${1:?platform is required}"
mode="${2:-full}"
debug_output="${MAESTRO_DEBUG_OUTPUT:-e2e/artifacts/${platform}}"
run_suffix="$(date +%s)-$$"

Expand Down Expand Up @@ -40,6 +41,7 @@ extract_ios_app_from_tar() {
}

resolve_ios_app_path() {
device_id="$1"
if [ -n "${MAESTRO_IOS_APP_PATH:-}" ]; then
if [ -d "${MAESTRO_IOS_APP_PATH}" ]; then
printf '%s' "${MAESTRO_IOS_APP_PATH}"
Expand All @@ -60,7 +62,13 @@ resolve_ios_app_path() {
return
fi

find "$HOME/Library/Developer/Xcode/DerivedData" -path '*Build/Products/Release-iphonesimulator/Folo.app' | head -n1
existing_path="$(find "$HOME/Library/Developer/Xcode/DerivedData" -path '*Build/Products/Release-iphonesimulator/Folo.app' | head -n1)"
if [ -n "${existing_path}" ]; then
printf '%s' "${existing_path}"
return
fi

build_ios_simulator_app "${device_id}"
}

wait_for_android_ready() {
Expand Down Expand Up @@ -106,6 +114,48 @@ prepare_ios_simulator() {
xcrun simctl bootstatus "${device_id}" -b >/dev/null 2>&1 || true
}

append_ios_arch_args() {
arch="$(uname -m)"
if [ "${arch}" = "arm64" ]; then
printf '%s\n' "ONLY_ACTIVE_ARCH=YES" "ARCHS=arm64"
fi
}

build_ios_simulator_app() {
device_id="$1"
(
cd ios
pod install
set -- $(append_ios_arch_args)
PROFILE=e2e-ios-simulator \
EXPO_PUBLIC_E2E_ENV_PROFILE="${EXPO_PUBLIC_E2E_ENV_PROFILE:-}" \
EXPO_PUBLIC_E2E_LANGUAGE="${EXPO_PUBLIC_E2E_LANGUAGE:-en}" \
xcodebuild -workspace Folo.xcworkspace \
-scheme Folo \
-configuration Release \
-sdk iphonesimulator \
-destination "id=${device_id}" \
build \
"$@"
)

find "$HOME/Library/Developer/Xcode/DerivedData" -path '*Build/Products/Release-iphonesimulator/Folo.app' | head -n1
}

run_ios_bootstrap_auth() {
device_id="$1"

case "${EXPO_PUBLIC_E2E_ENV_PROFILE:-prod}" in
prod|local)
pnpm run e2e:bootstrap:ios:prod-auth -- --udid "${device_id}"
;;
*)
maestro test --format junit --platform ios --device "${device_id}" --debug-output "${debug_output}/bootstrap-auth" \
-e E2E_EMAIL="${E2E_EMAIL}" -e E2E_PASSWORD="${E2E_PASSWORD}" e2e/flows/ios/register.yaml
;;
esac
}

case "${platform}" in
ios)
device_id="$(resolve_ios_device)"
Expand All @@ -114,7 +164,7 @@ case "${platform}" in
exit 1
fi

app_path="$(resolve_ios_app_path)"
app_path="$(resolve_ios_app_path "${device_id}")"
if [ -z "${app_path}" ] || [ ! -d "${app_path}" ]; then
echo "Unable to resolve a built iOS .app bundle. Set MAESTRO_IOS_APP_PATH or place a build-*.tar.gz in apps/mobile." >&2
exit 1
Expand All @@ -124,8 +174,19 @@ case "${platform}" in
xcrun simctl install "${device_id}" "${app_path}" >/dev/null 2>&1 || true
xcrun simctl launch "${device_id}" is.follow >/dev/null 2>&1 || true

maestro test --format junit --platform ios --device "${device_id}" --debug-output "${debug_output}/auth" \
-e E2E_EMAIL="${E2E_EMAIL}" -e E2E_PASSWORD="${E2E_PASSWORD}" e2e/flows/ios/auth.yaml
case "${mode}" in
full)
maestro test --format junit --platform ios --device "${device_id}" --debug-output "${debug_output}/auth" \
-e E2E_EMAIL="${E2E_EMAIL}" -e E2E_PASSWORD="${E2E_PASSWORD}" e2e/flows/ios/auth.yaml
;;
bootstrap-auth)
run_ios_bootstrap_auth "${device_id}"
;;
*)
echo "Unsupported iOS runner mode: ${mode}" >&2
exit 1
;;
esac

;;
android)
Expand Down
3 changes: 3 additions & 0 deletions apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
"bump": "vv",
"dev": "npm run start",
"e2e:android": "sh ./e2e/run-maestro.sh android",
"e2e:bootstrap:ios:auth": "tsx scripts/e2e-prod-ios-auth-bootstrap.ts",
"e2e:bootstrap:ios:prod-auth": "pnpm run e2e:bootstrap:ios:auth",
"e2e:doctor": "sh -c 'for f in e2e/flows/shared/*.yaml e2e/flows/android/*.yaml e2e/flows/ios/*.yaml; do maestro check-syntax $f; done'",
"e2e:ios": "sh ./e2e/run-maestro.sh ios",
"e2e:ios:bootstrap": "sh ./e2e/run-maestro.sh ios bootstrap-auth",
"eas-build-post-install": "rm -rf $TMPDIR/metro-cache",
"eas-build-pre-install": "command -v pod >/dev/null 2>&1 && pod repo update || echo 'CocoaPods not found, skipping pod repo update'",
"ios": "expo run:ios",
Expand Down
Loading
Loading