diff --git a/.github/workflows/e2e-reusable.yml b/.github/workflows/e2e-reusable.yml
index 7b50dfc8c9..85e5fb1698 100644
--- a/.github/workflows/e2e-reusable.yml
+++ b/.github/workflows/e2e-reusable.yml
@@ -150,16 +150,37 @@ jobs:
- name: Run E2E (full suite)
if: ${{ inputs.full }}
+ env:
+ E2E_BAIL_ON_FAILURE: ${{ vars.E2E_BAIL_ON_FAILURE || '' }}
run: |
+ BAIL_FLAG=""
+ if [[ "${E2E_BAIL_ON_FAILURE:-}" == "1" ]]; then
+ BAIL_FLAG="--bail"
+ fi
xvfb-run -a --server-args="-screen 0 1280x960x24" \
- bash app/scripts/e2e-run-session.sh
-
- # Artifact uploads intentionally omitted — this reusable workflow
- # is invoked from release-staging.yml and release-production.yml,
- # and uploaded logs can carry mock-backend payloads, env-var
- # echoes, and CDP transcripts that we don't want pinned to a
- # release artifact. Local repro: rerun the spec via Docker and
- # the same logs land in /tmp.
+ bash app/scripts/e2e-run-all-flows.sh --skip-preflight $BAIL_FLAG
+
+ - name: Upload E2E failure artifacts
+ if: failure()
+ uses: actions/upload-artifact@v5
+ with:
+ name: e2e-failure-logs-${{ runner.os }}-${{ github.run_id }}
+ path: |
+ /tmp/openhuman-e2e-app-*.log
+ app/test/e2e/artifacts/
+ retention-days: 7
+ if-no-files-found: ignore
+
+ - name: Write job summary
+ if: always()
+ run: |
+ echo "## E2E Results (${{ runner.os }})" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ if [ -f /tmp/e2e-summary.txt ]; then
+ cat /tmp/e2e-summary.txt >> $GITHUB_STEP_SUMMARY
+ else
+ echo "No summary file found." >> $GITHUB_STEP_SUMMARY
+ fi
# Rust-side E2E counterpart to the Tauri runs above. Same Linux-only
# scope (CI does not run this on macOS or Windows — the Rust core is
diff --git a/app/scripts/e2e-preflight.sh b/app/scripts/e2e-preflight.sh
new file mode 100755
index 0000000000..d50897e980
--- /dev/null
+++ b/app/scripts/e2e-preflight.sh
@@ -0,0 +1,195 @@
+#!/usr/bin/env bash
+#
+# e2e-preflight.sh — Pre-flight environment validation for the E2E test suite.
+#
+# Checks:
+# 1. The E2E app binary/bundle exists for the current platform.
+# 2. Node.js and pnpm are available.
+# 3. Appium is installed (and the chromium driver is registered).
+# 4. Ports 19222, 4723, and 18473 are not blocked by stale processes.
+#
+# Exits 0 if all hard requirements are met.
+# Exits 1 if any hard requirement is missing.
+# Warnings are printed for soft issues (occupied ports, missing chromium driver)
+# but do not fail the script.
+#
+set -uo pipefail
+
+# ---------------------------------------------------------------------------
+# Color helpers — only when stdout is a terminal.
+# ---------------------------------------------------------------------------
+if [ -t 1 ]; then
+ RED='\033[0;31m'
+ YELLOW='\033[1;33m'
+ GREEN='\033[0;32m'
+ BOLD='\033[1m'
+ RESET='\033[0m'
+else
+ RED='' YELLOW='' GREEN='' BOLD='' RESET=''
+fi
+
+info() { printf "%b[preflight]%b %s\n" "$BOLD" "$RESET" "$*"; }
+ok() { printf "%b[preflight] ✓%b %s\n" "$GREEN" "$RESET" "$*"; }
+warn() { printf "%b[preflight] ⚠%b %s\n" "$YELLOW" "$RESET" "$*" >&2; }
+fail() { printf "%b[preflight] ✗%b %s\n" "$RED" "$RESET" "$*" >&2; }
+
+ERRORS=0
+_fail() { fail "$*"; (( ERRORS++ )) || true; }
+
+APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+
+info "Starting E2E pre-flight checks..."
+echo ""
+
+# ---------------------------------------------------------------------------
+# 1. App binary / bundle
+# ---------------------------------------------------------------------------
+info "Checking E2E app bundle..."
+
+PLATFORM="$(uname -s)"
+BINARY_FOUND=0
+BINARY_PATH=""
+
+case "$PLATFORM" in
+ Darwin)
+ MACOS_BUNDLE="$APP_DIR/src-tauri/target/debug/bundle/macos/OpenHuman.app"
+ if [[ -d "$MACOS_BUNDLE" ]]; then
+ BINARY_FOUND=1
+ BINARY_PATH="$MACOS_BUNDLE"
+ fi
+ ;;
+ Linux)
+ LINUX_BIN="$APP_DIR/src-tauri/target/debug/openhuman"
+ LINUX_DEB="$APP_DIR/src-tauri/target/debug/bundle/deb"
+ if [[ -f "$LINUX_BIN" ]]; then
+ BINARY_FOUND=1
+ BINARY_PATH="$LINUX_BIN"
+ elif [[ -d "$LINUX_DEB" ]]; then
+ BINARY_FOUND=1
+ BINARY_PATH="$LINUX_DEB"
+ fi
+ ;;
+ MINGW*|MSYS*|CYGWIN*|Windows*)
+ WIN_BIN="$APP_DIR/src-tauri/target/debug/openhuman.exe"
+ if [[ -f "$WIN_BIN" ]]; then
+ BINARY_FOUND=1
+ BINARY_PATH="$WIN_BIN"
+ fi
+ ;;
+ *)
+ warn "Unknown platform '$PLATFORM' — cannot verify app bundle path."
+ BINARY_FOUND=1 # don't block on unknown platforms
+ ;;
+esac
+
+if [[ $BINARY_FOUND -eq 1 ]]; then
+ ok "App bundle found: $BINARY_PATH"
+else
+ _fail "E2E build not found for $PLATFORM."
+ case "$PLATFORM" in
+ Darwin)
+ fail " Expected: $MACOS_BUNDLE"
+ ;;
+ Linux)
+ fail " Expected: $LINUX_BIN"
+ ;;
+ MINGW*|MSYS*|CYGWIN*)
+ fail " Expected: $WIN_BIN"
+ ;;
+ esac
+ fail " Run: pnpm --filter openhuman-app test:e2e:build"
+fi
+
+echo ""
+
+# ---------------------------------------------------------------------------
+# 2. Node.js + pnpm
+# ---------------------------------------------------------------------------
+info "Checking Node.js and pnpm..."
+
+if command -v node >/dev/null 2>&1; then
+ NODE_VERSION="$(node --version 2>/dev/null || echo 'unknown')"
+ ok "node found: $NODE_VERSION"
+else
+ _fail "node not found. Node.js is required to run WDIO."
+fi
+
+if command -v pnpm >/dev/null 2>&1; then
+ PNPM_VERSION="$(pnpm --version 2>/dev/null || echo 'unknown')"
+ ok "pnpm found: $PNPM_VERSION"
+else
+ _fail "pnpm not found. Install via: npm install -g pnpm"
+fi
+
+echo ""
+
+# ---------------------------------------------------------------------------
+# 3. Appium + chromium driver
+# ---------------------------------------------------------------------------
+info "Checking Appium..."
+
+if command -v appium >/dev/null 2>&1; then
+ APPIUM_VERSION="$(appium --version 2>/dev/null || echo 'unknown')"
+ ok "appium found: $APPIUM_VERSION"
+
+ # Check for the chromium driver — warn only (e2e-run-session.sh handles this)
+ CHROMIUM_INSTALLED=0
+ if appium driver list --installed 2>&1 | grep -qi "chromium"; then
+ CHROMIUM_INSTALLED=1
+ ok "Appium chromium driver is installed"
+ fi
+ if [[ $CHROMIUM_INSTALLED -eq 0 ]]; then
+ warn "Appium chromium driver not found in 'appium driver list --installed'."
+ warn " To install: appium driver install --source=npm appium-chromium-driver"
+ warn " (e2e-run-session.sh will attempt idempotent install at runtime.)"
+ fi
+else
+ _fail "Appium not found."
+ fail " Install: npm install -g appium@3"
+ fail " Then: appium driver install --source=npm appium-chromium-driver"
+fi
+
+echo ""
+
+# ---------------------------------------------------------------------------
+# 4. Port availability (warnings only — stale processes are soft blockers)
+# ---------------------------------------------------------------------------
+info "Checking port availability..."
+
+_check_port() {
+ local port="$1"
+ local label="$2"
+ local pid=""
+ # Try lsof first (macOS/Linux), fall back to ss (Linux only)
+ if command -v lsof >/dev/null 2>&1; then
+ pid=$(lsof -ti tcp:"$port" 2>/dev/null | head -1 || true)
+ elif command -v ss >/dev/null 2>&1; then
+ pid=$(ss -tlnp "sport = :$port" 2>/dev/null | awk 'NR>1 {match($NF,/pid=([0-9]+)/,a); print a[1]}' | head -1 || true)
+ fi
+
+ if [[ -n "$pid" ]]; then
+ warn "Port $port ($label) is occupied by PID $pid."
+ warn " If this is a stale process from a prior run, kill it:"
+ warn " kill $pid"
+ else
+ ok "Port $port ($label) is free"
+ fi
+}
+
+_check_port 19222 "CEF CDP"
+_check_port 4723 "Appium"
+_check_port 18473 "mock backend (can be pre-running — OK if deliberate)"
+
+echo ""
+
+# ---------------------------------------------------------------------------
+# Summary
+# ---------------------------------------------------------------------------
+if [[ $ERRORS -gt 0 ]]; then
+ printf "%b[preflight] PRE-FLIGHT FAILED%b — %d error(s) above must be resolved before running E2E tests.\n" \
+ "$RED" "$RESET" "$ERRORS" >&2
+ exit 1
+fi
+
+printf "%b[preflight] Pre-flight passed%b — environment looks good.\n" "$GREEN" "$RESET"
+exit 0
diff --git a/app/scripts/e2e-run-all-flows.sh b/app/scripts/e2e-run-all-flows.sh
index fb6afd3fcd..49e1664a51 100755
--- a/app/scripts/e2e-run-all-flows.sh
+++ b/app/scripts/e2e-run-all-flows.sh
@@ -1,159 +1,457 @@
#!/usr/bin/env bash
#
-# Run all E2E WDIO specs sequentially (Appium restarted per spec).
-# Requires a prior E2E app build: pnpm --filter openhuman-app test:e2e:build
+# e2e-run-all-flows.sh — Master E2E orchestrator for all 66 WDIO specs.
#
-# Each spec runs to completion regardless of prior failures; a pass/fail
-# summary is printed at the end and the script exits non-zero if any spec
-# failed. (Previously `set -e` caused the first failure to abort the run
-# and made the terminal appear to crash.)
+# USAGE:
+# bash app/scripts/e2e-run-all-flows.sh [OPTIONS]
+#
+# OPTIONS:
+# --suite=SUITE Run only one suite category. Valid values:
+# auth, navigation, chat, skills, notifications,
+# webhooks, providers, payments, settings, system,
+# journeys, all (default: all)
+# --bail Stop after the first spec failure (default: run all)
+# --skip-preflight Skip the pre-flight environment check
+#
+# ENVIRONMENT:
+# E2E_ARTIFACTS_DIR Directory where failure logs are copied.
+# Default: app/test/e2e/artifacts/YYYYMMDD-HHMMSS
+#
+# REQUIREMENTS:
+# pnpm --filter openhuman-app test:e2e:build (must be run first)
+#
+# Each spec runs to completion regardless of prior failures unless --bail is
+# passed. A per-category mini-summary and a full summary are printed at the
+# end. The script exits non-zero if any spec failed.
+#
+# (Previously `set -e` caused the first failure to abort the run and made
+# the terminal appear to crash. `set -uo pipefail` preserves error detection
+# without aborting mid-run.)
#
set -uo pipefail
APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
-cd "$APP_DIR" || { echo "FATAL: could not cd to $APP_DIR" >&2; exit 1; }
+REPO_DIR="$(cd "$APP_DIR/.." && pwd)"
+cd "$APP_DIR" || {
+ echo "[e2e-run-all-flows] Failed to cd into $APP_DIR" >&2
+ exit 1
+}
+
+# ---------------------------------------------------------------------------
+# Argument parsing
+# ---------------------------------------------------------------------------
+SUITE="all"
+BAIL=0
+SKIP_PREFLIGHT=0
+
+for arg in "$@"; do
+ case "$arg" in
+ --suite=*) SUITE="${arg#--suite=}" ;;
+ --bail) BAIL=1 ;;
+ --skip-preflight) SKIP_PREFLIGHT=1 ;;
+ *)
+ echo "Unknown option: $arg" >&2
+ echo "Usage: bash app/scripts/e2e-run-all-flows.sh [--suite=SUITE] [--bail] [--skip-preflight]" >&2
+ exit 1
+ ;;
+ esac
+done
+
+VALID_SUITES="auth navigation chat skills notifications webhooks providers payments settings system journeys all"
+SUITE_VALID=0
+for s in $VALID_SUITES; do
+ [[ "$SUITE" == "$s" ]] && SUITE_VALID=1 && break
+done
+if [[ $SUITE_VALID -eq 0 ]]; then
+ echo "Invalid suite: '$SUITE'. Valid values: $VALID_SUITES" >&2
+ exit 1
+fi
-# Parallel arrays: names + exit codes collected during the run.
+# ---------------------------------------------------------------------------
+# Artifacts directory
+# ---------------------------------------------------------------------------
+E2E_ARTIFACTS_DIR="${E2E_ARTIFACTS_DIR:-$APP_DIR/test/e2e/artifacts/$(date +%Y%m%d-%H%M%S)}"
+export E2E_ARTIFACTS_DIR
+
+# ---------------------------------------------------------------------------
+# Run tracking: parallel arrays indexed by position.
+# _spec_suite[i] — suite name this spec belongs to
+# _spec_names[i] — human-readable label
+# _spec_results[i] — 0 (pass) or 1 (fail)
+# _spec_duration[i] — wall-clock seconds (integer)
+# ---------------------------------------------------------------------------
+_spec_suite=()
_spec_names=()
_spec_results=()
+_spec_duration=()
+
+_BAILED=0
+_RUN_START_EPOCH=$(date +%s)
+# ---------------------------------------------------------------------------
+# run SPEC LABEL SUITE
+#
+# Records start time, runs e2e-run-spec.sh, records end time and result.
+# Respects --bail: once _BAILED=1 all subsequent run() calls are no-ops
+# that record a synthetic skip (exit 2) so the finish summary is still full.
+# ---------------------------------------------------------------------------
run() {
local spec="$1"
local label="${2:-$1}"
+ local suite="${3:-unknown}"
+
+ _spec_suite+=("$suite")
_spec_names+=("$label")
+
+ if [[ $_BAILED -eq 1 ]]; then
+ _spec_results+=(2) # 2 = skipped due to bail
+ _spec_duration+=(0)
+ return
+ fi
+
+ local t_start t_end duration
+ t_start=$(date +%s)
if "$APP_DIR/scripts/e2e-run-spec.sh" "$spec" "$label"; then
_spec_results+=(0)
else
_spec_results+=(1)
+ if [[ $BAIL -eq 1 ]]; then
+ echo ""
+ echo "[e2e-run-all-flows] --bail: stopping after first failure ($label)"
+ _BAILED=1
+ fi
+ # Copy any failure logs into the artifacts directory
+ _copy_failure_logs "$label"
+ fi
+ t_end=$(date +%s)
+ duration=$(( t_end - t_start ))
+ _spec_duration+=("$duration")
+}
+
+# ---------------------------------------------------------------------------
+# _copy_failure_logs LABEL
+# Copies /tmp/openhuman-e2e-app-*.log files into E2E_ARTIFACTS_DIR on failure.
+# ---------------------------------------------------------------------------
+_copy_failure_logs() {
+ local label="$1"
+ local logs
+ logs=$(ls /tmp/openhuman-e2e-app-*.log 2>/dev/null || true)
+ if [[ -z "$logs" ]]; then
+ return
fi
+ mkdir -p "$E2E_ARTIFACTS_DIR"
+ for f in $logs; do
+ local dest="$E2E_ARTIFACTS_DIR/$(basename "$f" .log)-${label}.log"
+ cp "$f" "$dest" 2>/dev/null || true
+ done
+ echo "[e2e-run-all-flows] Failure logs copied to $E2E_ARTIFACTS_DIR"
}
-# Print summary and exit with the appropriate code.
+# ---------------------------------------------------------------------------
+# _mini_summary SUITE_NAME
+# Prints a one-line pass/fail summary for a completed suite.
+# ---------------------------------------------------------------------------
+_mini_summary() {
+ local suite="$1"
+ local pass=0 fail=0 skip=0
+ for i in "${!_spec_names[@]}"; do
+ if [[ "${_spec_suite[$i]}" != "$suite" ]]; then continue; fi
+ case "${_spec_results[$i]:-2}" in
+ 0) (( pass++ )) || true ;;
+ 1) (( fail++ )) || true ;;
+ 2) (( skip++ )) || true ;;
+ esac
+ done
+ local total=$(( pass + fail + skip ))
+ if [[ $fail -gt 0 ]]; then
+ printf " [%s] %d/%d passed (%d failed)\n" "$suite" "$pass" "$total" "$fail"
+ elif [[ $skip -gt 0 ]]; then
+ printf " [%s] %d/%d passed (%d skipped/bailed)\n" "$suite" "$pass" "$total" "$skip"
+ else
+ printf " [%s] %d/%d passed\n" "$suite" "$pass" "$total"
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# finish — print per-category table, totals, wall time, and hints.
+# Writes a Markdown summary to /tmp/e2e-summary.txt for CI job summaries.
+# ---------------------------------------------------------------------------
finish() {
- local pass=0 fail=0
+ local t_end_epoch
+ t_end_epoch=$(date +%s)
+ local wall=$(( t_end_epoch - _RUN_START_EPOCH ))
+ local wall_min=$(( wall / 60 ))
+ local wall_sec=$(( wall % 60 ))
+
+ local pass=0 fail=0 skip=0
echo ""
- echo "══════════════════════════════════════════════"
- echo " E2E run summary ($(uname -s))"
- echo "══════════════════════════════════════════════"
+ echo "══════════════════════════════════════════════════════════════════"
+ printf " E2E run summary ($(uname -s)) suite=%s\n" "$SUITE"
+ echo "══════════════════════════════════════════════════════════════════"
+
+ # --- per-spec rows ---
+ local prev_suite=""
for i in "${!_spec_names[@]}"; do
- if [[ "${_spec_results[$i]}" -eq 0 ]]; then
- printf " ✓ %s\n" "${_spec_names[$i]}"
- (( pass++ )) || true
- else
- printf " ✗ %s\n" "${_spec_names[$i]}"
- (( fail++ )) || true
+ local cur_suite="${_spec_suite[$i]}"
+ if [[ "$cur_suite" != "$prev_suite" ]]; then
+ echo ""
+ printf " ## %s\n" "$cur_suite"
+ prev_suite="$cur_suite"
fi
+ local dur="${_spec_duration[$i]:-0}"
+ case "${_spec_results[$i]:-2}" in
+ 0)
+ printf " ✓ %-45s %3ds\n" "${_spec_names[$i]}" "$dur"
+ (( pass++ )) || true
+ ;;
+ 1)
+ printf " ✗ %-45s %3ds\n" "${_spec_names[$i]}" "$dur"
+ (( fail++ )) || true
+ ;;
+ 2)
+ printf " - %-45s (skipped/bailed)\n" "${_spec_names[$i]}"
+ (( skip++ )) || true
+ ;;
+ esac
done
- echo "──────────────────────────────────────────────"
- printf " Passed: %d Failed: %d Total: %d\n" "$pass" "$fail" "${#_spec_names[@]}"
- echo "══════════════════════════════════════════════"
+
+ local total=$(( pass + fail + skip ))
+ echo ""
+ echo "──────────────────────────────────────────────────────────────────"
+ printf " Passed: %-4d Failed: %-4d Skipped: %-4d Total: %d\n" \
+ "$pass" "$fail" "$skip" "$total"
+ printf " Wall time: %dm %02ds\n" "$wall_min" "$wall_sec"
+ echo "══════════════════════════════════════════════════════════════════"
+
+ if [[ $fail -gt 0 ]]; then
+ echo ""
+ echo " To re-run a single failing spec:"
+ echo " bash app/scripts/e2e-run-session.sh test/e2e/specs/SPEC.spec.ts"
+ echo ""
+ echo " Artifacts (if any):"
+ echo " $E2E_ARTIFACTS_DIR"
+ echo ""
+ fi
+
+ # --- write /tmp/e2e-summary.txt for CI job summary ---
+ {
+ printf "## E2E Results ($(uname -s)) — suite=%s\n\n" "$SUITE"
+ printf "| Result | Count |\n"
+ printf "|--------|-------|\n"
+ printf "| Passed | %d |\n" "$pass"
+ printf "| Failed | %d |\n" "$fail"
+ printf "| Skipped | %d |\n" "$skip"
+ printf "| **Total** | **%d** |\n" "$total"
+ printf "\n**Wall time:** %dm %02ds\n\n" "$wall_min" "$wall_sec"
+
+ if [[ $fail -gt 0 ]]; then
+ printf "### Failed specs\n\n"
+ for i in "${!_spec_names[@]}"; do
+ if [[ "${_spec_results[$i]}" -eq 1 ]]; then
+ printf -- "- \`%s\`\n" "${_spec_names[$i]}"
+ fi
+ done
+ printf "\n"
+ fi
+ } > /tmp/e2e-summary.txt
+
if [[ $fail -gt 0 ]]; then
exit 1
fi
}
trap finish EXIT
+# ---------------------------------------------------------------------------
+# Pre-flight check (unless --skip-preflight)
+# ---------------------------------------------------------------------------
+if [[ $SKIP_PREFLIGHT -eq 0 ]]; then
+ if [[ -f "$APP_DIR/scripts/e2e-preflight.sh" ]]; then
+ echo "[e2e-run-all-flows] Running pre-flight checks..."
+ if ! bash "$APP_DIR/scripts/e2e-preflight.sh"; then
+ echo "[e2e-run-all-flows] Pre-flight failed. Aborting." >&2
+ exit 1
+ fi
+ else
+ echo "[e2e-run-all-flows] Pre-flight script not found or not executable, skipping."
+ fi
+fi
+
+# ---------------------------------------------------------------------------
+# Helpers: should_run_suite SUITE_NAME
+# Returns 0 (true) if this suite should run given --suite flag.
+# ---------------------------------------------------------------------------
+should_run_suite() {
+ [[ "$SUITE" == "all" || "$SUITE" == "$1" ]]
+}
+
# ---------------------------------------------------------------------------
# Auth & onboarding
# ---------------------------------------------------------------------------
-run "test/e2e/specs/smoke.spec.ts" "smoke"
-run "test/e2e/specs/login-flow.spec.ts" "login"
-run "test/e2e/specs/auth-access-control.spec.ts" "auth"
-run "test/e2e/specs/logout-relogin-onboarding.spec.ts" "logout-relogin"
-run "test/e2e/specs/onboarding-modes.spec.ts" "onboarding-modes"
-run "test/e2e/specs/runtime-picker-login.spec.ts" "runtime-picker-login"
+if should_run_suite "auth"; then
+ echo ""
+ echo "## Running suite: auth"
+ run "test/e2e/specs/smoke.spec.ts" "smoke" "auth"
+ run "test/e2e/specs/login-flow.spec.ts" "login" "auth"
+ run "test/e2e/specs/auth-access-control.spec.ts" "auth" "auth"
+ run "test/e2e/specs/logout-relogin-onboarding.spec.ts" "logout-relogin" "auth"
+ run "test/e2e/specs/onboarding-modes.spec.ts" "onboarding-modes" "auth"
+ run "test/e2e/specs/runtime-picker-login.spec.ts" "runtime-picker-login" "auth"
+ _mini_summary "auth"
+fi
# ---------------------------------------------------------------------------
# Navigation & core UI
# ---------------------------------------------------------------------------
-run "test/e2e/specs/navigation.spec.ts" "navigation"
-run "test/e2e/specs/command-palette.spec.ts" "command-palette"
-run "test/e2e/specs/channels-smoke.spec.ts" "channels-smoke"
-run "test/e2e/specs/insights-dashboard.spec.ts" "insights-dashboard"
+if should_run_suite "navigation"; then
+ echo ""
+ echo "## Running suite: navigation"
+ run "test/e2e/specs/navigation.spec.ts" "navigation" "navigation"
+ run "test/e2e/specs/navigation-smoothness.spec.ts" "navigation-smoothness" "navigation"
+ run "test/e2e/specs/navigation-settings-panels.spec.ts" "navigation-settings" "navigation"
+ run "test/e2e/specs/command-palette.spec.ts" "command-palette" "navigation"
+ run "test/e2e/specs/channels-smoke.spec.ts" "channels-smoke" "navigation"
+ run "test/e2e/specs/insights-dashboard.spec.ts" "insights-dashboard" "navigation"
+ _mini_summary "navigation"
+fi
# ---------------------------------------------------------------------------
# Chat & agent harness
# ---------------------------------------------------------------------------
-run "test/e2e/specs/chat-harness-send-stream.spec.ts" "chat-send-stream"
-run "test/e2e/specs/chat-harness-cancel.spec.ts" "chat-cancel"
-run "test/e2e/specs/chat-harness-scroll-render.spec.ts" "chat-scroll-render"
-run "test/e2e/specs/chat-harness-subagent.spec.ts" "chat-subagent"
-run "test/e2e/specs/chat-harness-wallet-flow.spec.ts" "chat-wallet"
-run "test/e2e/specs/agent-review.spec.ts" "agent-review"
-run "test/e2e/specs/mega-flow.spec.ts" "mega-flow"
+if should_run_suite "chat"; then
+ echo ""
+ echo "## Running suite: chat"
+ run "test/e2e/specs/chat-harness-send-stream.spec.ts" "chat-send-stream" "chat"
+ run "test/e2e/specs/chat-harness-cancel.spec.ts" "chat-cancel" "chat"
+ run "test/e2e/specs/chat-harness-scroll-render.spec.ts" "chat-scroll-render" "chat"
+ run "test/e2e/specs/chat-harness-subagent.spec.ts" "chat-subagent" "chat"
+ run "test/e2e/specs/chat-harness-wallet-flow.spec.ts" "chat-wallet" "chat"
+ run "test/e2e/specs/chat-tool-call-flow.spec.ts" "chat-tool-call" "chat"
+ run "test/e2e/specs/chat-multi-tool-round.spec.ts" "chat-multi-tool" "chat"
+ run "test/e2e/specs/chat-tool-error-recovery.spec.ts" "chat-error-recovery" "chat"
+ run "test/e2e/specs/agent-review.spec.ts" "agent-review" "chat"
+ run "test/e2e/specs/mega-flow.spec.ts" "mega-flow" "chat"
+ _mini_summary "chat"
+fi
# ---------------------------------------------------------------------------
# Skills
# ---------------------------------------------------------------------------
-run "test/e2e/specs/skills-registry.spec.ts" "skills-registry"
-run "test/e2e/specs/skill-execution-flow.spec.ts" "skill-execution"
-run "test/e2e/specs/skill-lifecycle.spec.ts" "skill-lifecycle"
-run "test/e2e/specs/skill-multi-round.spec.ts" "skill-multi-round"
-run "test/e2e/specs/skill-oauth.spec.ts" "skill-oauth"
-run "test/e2e/specs/skill-socket-reconnect.spec.ts" "skill-socket-reconnect"
+if should_run_suite "skills"; then
+ echo ""
+ echo "## Running suite: skills"
+ run "test/e2e/specs/skills-registry.spec.ts" "skills-registry" "skills"
+ run "test/e2e/specs/skill-execution-flow.spec.ts" "skill-execution" "skills"
+ run "test/e2e/specs/skill-lifecycle.spec.ts" "skill-lifecycle" "skills"
+ run "test/e2e/specs/skill-multi-round.spec.ts" "skill-multi-round" "skills"
+ run "test/e2e/specs/skill-oauth.spec.ts" "skill-oauth" "skills"
+ run "test/e2e/specs/skill-socket-reconnect.spec.ts" "skill-socket-reconnect" "skills"
+ _mini_summary "skills"
+fi
# ---------------------------------------------------------------------------
# Notifications, memory, cron
# ---------------------------------------------------------------------------
-run "test/e2e/specs/notifications.spec.ts" "notifications"
-run "test/e2e/specs/memory-roundtrip.spec.ts" "memory-roundtrip"
-run "test/e2e/specs/cron-jobs-flow.spec.ts" "cron-jobs"
-run "test/e2e/specs/autocomplete-flow.spec.ts" "autocomplete"
+if should_run_suite "notifications"; then
+ echo ""
+ echo "## Running suite: notifications"
+ run "test/e2e/specs/notifications.spec.ts" "notifications" "notifications"
+ run "test/e2e/specs/memory-roundtrip.spec.ts" "memory-roundtrip" "notifications"
+ run "test/e2e/specs/cron-jobs-flow.spec.ts" "cron-jobs" "notifications"
+ run "test/e2e/specs/autocomplete-flow.spec.ts" "autocomplete" "notifications"
+ _mini_summary "notifications"
+fi
# ---------------------------------------------------------------------------
# Webhooks & tools
# ---------------------------------------------------------------------------
-run "test/e2e/specs/webhooks-ingress-flow.spec.ts" "webhooks-ingress"
-run "test/e2e/specs/webhooks-tunnel-flow.spec.ts" "webhooks-tunnel"
-run "test/e2e/specs/tool-browser-flow.spec.ts" "tool-browser"
-run "test/e2e/specs/tool-filesystem-flow.spec.ts" "tool-filesystem"
-run "test/e2e/specs/tool-shell-git-flow.spec.ts" "tool-shell-git"
+if should_run_suite "webhooks"; then
+ echo ""
+ echo "## Running suite: webhooks"
+ run "test/e2e/specs/webhooks-ingress-flow.spec.ts" "webhooks-ingress" "webhooks"
+ run "test/e2e/specs/webhooks-tunnel-flow.spec.ts" "webhooks-tunnel" "webhooks"
+ run "test/e2e/specs/tool-browser-flow.spec.ts" "tool-browser" "webhooks"
+ run "test/e2e/specs/tool-filesystem-flow.spec.ts" "tool-filesystem" "webhooks"
+ run "test/e2e/specs/tool-shell-git-flow.spec.ts" "tool-shell-git" "webhooks"
+ _mini_summary "webhooks"
+fi
# ---------------------------------------------------------------------------
# Provider flows
# ---------------------------------------------------------------------------
-run "test/e2e/specs/telegram-flow.spec.ts" "telegram"
-run "test/e2e/specs/gmail-flow.spec.ts" "gmail"
-run "test/e2e/specs/slack-flow.spec.ts" "slack"
-run "test/e2e/specs/whatsapp-flow.spec.ts" "whatsapp"
-run "test/e2e/specs/conversations-web-channel-flow.spec.ts" "conversations"
-run "test/e2e/specs/composio-triggers-flow.spec.ts" "composio-triggers"
+if should_run_suite "providers"; then
+ echo ""
+ echo "## Running suite: providers"
+ run "test/e2e/specs/telegram-flow.spec.ts" "telegram" "providers"
+ run "test/e2e/specs/gmail-flow.spec.ts" "gmail" "providers"
+ run "test/e2e/specs/accounts-provider-modal.spec.ts" "accounts-providers" "providers"
+ run "test/e2e/specs/slack-flow.spec.ts" "slack" "providers"
+ run "test/e2e/specs/whatsapp-flow.spec.ts" "whatsapp" "providers"
+ # notion-flow.spec.ts was removed; skip to avoid "spec not found" failure.
+ # run "test/e2e/specs/notion-flow.spec.ts" "notion" "providers"
+ run "test/e2e/specs/conversations-web-channel-flow.spec.ts" "conversations" "providers"
+ run "test/e2e/specs/composio-triggers-flow.spec.ts" "composio-triggers" "providers"
+ _mini_summary "providers"
+fi
# ---------------------------------------------------------------------------
# Payments & rewards
# ---------------------------------------------------------------------------
-run "test/e2e/specs/card-payment-flow.spec.ts" "card-payment"
-run "test/e2e/specs/crypto-payment-flow.spec.ts" "crypto-payment"
-run "test/e2e/specs/rewards-unlock-flow.spec.ts" "rewards-unlock"
-run "test/e2e/specs/rewards-progression-persistence.spec.ts" "rewards-progression"
+if should_run_suite "payments"; then
+ echo ""
+ echo "## Running suite: payments"
+ run "test/e2e/specs/card-payment-flow.spec.ts" "card-payment" "payments"
+ run "test/e2e/specs/crypto-payment-flow.spec.ts" "crypto-payment" "payments"
+ run "test/e2e/specs/rewards-unlock-flow.spec.ts" "rewards-unlock" "payments"
+ run "test/e2e/specs/rewards-progression-persistence.spec.ts" "rewards-progression" "payments"
+ _mini_summary "payments"
+fi
# ---------------------------------------------------------------------------
# Settings panels
# ---------------------------------------------------------------------------
-run "test/e2e/specs/settings-channels-permissions.spec.ts" "settings-channels"
-run "test/e2e/specs/settings-data-management.spec.ts" "settings-data"
-run "test/e2e/specs/settings-dev-options.spec.ts" "settings-dev"
-run "test/e2e/specs/settings-ai-skills.spec.ts" "settings-ai-skills"
-run "test/e2e/specs/settings-account-preferences.spec.ts" "settings-account"
-run "test/e2e/specs/settings-advanced-config.spec.ts" "settings-advanced"
-run "test/e2e/specs/settings-feature-preferences.spec.ts" "settings-features"
+if should_run_suite "settings"; then
+ echo ""
+ echo "## Running suite: settings"
+ run "test/e2e/specs/settings-channels-permissions.spec.ts" "settings-channels" "settings"
+ run "test/e2e/specs/settings-data-management.spec.ts" "settings-data" "settings"
+ run "test/e2e/specs/settings-dev-options.spec.ts" "settings-dev" "settings"
+ run "test/e2e/specs/settings-ai-skills.spec.ts" "settings-ai-skills" "settings"
+ run "test/e2e/specs/settings-account-preferences.spec.ts" "settings-account" "settings"
+ run "test/e2e/specs/settings-advanced-config.spec.ts" "settings-advanced" "settings"
+ run "test/e2e/specs/settings-feature-preferences.spec.ts" "settings-features" "settings"
+ _mini_summary "settings"
+fi
# ---------------------------------------------------------------------------
-# AI, voice & screen
+# System / AI / voice / screen / Tauri
+# linux-cef-deb-runtime.spec.ts is Linux-only (tests /usr/bin path resolution
+# for .deb package installs) — skipped on macOS/Windows.
# ---------------------------------------------------------------------------
-run "test/e2e/specs/local-model-runtime.spec.ts" "local-model"
-run "test/e2e/specs/voice-mode.spec.ts" "voice-mode"
-run "test/e2e/specs/audio-toolkit-flow.spec.ts" "audio-toolkit"
+if should_run_suite "system"; then
+ echo ""
+ echo "## Running suite: system"
+ run "test/e2e/specs/local-model-runtime.spec.ts" "local-model" "system"
+ run "test/e2e/specs/voice-mode.spec.ts" "voice-mode" "system"
+ run "test/e2e/specs/screen-intelligence.spec.ts" "screen-intelligence" "system"
+ run "test/e2e/specs/audio-toolkit-flow.spec.ts" "audio-toolkit" "system"
+ run "test/e2e/specs/tauri-commands.spec.ts" "tauri-commands" "system"
+ # service-connectivity-flow tests the old sidecar service model removed in
+ # PR #1061 (core is now in-process). Skip by not setting OPENHUMAN_SERVICE_MOCK=1.
+ run "test/e2e/specs/service-connectivity-flow.spec.ts" "service-connectivity" "system"
+ if [[ "$(uname -s)" == "Linux" ]]; then
+ run "test/e2e/specs/linux-cef-deb-runtime.spec.ts" "linux-cef-deb-runtime" "system"
+ fi
+ _mini_summary "system"
+fi
# ---------------------------------------------------------------------------
-# System / Tauri
+# User journeys
# ---------------------------------------------------------------------------
-run "test/e2e/specs/tauri-commands.spec.ts" "tauri-commands"
-OPENHUMAN_SERVICE_MOCK=1 \
- run "test/e2e/specs/service-connectivity-flow.spec.ts" "service-connectivity"
-
-# linux-cef-deb-runtime.spec.ts is Linux-only (tests /usr/bin path resolution
-# for .deb package installs) — skipped on macOS/Windows.
-if [[ "$(uname -s)" == "Linux" ]]; then
- run "test/e2e/specs/linux-cef-deb-runtime.spec.ts" "linux-cef-deb-runtime"
+if should_run_suite "journeys"; then
+ echo ""
+ echo "## Running suite: journeys"
+ run "test/e2e/specs/user-journey-full-task.spec.ts" "journey-full-task" "journeys"
+ run "test/e2e/specs/user-journey-settings-round-trip.spec.ts" "journey-settings" "journeys"
+ run "test/e2e/specs/chat-conversation-history.spec.ts" "chat-history" "journeys"
+ _mini_summary "journeys"
fi
diff --git a/app/scripts/e2e-run-session.sh b/app/scripts/e2e-run-session.sh
index 195d6d6ff2..e463da3d4a 100755
--- a/app/scripts/e2e-run-session.sh
+++ b/app/scripts/e2e-run-session.sh
@@ -199,6 +199,11 @@ fi
cat > "$E2E_CONFIG_FILE" << TOMLEOF
api_url = "http://127.0.0.1:${E2E_MOCK_PORT}"
primary_cloud = "p_e2e_mock"
+default_model = "e2e-mock-model"
+chat_provider = "e2e:e2e-mock-model"
+reasoning_provider = "e2e:e2e-mock-model"
+agentic_provider = "e2e:e2e-mock-model"
+coding_provider = "e2e:e2e-mock-model"
[[cloud_providers]]
id = "p_e2e_mock"
@@ -206,6 +211,7 @@ slug = "e2e"
label = "E2E Mock"
endpoint = "http://127.0.0.1:${E2E_MOCK_PORT}/openai/v1"
auth_style = "none"
+default_model = "e2e-mock-model"
TOMLEOF
echo "[runner] Wrote E2E config.toml routing inference to mock at http://127.0.0.1:${E2E_MOCK_PORT}"
diff --git a/app/src/components/accounts/AddAccountModal.tsx b/app/src/components/accounts/AddAccountModal.tsx
index 99b7d79734..96596d56ef 100644
--- a/app/src/components/accounts/AddAccountModal.tsx
+++ b/app/src/components/accounts/AddAccountModal.tsx
@@ -33,15 +33,19 @@ const AddAccountModal = ({ open, onClose, onPick, connectedProviders }: AddAccou
return (
e.stopPropagation()}>
-
+
{t('accounts.addModal.title')}