Files
inq-roi-simulador-web/tests/e2e/setup/auth-setup.ts
2026-06-23 22:33:01 -03:00

94 lines
3.9 KiB
TypeScript

// Global setup de Playwright — Sprint 4 Etapa 8.
//
// Decisión tomada: Opción A (sesión real contra Supabase con usuario de test
// email/password, platform_role='platform_admin' en public.users). Permite que
// los specs E2E ejerciten el flujo real de auth + RLS + queries, no un mock.
//
// Usuario de test creado manualmente en Supabase Studio (no vía signup automático).
// Credenciales en .env.test (gitignored) — ver .env.example para el formato.
//
// Genera tests/e2e/setup/auth-state.json: storageState de Playwright con la
// sesión inyectada en localStorage bajo la clave sb-{project_ref}-auth-token,
// el mismo formato que usa supabase-js v2 y que AuthContext.getUserFromStorage()
// espera leer. Los specs que necesitan auth usan:
// test.use({ storageState: 'tests/e2e/setup/auth-state.json' })
import { chromium } from '@playwright/test'
import { createClient } from '@supabase/supabase-js'
import { readFileSync, existsSync } from 'fs'
import { resolve } from 'path'
import { fileURLToPath } from 'url'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const ROOT = resolve(__dirname, '../../..')
const STATE_PATH = resolve(__dirname, 'auth-state.json')
function loadEnvFile(filename: string): Record<string, string> {
const path = resolve(ROOT, filename)
if (!existsSync(path)) return {}
const out: Record<string, string> = {}
for (const line of readFileSync(path, 'utf-8').split('\n')) {
const trimmed = line.trim()
if (!trimmed || trimmed.startsWith('#')) continue
const idx = trimmed.indexOf('=')
if (idx === -1) continue
out[trimmed.slice(0, idx)] = trimmed.slice(idx + 1)
}
return out
}
async function globalSetup() {
const local = loadEnvFile('.env.local')
const test = loadEnvFile('.env.test')
const supabaseUrl = local.VITE_SUPABASE_URL
const supabaseAnonKey = local.VITE_SUPABASE_ANON_KEY
const email = test.TEST_USER_EMAIL
const password = test.TEST_USER_PASSWORD
if (!supabaseUrl || !supabaseAnonKey) {
console.warn('[auth-setup] VITE_SUPABASE_URL/ANON_KEY no encontradas en .env.local — specs con auth se saltarán storageState')
return
}
if (!email || !password) {
console.warn('[auth-setup] TEST_USER_EMAIL/PASSWORD no encontradas en .env.test — specs con auth se saltarán storageState')
return
}
const supabase = createClient(supabaseUrl, supabaseAnonKey)
const { data, error } = await supabase.auth.signInWithPassword({ email, password })
if (error || !data.session) {
console.error('[auth-setup] Login de usuario de test falló:', error?.message)
return
}
// Diagnóstico Sprint 5 Etapa 6a-extra: @supabase/auth-js v2 define STORAGE_KEY =
// 'supabase.auth.token' como default de GoTrueClient (constants.js), pero
// @supabase/supabase-js's createClient() SOBRESCRIBE ese default calculando
// `sb-${projectRef}-auth-token` a partir de la URL (ver SupabaseClient constructor:
// `i = sb-${hostname.split('.')[0]}-auth-token`, mergeado en `auth.storageKey`),
// salvo que el caller pase un storageKey explícito. src/lib/supabase.ts no lo hace,
// así que esta es la clave REAL que usa la app. Confirmado empíricamente: el test
// "app header shows user avatar" (sprint-4-smoke.spec.ts) requiere que AuthContext
// encuentre una sesión real bajo esta clave — pasa, y auth-state.json generado
// contiene exactamente `sb-${projectRef}-auth-token`. No es un bug.
const projectRef = new URL(supabaseUrl).hostname.split('.')[0]
const storageKey = `sb-${projectRef}-auth-token`
const browser = await chromium.launch()
const page = await browser.newPage()
await page.goto('http://localhost:4173/login')
await page.evaluate(
([key, session]) => {
window.localStorage.setItem(key, JSON.stringify(session))
},
[storageKey, data.session] as const
)
await page.context().storageState({ path: STATE_PATH })
await browser.close()
console.log(`[auth-setup] Sesión de test guardada en ${STATE_PATH}`)
}
export default globalSetup