- Updated Jenkinsfile to include Playwright's trace option for better debugging on test failures. - Added archiving of test results, including trace files, error context, and screenshots for failed tests. - Enhanced error handling in `auth.setup.ts` to log diagnostic information when authentication fails, improving visibility into issues. These changes aim to improve the reliability and debuggability of E2E tests.
83 lines
4.5 KiB
TypeScript
83 lines
4.5 KiB
TypeScript
/**
|
|
* Setup de autenticación para tests E2E de Playwright.
|
|
*
|
|
* El flujo de login de Anexo76 pasa por Workspace (workspace.aduanasoft.com):
|
|
* 1. Navegar a /login → 303 redirect al formulario de Workspace
|
|
* 2. Llenar el form de Workspace con el usuario de prueba (creado en globalSetup)
|
|
* 3. Workspace/Keycloak redirige de vuelta a /auth/callback → /dashboard
|
|
* 4. Guardar storageState para todos los tests dependientes
|
|
*
|
|
* El usuario de prueba es creado y eliminado por globalSetup / globalTeardown.
|
|
* Patrón idéntico al de aduanasoft-hub.
|
|
*/
|
|
import { test as setup, expect } from '@playwright/test'
|
|
import { mkdirSync } from 'node:fs'
|
|
import path from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
const authFile = path.join(__dirname, '.auth/user.json')
|
|
|
|
setup('autenticacion', async ({ page }) => {
|
|
const username = process.env.E2E_TEST_USER ?? ''
|
|
const password = process.env.E2E_TEST_PASSWORD ?? ''
|
|
|
|
if (!username) throw new Error('E2E_TEST_USER no está configurado')
|
|
if (!password) throw new Error('E2E_TEST_PASSWORD no está configurado')
|
|
|
|
// ── 1. Navegar a /login — Workspace intercepta y muestra su formulario ──
|
|
await page.goto('/login')
|
|
|
|
// ── 2. Llenar el formulario de Workspace (mismos selectores que hub/login.spec.ts) ──
|
|
await page.getByRole('textbox', { name: /usuario o email/i }).fill(username)
|
|
await page.getByLabel(/contraseña/i).fill(password)
|
|
await page.getByRole('button', { name: /continuar/i }).click()
|
|
|
|
// ── 3. Si Workspace muestra el app-launcher (usuario con múltiples apps),
|
|
// elegir anexo76-dev — la app del launcher cuya URL apunta al frontend bajo prueba.
|
|
// Cuando hay match directo de dominio, Workspace salta el launcher y este paso no ejecuta.
|
|
try {
|
|
await page.waitForURL(/\/(app-launcher|dashboard|auth\/callback)/, { timeout: 30_000 })
|
|
} catch (e) {
|
|
// Si seguimos en /login de Workspace, las credenciales fueron rechazadas o el form no aceptó.
|
|
// Imprimir diagnóstico antes de fallar: URL actual + posibles mensajes de error visibles.
|
|
const currentUrl = page.url()
|
|
const errorMessages = await page.locator('.text-destructive, [role="alert"], .error, .invalid-feedback')
|
|
.allTextContents()
|
|
.catch(() => [] as string[])
|
|
const visibleText = await page.locator('body').innerText().catch(() => '')
|
|
console.log(`[auth.setup][DIAG] URL: ${currentUrl}`)
|
|
console.log(`[auth.setup][DIAG] Mensajes de error: ${JSON.stringify(errorMessages)}`)
|
|
console.log(`[auth.setup][DIAG] Body (primeros 800 chars): ${visibleText.slice(0, 800)}`)
|
|
throw e
|
|
}
|
|
if (/\/app-launcher/.test(page.url())) {
|
|
await page.getByRole('button', { name: /^anexo76-dev/i }).click()
|
|
}
|
|
|
|
// ── 4. Esperar redirect a /dashboard ──────────────────────────────────────
|
|
await expect(page).toHaveURL(/\/dashboard/, { timeout: 30_000 })
|
|
|
|
// ── 5. ExchangeRateGuard abre un dialog modal en cada carga del dashboard
|
|
// si no existe un tipo de cambio para hoy. El guard espera a que
|
|
// companyStore.activeCompany?.id esté disponible (API async) antes de
|
|
// consultar la DB y abrir el dialog. Esperar networkidle para que ese
|
|
// flujo se complete antes de buscar el dialog.
|
|
await page.waitForLoadState('networkidle', { timeout: 30_000 }).catch(() => {})
|
|
const tcDialog = page.getByRole('dialog', { name: /Nuevo Tipo de Cambio/i })
|
|
if (await tcDialog.isVisible({ timeout: 10_000 }).catch(() => false)) {
|
|
const valueInput = tcDialog.getByRole('spinbutton', { name: /Tipo de Cambio/i })
|
|
await tcDialog.getByRole('button', { name: /Consultar DOF/i }).click()
|
|
// El backend consulta el DOF y llena el campo. Esperar a que tenga un valor.
|
|
await expect(valueInput).not.toHaveValue('', { timeout: 15_000 })
|
|
// "Ok" abre un AlertDialog de confirmación; el TC se crea hasta "Confirmar".
|
|
await tcDialog.getByRole('button', { name: /^Ok$/ }).click()
|
|
await page.getByRole('button', { name: /^Confirmar$/ }).click()
|
|
await tcDialog.waitFor({ state: 'hidden', timeout: 10_000 })
|
|
}
|
|
|
|
// ── 6. Guardar estado para los tests dependientes ─────────────────────────
|
|
mkdirSync(path.dirname(authFile), { recursive: true })
|
|
await page.context().storageState({ path: authFile })
|
|
})
|