#!/usr/bin/env bash # LDD browser E2E: полный стек + Playwright (Chromium/Firefox/WebKit) на одноразовой БД. set -euo pipefail cd "$(dirname "$0")/.." DB="lms_e2e_$(date -u +%H%M%S)_$$" POSTGRES_WAS_RUNNING=0 STACK_STARTED=0 E2E_STATUS=1 log() { echo ">> $*"; } playwright_version() { (cd e2e && node -p "require('@playwright/test/package.json').version" 2>/dev/null || true) } host_playwright_usable() { # WebKit на Fedora часто не запускается из-за Ubuntu-специфичных libicu/libjpeg; # проверяем все три браузера, чтобы fallback сработал и при нехватке lib для Firefox. (cd e2e && node -e ' const { chromium, firefox, webkit } = require("playwright"); (async () => { for (const type of [chromium, firefox, webkit]) { const browser = await type.launch(); await browser.close(); } })().catch(() => process.exit(1)); ') >/dev/null 2>&1 } ensure_playwright_browsers() { local browser detected missing=() # Проверяем точные пути браузеров текущей версии Playwright, а не кэш целиком. if ! detected=$(cd e2e && node -e ' const fs = require("fs"); const { chromium, firefox, webkit } = require("playwright"); for (const [name, type] of [["chromium", chromium], ["firefox", firefox], ["webkit", webkit]]) { if (!fs.existsSync(type.executablePath())) console.log(name); } ' 2>/dev/null); then detected=$'chromium\nfirefox\nwebkit' fi while IFS= read -r browser; do [[ -n "$browser" ]] && missing+=("$browser") done <<<"$detected" if ((${#missing[@]} == 0)); then log "Браузеры Playwright уже установлены (Chromium/Firefox/WebKit)." return fi log "Устанавливаю браузеры Playwright: ${missing[*]}; системные зависимости ставит CI." (cd e2e && npx playwright install --no-progress "${missing[@]}") } cleanup() { local code=$? if ((STACK_STARTED)); then log "Останавливаю тестовый стек..." ./run.sh stop >/dev/null 2>&1 || true fi docker exec -i lms-postgres psql -U lms -d postgres -v ON_ERROR_STOP=1 \ -c "DROP DATABASE IF EXISTS $DB;" >/dev/null 2>&1 || true if ((POSTGRES_WAS_RUNNING == 0)); then docker compose stop postgres >/dev/null 2>&1 || true fi exit $code } trap cleanup EXIT INT TERM for port in 8080 8081 8082 8083 8090; do if ss -ltn 2>/dev/null | grep -q ":$port "; then echo "Порт $port занят. Остановите текущий стек (./run.sh stop) и повторите." >&2 exit 1 fi done command -v docker >/dev/null 2>&1 || { echo "Нужен docker" >&2; exit 1; } command -v npx >/dev/null 2>&1 || { echo "Нужен Node.js/npx" >&2; exit 1; } JAVA_BIN="${JAVA_BIN:-$(command -v java || true)}" [[ -x "$JAVA_BIN" ]] || { echo "Не найден Java 21: $JAVA_BIN" >&2; exit 1; } [[ -d e2e/node_modules ]] || { echo "Выполните: cd e2e && npm install" >&2; exit 1; } if docker compose ps --status running --services 2>/dev/null | grep -qx postgres; then POSTGRES_WAS_RUNNING=1 fi log "Запускаю PostgreSQL..." docker compose up -d postgres >/dev/null for _ in $(seq 1 40); do docker exec lms-postgres pg_isready -U lms -d postgres >/dev/null 2>&1 && break sleep 1 done docker exec lms-postgres pg_isready -U lms -d postgres >/dev/null docker exec -i lms-postgres psql -U lms -d postgres -v ON_ERROR_STOP=1 \ -c "CREATE DATABASE $DB;" >/dev/null log "Собираю bootJar..." ./gradlew bootJar --console=plain >/tmp/lms-ldd-e2e-bootjar.log log "Запускаю стек..." export LMS_DB_URL="jdbc:postgresql://localhost:5432/$DB" export LMS_DB_NAME="$DB" export LMS_DB_USER="lms" export LMS_DB_PASSWORD="lms" export LMS_INTERNAL_TOKEN="ldd-internal-token" export LMS_SESSION_COOKIE_SECURE="false" export JAVA_BIN STACK_STARTED=1 ./run.sh start mkdir -p build/evidence ensure_playwright_browsers set +e if host_playwright_usable; then log "Запускаю Playwright на хосте (Chromium/Firefox/WebKit)..." ( cd e2e LDD_WEB_URL="http://localhost:8090" \ LDD_API_URL="http://localhost:8080" \ npx playwright test --config=playwright.config.ts ) E2E_STATUS=$? else version=$(playwright_version) if [[ -z "$version" ]]; then echo "WebKit не запускается на хосте, а версию Playwright определить не удалось." >&2 E2E_STATUS=1 else log "WebKit не запускается на хосте; запускаю Chromium/Firefox/WebKit в Docker-образе Playwright ${version}." docker run --rm --network host \ -v "$PWD:/work" \ -w /work/e2e \ -e LDD_WEB_URL="http://localhost:8090" \ -e LDD_API_URL="http://localhost:8080" \ "mcr.microsoft.com/playwright:v${version}-noble" \ npx playwright test --config=playwright.config.ts E2E_STATUS=$? fi fi set -e python3 - "$E2E_STATUS" <<'PY' import json import os import sys status_code = int(sys.argv[1]) playwright_path = "build/evidence/playwright.json" detail = "Playwright exit=" + str(status_code) if os.path.isfile(playwright_path): try: root = json.load(open(playwright_path, encoding="utf-8")) stats = root.get("stats", {}) detail += ( f"; expected={stats.get('expected', 0)}" f", unexpected={stats.get('unexpected', 0)}" f", flaky={stats.get('flaky', 0)}" f", skipped={stats.get('skipped', 0)}" ) except Exception as exc: detail += "; не удалось разобрать playwright.json: " + str(exc) status = "passed" if status_code == 0 else "failed" with open("build/evidence/browser.json", "w", encoding="utf-8") as fh: now = __import__("datetime").datetime.now(__import__("datetime").UTC).isoformat() json.dump( {"generatedAt": now, "checks": [{"id": "browser.e2e", "status": status, "detail": detail}]}, fh, ensure_ascii=False, indent=2, ) print(("PASS " if status == "passed" else "FAIL ") + "browser.e2e: " + detail) sys.exit(0 if status == "passed" else 1) PY