From b8f46d09ae002d428d531b40e6dec309809fcf87 Mon Sep 17 00:00:00 2001 From: AlexeerCT Date: Fri, 24 Apr 2026 11:18:57 -0500 Subject: [PATCH] Enhance Jenkinsfile for frontend testing and update Playwright configuration - Added a new 'E2E (docker compose + Playwright)' stage in the Jenkinsfile to run end-to-end tests using Docker Compose and Playwright. - Updated the 'Test frontend' stage to utilize the Playwright test image and improved error handling for missing files. - Modified Playwright configuration to use an environment variable for the base URL, enhancing flexibility in CI environments. - Expanded TypeScript configuration to include additional test and configuration files for better type checking. --- Jenkinsfile | 64 +++++++++++++++++++++++++++---- frontend/playwright.config.ts | 6 +-- frontend/tsconfig.json | 20 +++++++++- frontend/vite.config.ts | 11 +++--- scripts/wait-for-jenkins-stack.sh | 31 +++++++++++++++ 5 files changed, 116 insertions(+), 16 deletions(-) create mode 100755 scripts/wait-for-jenkins-stack.sh diff --git a/Jenkinsfile b/Jenkinsfile index af2c0f93..59bb2f57 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -13,6 +13,8 @@ pipeline { environment { REGISTRY = 'dev.aduanasoft.com' IMAGE_NAMESPACE = 'anexo76' + // Debe coincidir con @playwright/test del frontend (ver frontend/pnpm-lock) + PLAYWRIGHT_TEST_IMAGE = 'mcr.microsoft.com/playwright:v1.56.1-noble' } stages { @@ -131,15 +133,17 @@ pipeline { } } - // Test frontend: OBLIGATORIO usar el repo clonado (WORKSPACE), no la imagen del frontend en Harbor. - // Svelte se compila en el Dockerfile: la imagen de producción solo trae build/ + deps de runtime; no hay fuentes - // para vitest, ni paraglide desde inlang, ni pnpm devDependencies. Imagen usada aquí: solo node:22-bookworm. - // Mismo criterio que Test backend: docker cp (no -v) si Jenkins vive en Docker. Vitest: sin @vitest/browser en CI. - stage('Test frontend') { + // Código = WORKSPACE (docker cp), no la imagen Harbor. Imagen base: mcr.microsoft.com/playwright (Chromium p/ Vitest y E2E). + stage('Test frontend (Vitest + Svelte browser)') { steps { sh ''' set -euxo pipefail - echo "== Test frontend (vitest) ==" + echo "== Test frontend: vitest (Node + @vitest/browser) con $PLAYWRIGHT_TEST_IMAGE ==" + if [ ! -f "$WORKSPACE/frontend/package.json" ]; then + echo "ERROR: $WORKSPACE/frontend/package.json no existe" + ls -la "$WORKSPACE" 2>&1 | head -40 + exit 1 + fi FE_CONTAINER="anexo76-test-fe-${BUILD_NUMBER}" cleanup() { @@ -148,7 +152,7 @@ pipeline { trap cleanup EXIT docker rm -f "$FE_CONTAINER" >/dev/null 2>&1 || true - docker run -d --name "$FE_CONTAINER" node:22-bookworm sleep infinity + docker run -d --name "$FE_CONTAINER" "$PLAYWRIGHT_TEST_IMAGE" sleep infinity docker exec "$FE_CONTAINER" mkdir -p /workspace docker cp "$WORKSPACE/." "$FE_CONTAINER:/workspace" @@ -156,6 +160,7 @@ pipeline { -e CI=true \ -e NODE_ENV=test \ -e "JENKINS_URL=${JENKINS_URL}" \ + -e JENKINS_VITEST_FULL=1 \ -w /workspace/frontend \ "$FE_CONTAINER" \ bash -lc ' @@ -172,6 +177,51 @@ pipeline { } } + stage('E2E (docker compose + Playwright)') { + options { + timeout(time: 90, unit: 'MINUTES') + } + steps { + sh ''' + set -euxo pipefail + echo "== E2E: levanta docker-compose.yml (host) y pnpm test:e2e ==" + if [ ! -f "$WORKSPACE/frontend/package.json" ] || [ ! -f "$WORKSPACE/docker-compose.yml" ]; then + echo "ERROR: faltan archivos de repo (frontend o compose)" + exit 1 + fi + export E2E_COMPOSE_PROJECT="anexo76-e2e-${BUILD_NUMBER}" + export COMPOSE_PROJECT_NAME="${E2E_COMPOSE_PROJECT}" + cd "$WORKSPACE" + + compose_down() { + export COMPOSE_PROJECT_NAME="${E2E_COMPOSE_PROJECT}" + docker compose -f "$WORKSPACE/docker-compose.yml" down --remove-orphans 2>/dev/null || true + } + trap compose_down EXIT + compose_down + # Stack completo (API, Keycloak, Vite) — puede tardar varios minutos la 1.ª vez + docker compose -f "$WORKSPACE/docker-compose.yml" up -d --build + bash "$WORKSPACE/scripts/wait-for-jenkins-stack.sh" + # --network host: en Linux el contenedor de tests ve localhost:5173/8000 publicados por compose + docker run --rm --network host \ + -e CI=true \ + -e "JENKINS_URL=${JENKINS_URL}" \ + -e PLAYWRIGHT_TEST_BASE_URL=http://127.0.0.1:5173 \ + -v "$WORKSPACE:/workspace" \ + -w /workspace/frontend \ + "$PLAYWRIGHT_TEST_IMAGE" \ + bash -lc ' + set -euxo pipefail + test -f package.json + corepack enable + pnpm install --frozen-lockfile + pnpm run i18n:compile + pnpm run test:e2e + ' + ''' + } + } + stage('Generate version') { steps { script { diff --git a/frontend/playwright.config.ts b/frontend/playwright.config.ts index c84ea18b..065611dd 100644 --- a/frontend/playwright.config.ts +++ b/frontend/playwright.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from '@playwright/test'; -import { fileURLToPath } from 'url'; -import path from 'path'; +import { fileURLToPath } from 'node:url'; +import path from 'node:path'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); @@ -13,7 +13,7 @@ export default defineConfig({ forbidOnly: inCI, timeout: 60000, use: { - baseURL: 'http://localhost:5173', + baseURL: process.env.PLAYWRIGHT_TEST_BASE_URL || 'http://localhost:5173', // Sin display en agentes de integración: obligatorio headless headless: inCI ? true : false }, diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 58b7d417..fb43d32c 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -11,7 +11,25 @@ "strict": true, "moduleResolution": "bundler", "allowArbitraryExtensions": true - } + }, + "include": [ + "playwright.config.ts", + "e2e/**/*.ts", + "e2e/**/*.js", + "vitest-setup-client.ts", + "eslint.config.js", + "./.svelte-kit/ambient.d.ts", + "./.svelte-kit/non-ambient.d.ts", + "./.svelte-kit/types/**/$types.d.ts", + "./src/**/*.js", + "./src/**/*.ts", + "./src/**/*.svelte", + "./tests/**/*.js", + "./tests/**/*.ts", + "./tests/**/*.svelte", + "./vite.config.js", + "./vite.config.ts" + ] // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files // diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index cb3bc81a..5356c0b1 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -3,11 +3,12 @@ import tailwindcss from '@tailwindcss/vite'; import { defineConfig } from 'vitest/config'; import { sveltekit } from '@sveltejs/kit/vite'; -/* Jenkins / CI: sin navegador ni pnpm exec playwright install (Vitest 3+ intenta aun levantar el project "client" si queda en la config). */ -const vitestNoBrowser = - process.env.CI === 'true' || - Boolean(process.env.JENKINS_URL) || - process.env.VITEST_NO_BROWSER === '1'; +/* En CI, por defecto solo Node (evita Chromium). Con JENKINS_VITEST_FULL=1 o VITEST_FULL=1, también @vitest/browser. */ +const inCi = process.env.CI === 'true' || Boolean(process.env.JENKINS_URL); +const vitestFull = + process.env.VITEST_FULL === '1' || process.env.JENKINS_VITEST_FULL === '1'; +const skipBrowser = process.env.VITEST_NO_BROWSER === '1'; +const vitestNoBrowser = (inCi && !vitestFull) || skipBrowser; const vitestServerProject = { extends: './vite.config.ts', diff --git a/scripts/wait-for-jenkins-stack.sh b/scripts/wait-for-jenkins-stack.sh new file mode 100755 index 00000000..9a13a5a6 --- /dev/null +++ b/scripts/wait-for-jenkins-stack.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# Espera API y Vite levantados en el host (tras docker compose), para E2E en CI. +set -euo pipefail +BACKEND_MAX_SEC="${BACKEND_MAX_SEC:-600}" +FRONTEND_MAX_SEC="${FRONTEND_MAX_SEC:-300}" +i=0 +while true; do + if curl -fsS "http://localhost:8000/api/health" >/dev/null 2>&1; then + echo "== stack: backend listo ==" + break + fi + i=$((i + 2)) + if [ "$i" -ge "$BACKEND_MAX_SEC" ]; then + echo "ERROR: timeout esperando http://localhost:8000/api/health (${BACKEND_MAX_SEC}s)" + exit 1 + fi + sleep 2 +done +i=0 +while true; do + if curl -fsS "http://localhost:5173/" >/dev/null 2>&1; then + echo "== stack: frontend listo ==" + exit 0 + fi + i=$((i + 2)) + if [ "$i" -ge "$FRONTEND_MAX_SEC" ]; then + echo "ERROR: timeout esperando http://localhost:5173/ (${FRONTEND_MAX_SEC}s)" + exit 1 + fi + sleep 2 +done