import { test as base, chromium, BrowserContext, Page } from "@playwright/test"; import * as path from "path"; /** * Custom test fixture que usa un persistent browser context compartido. * * A diferencia de storageState (que solo guarda cookies + localStorage), * un persistent context preserva IndexedDB — donde Element Web guarda * las crypto keys de E2EE. Sin esto, cada test ve "Missing session data". * * El contexto es worker-scoped: se crea una vez y se reutiliza en todos * los tests del worker. Esto evita el dialogo "Element is open in another * window" que aparece cuando se abre/cierra el contexto repetidamente. */ const USER_DATA_DIR = path.resolve(__dirname, "..", ".auth", "chrome-profile"); export const test = base.extend< { page: Page }, { persistentContext: BrowserContext } >({ // Worker-scoped: un solo persistent context para todos los tests persistentContext: [ async ({}, use) => { const context = await chromium.launchPersistentContext(USER_DATA_DIR, { headless: true, baseURL: process.env.ELEMENT_URL || "http://localhost:8080", viewport: { width: 1280, height: 720 }, }); await use(context); await context.close(); }, { scope: "worker" }, ], // Cada test obtiene una pagina del contexto compartido page: async ({ persistentContext }, use) => { // Cerrar paginas sobrantes de tests anteriores for (const p of persistentContext.pages()) { await p.close(); } const page = await persistentContext.newPage(); await use(page); // Cerrar la pagina al finalizar el test await page.close(); }, }); /** * Maneja dialogos de Element que bloquean la carga: * - "Element is open in another window" → click Continue * - Spinner de carga → esperar * - "Missing session data" → error informativo * * Llamar despues de page.goto("/") */ export async function handleElementDialogs(page: Page) { // 1. "Element is open in another window" — click Continue const continueBtn = page.getByRole("button", { name: "Continue" }); const hasContinue = await continueBtn .waitFor({ state: "visible", timeout: 5_000 }) .then(() => true) .catch(() => false); if (hasContinue) { console.log("[element] 'Element is open in another window' — clicking Continue"); await continueBtn.click(); } // 2. "Missing session data" — fatal const missingData = page.locator('text="Missing session data"'); const hasMissing = await missingData .waitFor({ state: "visible", timeout: 3_000 }) .then(() => true) .catch(() => false); if (hasMissing) { throw new Error( "Missing session data: crypto keys perdidas. " + "Borrar .auth/ y re-ejecutar: rm -rf e2e/.auth && ./dev-scripts/e2e/run.sh" ); } // 3. Esperar a que rooms sidebar aparezca (sesion cargada) console.log("[element] Esperando rooms sidebar..."); const roomsTree = page.locator('[role="tree"][aria-label="Rooms"]'); await roomsTree.waitFor({ state: "visible", timeout: 30_000 }); console.log("[element] Rooms sidebar visible"); } export { expect } from "@playwright/test";