/** * Diagnóstico Sprint 6 Etapa 3 Parte A: ¿bajo qué clave de localStorage escribe * supabase-js v2 la sesión en este entorno? ProfileSheet.getAccessToken() lee * 'supabase.auth.token' (STORAGE_KEY default de @supabase/auth-js GoTrueClient), * pero auth-setup.ts documenta que @supabase/supabase-js's createClient() sobrescribe * ese default calculando `sb-${projectRef}-auth-token` desde la URL del proyecto — * a menos que el caller pase un storageKey explícito (src/lib/supabase.ts no lo hace). * * Este spec confirma empíricamente cuál de las dos claves existe realmente en el * localStorage de una sesión autenticada real, antes de tocar ProfileSheet.tsx. */ import { test, expect } from '@playwright/test' import { readFileSync, existsSync } from 'fs' import { resolve } from 'path' import { fileURLToPath } from 'url' test.use({ storageState: 'tests/e2e/setup/auth-state.json' }) const __dirname = fileURLToPath(new URL('.', import.meta.url)) const ROOT = resolve(__dirname, '../..') // Mismo patrón manual que auth-setup.ts / export-pdf.spec.ts — no hay dotenv // cargando process.env automáticamente en el runner de Playwright. function loadEnvFile(filename: string): Record { const path = resolve(ROOT, filename) if (!existsSync(path)) return {} const out: Record = {} 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 } test('diagnóstico: confirmar la clave real de localStorage para la sesión Supabase', async ({ page }) => { await page.goto('/') // Esperar a que la app cargue (AuthContext lee el storageState y monta el header) await page.waitForSelector('header button[title]', { timeout: 10_000 }) const localEnv = loadEnvFile('.env.local') const supabaseUrl = localEnv.VITE_SUPABASE_URL const projectRef = supabaseUrl ? new URL(supabaseUrl).hostname.split('.')[0] : null const diagnosis = await page.evaluate((ref: string | null) => { const keys = Object.keys(localStorage) const authKeys = keys.filter((k) => k.includes('auth') || k.startsWith('sb-')) const legacyRaw = localStorage.getItem('supabase.auth.token') const v2Key = ref ? `sb-${ref}-auth-token` : null const v2Raw = v2Key ? localStorage.getItem(v2Key) : null return { allAuthRelatedKeys: authKeys, legacyKeyExists: legacyRaw !== null, v2KeyName: v2Key, v2KeyExists: v2Raw !== null, legacyHasAccessToken: legacyRaw ? Boolean((JSON.parse(legacyRaw) as { access_token?: string }).access_token) : false, v2HasAccessToken: v2Raw ? Boolean((JSON.parse(v2Raw) as { access_token?: string }).access_token) : false, } }, projectRef) console.log('[diagnóstico localStorage]', JSON.stringify(diagnosis, null, 2)) // Afirmación central: la clave v2 (sb-{projectRef}-auth-token) es la que existe y // tiene un access_token real. La clave legacy ('supabase.auth.token') NO existe. expect(diagnosis.v2KeyExists, `Esperaba que ${diagnosis.v2KeyName} exista en localStorage`).toBe(true) expect(diagnosis.v2HasAccessToken, 'La clave v2 debe contener un access_token no vacío').toBe(true) expect(diagnosis.legacyKeyExists, "'supabase.auth.token' (clave legacy que lee ProfileSheet hoy) NO debería existir").toBe(false) })