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
15 changes: 14 additions & 1 deletion appium/scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

## Setup

**Quick start:** run `./bootstrap.sh` — it idempotently installs Appium + drivers,
Vite+ (and the `vpx` symlink), test deps, and creates `.env` from the example.
Then fill in your OneSignal credentials in `.env` and open a new shell so `vpx`
is on `PATH`. The manual steps below are the same thing, broken out.

1. **Clone the SDK repo** next to `sdk-shared` (or set `FLUTTER_DIR`):

```
Expand Down Expand Up @@ -33,12 +38,20 @@
appium driver install uiautomator2 # Android
```

4. **Install [Vite+](https://vite.plus)** (if not already) — it provides the `vpx` command the script uses to run WebdriverIO (the `vpx` symlink is created on `vp`'s first run):
4. **Install [Vite+](https://vite.plus)** (if not already) — it provides the `vpx` command the script uses to run WebdriverIO:

```bash
curl -fsSL https://vite.plus | bash
```

`vpx` is the `vp` binary under an argv[0] alias. If the installer doesn't create the symlink (seen on some versions), add it manually and open a new shell:

```bash
ln -sf ../current/bin/vp ~/.vite-plus/bin/vpx
```

> Note: the npm package `vite-plus` ships only `vp`/`oxfmt`/`oxlint` (no `vpx`). Use the official installer above; `./bootstrap.sh` handles the symlink for you.

The script checks all of these up front and prints the exact install command for anything missing; `node_modules` in `appium/` is installed automatically on first run.

> **CI vs local:** CI runs on BrowserStack (Node 24) without this script. Notification-dependent tests (in `02_push.spec.ts` and `12_activity.spec.ts`) are skipped on BrowserStack iOS via `isBrowserStackIos()` because BrowserStack requires an Enterprise Signing Certificate for those notification flows, which we don't have yet (temporary — they'll be re-enabled once signing support is available), so for now they only run locally. If your local Node is 26+, the script sets `WDIO_USE_NATIVE_FETCH=1` automatically.
Expand Down
113 changes: 113 additions & 0 deletions appium/scripts/bootstrap.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#!/usr/bin/env bash
# One-time local setup for the Appium E2E runner. Idempotent — safe to re-run.
# Installs everything run-local.sh's preflight checks for:
# 1. .env (copied from .env.example if missing)
# 2. appium (global npm install)
# 3. appium drivers (xcuitest for iOS, uiautomator2 for Android)
# 4. Vite+ (provides the vpx command; installs if vp/vpx absent)
# 5. node_modules (bun install / vp install in the appium dir)
set -uo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
APPIUM_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"

RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BOLD='\033[1m'; NC='\033[0m'
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
err() { echo -e "${RED}[ERROR]${NC} $*"; }
ok() { echo -e "${GREEN}[OK]${NC} $*"; }

FAILED=0
NEEDS_ENV=0

echo -e "${BOLD}━━━ Appium E2E bootstrap ━━━${NC}"

# ── 0. Prerequisites ──────────────────────────────────────────────────────────
if ! command -v node >/dev/null 2>&1 || ! command -v npm >/dev/null 2>&1; then
err "node/npm not found. Install Node.js first (https://nodejs.org), then re-run."
exit 1
fi
ok "node $(node -v) / npm $(npm -v)"

# ── 1. .env ───────────────────────────────────────────────────────────────────
if [[ -f "$SCRIPT_DIR/.env" ]]; then
ok ".env present"
elif [[ -f "$SCRIPT_DIR/.env.example" ]]; then
cp "$SCRIPT_DIR/.env.example" "$SCRIPT_DIR/.env"
warn "Created .env from .env.example — set ONESIGNAL_APP_ID / ONESIGNAL_API_KEY before running tests."
NEEDS_ENV=1
else
warn ".env and .env.example both missing — create $SCRIPT_DIR/.env manually."
fi

# ── 2. appium ─────────────────────────────────────────────────────────────────
if command -v appium >/dev/null 2>&1; then
ok "appium $(appium -v 2>/dev/null) already installed"
else
info "Installing appium globally (npm i -g appium)..."
if npm i -g appium; then ok "appium installed"; else err "appium install failed"; FAILED=1; fi
fi

# ── 3. appium drivers ─────────────────────────────────────────────────────────
if command -v appium >/dev/null 2>&1; then
installed="$(appium driver list --installed 2>&1)"
for driver in xcuitest uiautomator2; do
if grep -q "$driver" <<< "$installed"; then
ok "driver '$driver' already installed"
else
info "Installing appium driver '$driver'..."
if appium driver install "$driver"; then ok "driver '$driver' installed"; else err "driver '$driver' install failed"; FAILED=1; fi
fi
done
else
err "Skipping drivers — appium is not available."; FAILED=1
fi

# ── 4. Vite+ (vpx) ────────────────────────────────────────────────────────────
# run-local.sh runs WebdriverIO via `vpx` (Vite+'s package-binary runner, akin
# to npx). `vpx` is the `vp` binary invoked under an argv[0] alias, so it needs a
# vpx symlink next to vp. The installer doesn't always create it (observed on
# 0.2.5), so ensure it explicitly. Note: the npm package `vite-plus` ships only
# vp/oxfmt/oxlint (no vpx) — the official installer is what we want.
VP_HOME="$HOME/.vite-plus"
if command -v vpx >/dev/null 2>&1; then
ok "vpx already available ($(command -v vpx))"
else
if [[ ! -x "$VP_HOME/current/bin/vp" ]]; then
info "Installing Vite+ (provides vp/vpx) — curl -fsSL https://vite.plus | bash ..."
curl -fsSL https://vite.plus | bash || { err "Vite+ install failed — install manually: curl -fsSL https://vite.plus | bash"; FAILED=1; }
fi
if [[ -x "$VP_HOME/current/bin/vp" ]]; then
if [[ ! -e "$VP_HOME/bin/vpx" ]]; then
ln -sf ../current/bin/vp "$VP_HOME/bin/vpx"
ok "created vpx symlink ($VP_HOME/bin/vpx -> ../current/bin/vp)"
else
ok "vpx symlink present ($VP_HOME/bin/vpx)"
fi
warn "Open a new shell (or run: source \"$VP_HOME/env\") so 'vpx' is on PATH."
fi
fi

# ── 5. node_modules ───────────────────────────────────────────────────────────
if [[ -d "$APPIUM_DIR/node_modules" ]]; then
ok "node_modules present in appium/"
elif command -v bun >/dev/null 2>&1; then
info "Installing test deps with bun..."
(cd "$APPIUM_DIR" && bun install) && ok "deps installed (bun)" || { err "bun install failed"; FAILED=1; }
elif command -v vp >/dev/null 2>&1; then
info "Installing test deps with vp..."
(cd "$APPIUM_DIR" && vp install) && ok "deps installed (vp)" || { err "vp install failed"; FAILED=1; }
else
warn "Skipping deps — neither bun nor vp available yet. run-local.sh installs node_modules on first run."
fi

# ── Summary ───────────────────────────────────────────────────────────────────
echo ""
echo -e "${BOLD}━━━ Summary ━━━${NC}"
if (( FAILED )); then
err "Bootstrap finished with errors — resolve the items above and re-run."
exit 1
fi
ok "Bootstrap complete."
(( NEEDS_ENV )) && warn "Remember to fill in your OneSignal credentials in $SCRIPT_DIR/.env"
exit 0
70 changes: 70 additions & 0 deletions appium/scripts/checkout-releases.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env bash
# Check out the latest stable release point in each downstream SDK repo.
# - rel-branch repos: newest stable rel/X.Y.Z branch (betas + non-semver excluded)
# - tag-only repos (expo, ios): newest semver tag (detached HEAD)
# Repo paths honor the same *_DIR overrides as run-local.sh (loaded from .env),
# falling back to the config.sh defaults under $SDK_ROOT.
# Repos with uncommitted changes are skipped, never clobbered.
set -uo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
APPIUM_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
SDK_ROOT="$(cd "$APPIUM_DIR/../.." && pwd)"

# Load .env so *_DIR overrides here match run-local.sh's resolution.
if [[ -f "$SCRIPT_DIR/.env" ]]; then
set -a
source "$SCRIPT_DIR/.env"
set +a
fi

# var|default-subpath|kind (kind: rel = latest rel/* branch, tag = latest semver tag)
# Defaults mirror run-local/config.sh; override any via *_DIR in .env.
REPOS=(
"FLUTTER_DIR|OneSignal-Flutter-SDK|rel"
"RN_DIR|react-native-onesignal|rel"
"CORDOVA_DIR|OneSignal-Cordova-SDK|rel"
"CAPACITOR_DIR|OneSignal-Capacitor-SDK|rel"
"DOTNET_DIR|DotNet/OneSignal-DotNet-SDK|rel"
"UNITY_DIR|OneSignal-Unity-SDK|rel"
"ANDROID_DIR|OneSignal-Android-SDK|rel"
"EXPO_DIR|onesignal-expo-plugin|tag"
"IOS_DIR|OneSignal-iOS-SDK|tag"
)

RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'

for entry in "${REPOS[@]}"; do
IFS='|' read -r var subpath kind <<< "$entry"
p="${!var:-$SDK_ROOT/$subpath}" # .env override wins, else default
name="$(basename "$p")"

if [[ ! -d "$p/.git" ]]; then
echo -e "${RED}SKIP${NC} $name (not a git repo: $p — set $var in .env)"; continue
fi
if [[ -n "$(git -C "$p" status --porcelain)" ]]; then
echo -e "${YELLOW}SKIP${NC} $name (uncommitted changes — leaving on $(git -C "$p" rev-parse --abbrev-ref HEAD))"; continue
fi

git -C "$p" fetch --prune --tags origin >/dev/null 2>&1

if [[ "$kind" == "rel" ]]; then
target=$(git -C "$p" for-each-ref --format='%(refname:short)' 'refs/remotes/origin/rel/*' \
| sed 's|^origin/||' | grep -E '^rel/[0-9]+\.[0-9]+(\.[0-9]+)?$' | sort -V | tail -1)
if [[ -z "$target" ]]; then
echo -e "${RED}SKIP${NC} $name (no stable rel/* branch found)"; continue
fi
else
target=$(git -C "$p" tag --sort=-v:refname | grep -E '^v?[0-9]+\.[0-9]+(\.[0-9]+)?$' | head -1)
if [[ -z "$target" ]]; then
echo -e "${RED}SKIP${NC} $name (no semver tag found)"; continue
fi
fi

if git -C "$p" checkout "$target" >/dev/null 2>&1; then
[[ "$kind" == "rel" ]] && git -C "$p" pull --ff-only >/dev/null 2>&1
echo -e "${GREEN}OK${NC} $name -> $target"
else
echo -e "${RED}FAIL${NC} $name (could not checkout $target)"
fi
done
15 changes: 15 additions & 0 deletions appium/scripts/run-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,15 @@ PLATFORM_FILTER=""
SDKS_FILTER=""
BAIL=0
PODS_REQUESTED=0
RELEASES=0
for arg in "$@"; do
case "$arg" in
--skip-build|--skip-device|--skip-reset|--skip|--quiet|-q)
EXTRA_ARGS+=("$arg") ;;
--pods)
PODS_REQUESTED=1 ;;
--releases)
RELEASES=1 ;;
--spec=*)
EXTRA_ARGS+=("$arg") ;;
--platform=ios|--platform=android)
Expand Down Expand Up @@ -56,6 +59,9 @@ Options:
Note: 'android' (native) skips --platform=ios and
'ios' (native) skips --platform=android.
--bail Stop after the first failing combo
--releases Check out the latest release point in each SDK repo
first (runs checkout-releases.sh; honors *_DIR from
.env). Skips repos with uncommitted changes.

Options forwarded to run-local.sh:
--skip-build Skip per-app build (reuse existing artifact)
Expand Down Expand Up @@ -111,6 +117,15 @@ if (( PODS_REQUESTED )); then
fi
fi

if (( RELEASES )); then
echo -e "${BOLD}━━━ Checking out latest releases ━━━${NC}"
if ! "$SCRIPT_DIR/checkout-releases.sh"; then
error "checkout-releases.sh failed; aborting before running combos"
exit 1
fi
echo ""
fi

declare -a RESULTS
FAILED=0
BAILED=0
Expand Down
6 changes: 3 additions & 3 deletions appium/scripts/run-local/config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,10 @@ USAGE
(check what's installed with: appium driver list --installed)"

if ! command -v vpx >/dev/null 2>&1; then
if command -v vp >/dev/null 2>&1; then
error "vpx not found on PATH. Vite+ creates the vpx symlink on vp's first run — run 'vp --version' once, or reinstall: curl -fsSL https://vite.plus | bash"
if [[ -x "$HOME/.vite-plus/current/bin/vp" ]]; then
error "vpx not found on PATH, but Vite+ is installed — the vpx symlink is missing or ~/.vite-plus/bin isn't on PATH. Run $SCRIPT_DIR/bootstrap.sh (or: ln -sf ../current/bin/vp ~/.vite-plus/bin/vpx && open a new shell / source ~/.vite-plus/env)."
fi
error "vpx not found on PATH. Install Vite+ with: curl -fsSL https://vite.plus | bash"
error "vpx not found on PATH. Run $SCRIPT_DIR/bootstrap.sh (installs Vite+ and creates the vpx symlink), or install manually: curl -fsSL https://vite.plus | bash"
fi

if [[ ! -d "$APPIUM_DIR/node_modules" ]]; then
Expand Down