Merge branch 'development' into feature/bitacora-filtros-alineacion

This commit is contained in:
2026-04-24 16:48:08 -05:00
21 changed files with 597 additions and 307 deletions

View File

@@ -8,8 +8,6 @@ RUN apk update && apk add --no-cache ca-certificates wget && update-ca-certifica
# Copiar package files
COPY package.json pnpm-lock.yaml ./
# Instalar pnpm
RUN npm config set strict-ssl false
RUN npm install -g pnpm
@@ -17,6 +15,10 @@ RUN npm install -g pnpm
# Instalar dependencias
RUN pnpm install
# Entrypoint (espera API backend en dev; no montar desde el host)
COPY docker-entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
# Copiar código
COPY . .
@@ -26,5 +28,7 @@ COPY . .
# Exponer puerto
EXPOSE 5173
ENTRYPOINT ["/entrypoint.sh"]
# Comando por defecto (desarrollo)
CMD ["pnpm", "run", "dev", "--", "--host", "0.0.0.0"]

View File

@@ -39,6 +39,8 @@ FROM node:22-alpine AS runtime
WORKDIR /app
RUN apk add --no-cache wget
RUN npm config set strict-ssl false && \
npm install -g pnpm
@@ -46,6 +48,9 @@ RUN npm config set strict-ssl false && \
RUN addgroup -g 1001 -S nodejs
RUN adduser -S svelte -u 1001
COPY docker-entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
# Copiar solo archivos necesarios para producción y aplicar propietario en la copia
COPY --from=build --chown=svelte:nodejs /app/build ./build
COPY --from=build --chown=svelte:nodejs /app/package.json ./
@@ -65,5 +70,7 @@ ENV HOST=0.0.0.0
# pero necesitamos valores por defecto para el build
ENV INTERNAL_API_URL=http://backend:8000/api/
ENTRYPOINT ["/entrypoint.sh"]
# Ejecutar aplicación con Node.js
CMD ["pnpm", "start"]

View File

@@ -0,0 +1,44 @@
#!/bin/sh
set -e
# Arranque del contenedor: opcionalmente espera al health del backend, luego ejecuta CMD (pnpm dev / start, …)
wait_for_backend() {
local url=$1
local max_attempts=30
local attempt=1
echo "Esperando a que el backend esté disponible en ${url}..."
while [ $attempt -le $max_attempts ]; do
# NOTA: usar la URL tal cual la pasa el caller. Antes esta función agregaba
# un "/health" extra al final (resultando en /api/health/health → 404), lo
# que provocaba que el entrypoint esperara 30 intentos en vano antes de
# arrancar Vite, sumando ~90 s muertos al arranque del frontend.
if wget -q -O /dev/null "${url}" 2>/dev/null; then
echo "✓ Backend está listo"
return 0
fi
echo "Backend no está listo aún... (intento $attempt/$max_attempts)"
attempt=$((attempt + 1))
sleep 3
done
echo "⚠ WARNING: Backend no estuvo disponible, continuando de todas formas"
return 0
}
# compose usa INTERNAL_API_URL; alias opcional BACKEND_INTERNAL_URL
_api_base="${INTERNAL_API_URL:-${BACKEND_INTERNAL_URL:-http://backend:8000/api}}"
_api_base="${_api_base%/}"
wait_for_backend "${_api_base}/health"
# En desarrollo (volumen montado) reinstala deps si cambia package.json
if [ "$NODE_ENV" = "development" ]; then
echo "Instalando dependencias (development)..."
CI=true pnpm install
fi
echo "Iniciando: $*"
exec "$@"

View File

@@ -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
},

View File

@@ -1,6 +1,9 @@
import { describe, it, expect } from 'vitest'
describe('backend — health check', () => {
// Solo en entorno con DNS `backend` (p. ej. red Docker) y con backend levantado; en Jenkins/CI se omite.
const skipHttpIntegration = process.env.CI === 'true' || Boolean(process.env.JENKINS_URL)
describe.skipIf(skipHttpIntegration)('backend — health check', () => {
it('el backend esta corriendo y responde', async () => {
const response = await fetch('http://backend:8000/api/health')

View File

@@ -19,8 +19,9 @@ describe('getBackendAssetUrl', () => {
expect(getBackendAssetUrl('/api/v1/items', 'http://localhost:8000/api')).toBe('http://localhost:8000/api/v1/items')
})
it('construye URL completa para ruta normal', () => {
expect(getBackendAssetUrl('/uploads/file.png', 'http://localhost:8000/api')).toBe('http://localhost:8000/api/uploads/file.png')
})
it('construye URL completa para ruta relativa (VITE_API_URL por defecto host:8000)', () => {
// getBackendAssetUrl solo acepta path; la base sale de VITE o fallback http://localhost:8000
expect(getBackendAssetUrl('/uploads/file.png')).toBe('http://localhost:8000/uploads/file.png')
})
})

View File

@@ -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
//

View File

@@ -3,12 +3,51 @@ import tailwindcss from '@tailwindcss/vite';
import { defineConfig } from 'vitest/config';
import { sveltekit } from '@sveltejs/kit/vite';
/* 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',
test: {
name: 'server' as const,
environment: 'node' as const,
include: ['src/**/*.{test,spec}.{js,ts}'],
exclude: ['src/**/*.svelte.{test,spec}.{js,ts}'],
},
};
const vitestClientProject = {
extends: './vite.config.ts',
test: {
name: 'client' as const,
environment: 'browser' as const,
browser: {
enabled: true,
provider: 'playwright' as const,
instances: [{ browser: 'chromium' as const }],
},
include: ['src/**/*.svelte.{test,spec}.{js,ts}'],
exclude: ['src/lib/server/**'],
setupFiles: ['./vitest-setup-client.ts'],
},
};
export default defineConfig({
server: {
port: 5173, // fija el puerto
host: true, // escucha en 0.0.0.0
allowedHosts: [
'anexo76-dev.aduanasoft.com',
// Requeridos para dev local, healthcheck del contenedor y E2E (Playwright
// con --network host apunta a 127.0.0.1:5173). Al definir allowedHosts,
// Vite 5.1+ reemplaza el default ['.localhost'] y bloquea todo lo que no
// esté aquí, devolviendo 403 "Blocked request".
'127.0.0.1',
'localhost',
// 'otro-host.com' si necesitas más
],
proxy: {
@@ -28,31 +67,8 @@ export default defineConfig({
],
test: {
expect: { requireAssertions: true },
projects: [
{
extends: './vite.config.ts',
test: {
name: 'client',
environment: 'browser',
browser: {
enabled: true,
provider: 'playwright',
instances: [{ browser: 'chromium' }]
},
include: ['src/**/*.svelte.{test,spec}.{js,ts}'],
exclude: ['src/lib/server/**'],
setupFiles: ['./vitest-setup-client.ts']
}
},
{
extends: './vite.config.ts',
test: {
name: 'server',
environment: 'node',
include: ['src/**/*.{test,spec}.{js,ts}'],
exclude: ['src/**/*.svelte.{test,spec}.{js,ts}']
}
}
]
projects: vitestNoBrowser
? [vitestServerProject]
: [vitestClientProject, vitestServerProject]
}
});