#!/usr/bin/env bash set -euo pipefail ADMIN_USERNAME="${E2E_LOGIN_USERNAME:-e2e_admin}" ADMIN_PASSWORD="${E2E_LOGIN_PASSWORD:-E2EAdmin@123456}" ADMIN_EMAIL="${E2E_LOGIN_EMAIL:-e2e_admin@example.com}" BOOTSTRAP_SECRET_VALUE="${E2E_BOOTSTRAP_SECRET:-${BOOTSTRAP_SECRET:-e2e-bootstrap-secret-0123456789abcdefghijklmnopqrstuvwxyz}}" BROWSER_PORT="${E2E_CDP_PORT:-0}" BACKEND_PORT="${E2E_BACKEND_PORT:-0}" FRONTEND_PORT="${E2E_FRONTEND_PORT:-0}" SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" FRONTEND_ROOT="$(cd -- "$SCRIPT_DIR/.." && pwd)" PROJECT_ROOT="$(cd -- "$SCRIPT_DIR/../../.." && pwd)" TMP_ROOT="$(mktemp -d -t ums-playwright-e2e-XXXXXX)" DATA_ROOT="$TMP_ROOT/data" SMTP_CAPTURE_FILE="$TMP_ROOT/smtp-capture.jsonl" SERVER_BIN="$TMP_ROOT/ums-server" mkdir -p "$DATA_ROOT" backend_pid='' frontend_pid='' smtp_pid='' cleanup() { local exit_code=$? for pid in "$frontend_pid" "$backend_pid" "$smtp_pid"; do if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then kill "$pid" 2>/dev/null || true wait "$pid" 2>/dev/null || true fi done rm -rf "$TMP_ROOT" exit "$exit_code" } trap cleanup EXIT INT TERM get_free_port() { python3 - <<'PY' import socket s = socket.socket() s.bind(('127.0.0.1', 0)) print(s.getsockname()[1]) s.close() PY } wait_url_ready() { local url="$1" local label="$2" local attempts="${3:-120}" local delay="${4:-0.5}" for ((i=0; i/dev/null 2>&1; then return 0 fi sleep "$delay" done echo "$label did not become ready: $url" >&2 return 1 } SELECTED_BACKEND_PORT="$BACKEND_PORT" if [[ "$SELECTED_BACKEND_PORT" == "0" ]]; then SELECTED_BACKEND_PORT="$(get_free_port)" fi SELECTED_FRONTEND_PORT="$FRONTEND_PORT" if [[ "$SELECTED_FRONTEND_PORT" == "0" ]]; then SELECTED_FRONTEND_PORT="$(get_free_port)" fi SELECTED_SMTP_PORT="$(get_free_port)" BACKEND_BASE_URL="http://127.0.0.1:${SELECTED_BACKEND_PORT}" FRONTEND_BASE_URL="http://127.0.0.1:${SELECTED_FRONTEND_PORT}" SQLITE_PATH="$DATA_ROOT/user_management.e2e.db" cd "$PROJECT_ROOT" go build -o "$SERVER_BIN" ./cmd/server echo "playwright e2e backend: $BACKEND_BASE_URL" echo "playwright e2e frontend: $FRONTEND_BASE_URL" echo "playwright e2e smtp: 127.0.0.1:$SELECTED_SMTP_PORT" echo "playwright e2e sqlite: $SQLITE_PATH" node "$SCRIPT_DIR/mock-smtp-capture.mjs" --port "$SELECTED_SMTP_PORT" --output "$SMTP_CAPTURE_FILE" >"$TMP_ROOT/smtp.log" 2>&1 & smtp_pid=$! sleep 0.5 if ! kill -0 "$smtp_pid" 2>/dev/null; then cat "$TMP_ROOT/smtp.log" >&2 || true echo "smtp capture server failed to start" >&2 exit 1 fi ( export SERVER_PORT="$SELECTED_BACKEND_PORT" export DATABASE_DBNAME="$SQLITE_PATH" export SERVER_MODE='debug' export SERVER_FRONTEND_URL="$FRONTEND_BASE_URL" export CORS_ALLOWED_ORIGINS="$FRONTEND_BASE_URL,http://localhost:${SELECTED_FRONTEND_PORT}" export LOGGING_OUTPUT='stdout' export DISABLE_RATE_LIMIT='1' export EMAIL_HOST='127.0.0.1' export EMAIL_PORT="$SELECTED_SMTP_PORT" export EMAIL_FROM_EMAIL='noreply@test.local' export EMAIL_FROM_NAME='UMS E2E' export JWT_SECRET='e2e-test-jwt-secret-at-least-32-bytes-long-for-security' export BOOTSTRAP_SECRET="$BOOTSTRAP_SECRET_VALUE" exec "$SERVER_BIN" ) >"$TMP_ROOT/backend.log" 2>&1 & backend_pid=$! if ! wait_url_ready "$BACKEND_BASE_URL/health" 'backend'; then cat "$TMP_ROOT/backend.log" >&2 || true exit 1 fi ( cd "$FRONTEND_ROOT" export VITE_API_PROXY_TARGET="$BACKEND_BASE_URL" export VITE_API_BASE_URL='/api/v1' exec env -u NODE_ENV npm run dev -- --host 127.0.0.1 --port "$SELECTED_FRONTEND_PORT" ) >"$TMP_ROOT/frontend.log" 2>&1 & frontend_pid=$! if ! wait_url_ready "$FRONTEND_BASE_URL" 'frontend'; then cat "$TMP_ROOT/frontend.log" >&2 || true exit 1 fi cd "$FRONTEND_ROOT" export E2E_LOGIN_USERNAME="$ADMIN_USERNAME" export E2E_LOGIN_PASSWORD="$ADMIN_PASSWORD" export E2E_LOGIN_EMAIL="$ADMIN_EMAIL" export E2E_BOOTSTRAP_SECRET="$BOOTSTRAP_SECRET_VALUE" export BOOTSTRAP_SECRET="$BOOTSTRAP_SECRET_VALUE" export E2E_EXPECT_ADMIN_BOOTSTRAP='1' export E2E_EXTERNAL_WEB_SERVER='1' export E2E_MANAGED_BROWSER='1' export E2E_BASE_URL="$FRONTEND_BASE_URL" export E2E_SMTP_CAPTURE_FILE="$SMTP_CAPTURE_FILE" env -u NODE_ENV node ./scripts/run-playwright-cdp-e2e.mjs