import { chromium } from "@playwright/test"; import * as path from "path"; import * as fs from "fs"; import * as dotenv from "dotenv"; import { loginToElement } from "./fixtures/element-auth"; dotenv.config({ path: path.resolve(__dirname, ".env") }); const USER_DATA_DIR = path.resolve(__dirname, ".auth", "chrome-profile"); const MARKER_PATH = path.resolve(__dirname, ".auth", "login-done.marker"); const SCREENSHOTS_DIR = path.resolve(__dirname, "test-results", "global-setup"); /** * Global setup: ejecuta login una vez usando persistent context. * * A diferencia de storageState, el persistent context preserva IndexedDB * (crypto keys de E2EE). Los tests usan el mismo userDataDir via el * custom fixture persistent-context.ts. * * Si el marker file existe y no esta expirado, asumimos que la sesion * sigue activa y saltamos el login. */ async function globalSetup() { const elementURL = process.env.ELEMENT_URL || "http://localhost:8080"; const user = process.env.MATRIX_USER; const password = process.env.MATRIX_PASSWORD; const recoveryKey = process.env.MATRIX_RECOVERY_KEY; if (!user || !password || !recoveryKey) { throw new Error( "Faltan variables de entorno: MATRIX_USER, MATRIX_PASSWORD, MATRIX_RECOVERY_KEY" ); } // Reutilizar sesion cacheada si el marker existe y tiene menos de 12 horas if (isMarkerFresh(MARKER_PATH, 12 * 60 * 60 * 1000)) { console.log("[global-setup] Reutilizando sesion de persistent context"); return; } console.log("[global-setup] Ejecutando login en Element Web con persistent context..."); console.log(`[global-setup] URL: ${elementURL}`); console.log(`[global-setup] User: ${user}`); console.log(`[global-setup] UserDataDir: ${USER_DATA_DIR}`); // Limpiar perfil anterior para login fresco if (fs.existsSync(USER_DATA_DIR)) { fs.rmSync(USER_DATA_DIR, { recursive: true }); console.log("[global-setup] Perfil anterior eliminado"); } // Asegurar que los directorios existen fs.mkdirSync(USER_DATA_DIR, { recursive: true }); fs.mkdirSync(SCREENSHOTS_DIR, { recursive: true }); // Usar persistent context — preserva IndexedDB (crypto keys E2EE) const context = await chromium.launchPersistentContext(USER_DATA_DIR, { headless: true, viewport: { width: 1280, height: 720 }, }); const page = context.pages()[0] || (await context.newPage()); // Capturar logs de consola del browser page.on("console", (msg) => { const type = msg.type(); if (type === "error" || type === "warning") { console.log(`[browser-${type}] ${msg.text()}`); } }); page.on("pageerror", (err) => { console.error(`[browser-error] ${err.message}`); }); try { await loginToElement(page, { url: elementURL, user, password, recoveryKey, screenshotsDir: SCREENSHOTS_DIR, }); // Crear marker de sesion exitosa fs.writeFileSync(MARKER_PATH, new Date().toISOString()); console.log("[global-setup] Login completado, marker creado"); await page.screenshot({ path: path.join(SCREENSHOTS_DIR, "05-login-complete.png"), fullPage: true, }); } catch (err) { console.error("[global-setup] ERROR durante login:", err); await page.screenshot({ path: path.join(SCREENSHOTS_DIR, "ERROR-login-failed.png"), fullPage: true, }); const html = await page.content(); fs.writeFileSync( path.join(SCREENSHOTS_DIR, "ERROR-page-content.html"), html ); throw err; } finally { await context.close(); } } /** Verifica si el marker file existe y tiene menos de maxAge ms. */ function isMarkerFresh(filePath: string, maxAge: number): boolean { try { const stat = fs.statSync(filePath); return Date.now() - stat.mtimeMs < maxAge; } catch { return false; } } export default globalSetup;