// scripts/e2e/auto_enter_and_wait_token.js // Automate navigation to broadcast-panel and repeatedly click "Entrar al estudio" // until a token-like value appears in sessionStorage (key contains 'token' or 'livekit'). // Usage: // CHROME_DEBUG_URL=http://127.0.0.1:9222/json/version E2E_LOG=/tmp/e2e_auto_run.log node scripts/e2e/auto_enter_and_wait_token.js [targetUrl] [--keep-open] const fs = require('fs') const path = require('path') const TARGET = process.argv.find(a => a && !a.startsWith('--')) || 'http://localhost:5175' const KEEP_OPEN = process.argv.includes('--keep-open') || process.env.KEEP_OPEN === '1' const E2E_LOG = process.env.E2E_LOG || '/tmp/e2e_auto_run.log' function log(...args) { try { fs.appendFileSync(E2E_LOG, `[${new Date().toISOString()}] ${args.map(a => (typeof a === 'string' ? a : JSON.stringify(a))).join(' ')}\n`) } catch (e){} console.log(...args) } async function fetchJson(url) { const fetch = global.fetch || (await import('node-fetch')).then(m => m.default) const r = await fetch(url) if (!r.ok) throw new Error(`fetch ${url} failed ${r.status}`) return r.json() } async function findDebuggerWS(debugUrl) { const info = await fetchJson(debugUrl) return info.webSocketDebuggerUrl } async function findOrOpenPage(browser, targetUrl) { const pages = await browser.pages() for (const p of pages) { try { const u = p.url() if (!u || u === 'about:blank') continue if (u === targetUrl || u.startsWith(targetUrl) || u.includes('localhost:5175')) { return p } } catch (e) { } } const p = await browser.newPage() await p.bringToFront() await p.goto(targetUrl, { waitUntil: 'networkidle2', timeout: 30000 }) return p } async function waitForXPathPoly(page, xpath, timeout = 15000, interval = 250) { const start = Date.now() while (Date.now() - start < timeout) { try { const els = await page.$x(xpath) if (els && els.length) return els } catch (e) {} await new Promise(r => setTimeout(r, interval)) } throw new Error(`Timeout waiting for XPath: ${xpath}`) } async function detectTokenInSession(page) { return await page.evaluate(() => { try { const keys = Object.keys(sessionStorage || {}) for (const k of keys) { const v = sessionStorage.getItem(k) if (!v) continue const kl = k.toLowerCase() if (kl.includes('token') || kl.includes('livekit') || kl.includes('session') || kl.includes('studio')) { if (typeof v === 'string' && v.length > 8) return { key: k, value: v } } } // fallback: find any value that looks like JWT (three parts separated by .) for (const k of keys) { const v = sessionStorage.getItem(k) || '' if (typeof v === 'string' && v.split('.').length === 3 && v.length > 20) return { key: k, value: v } } return null } catch (e) { return null } }) } async function clickEnterStudio(page) { // Try to click an 'Entrar al estudio' button; return true if clicked const xpaths = [ "(//button[contains(normalize-space(.),'Entrar al estudio') or contains(@aria-label,'Entrar al estudio') or normalize-space(text())='Entrar al estudio'])[1]", "//button[contains(.,'Entrar al estudio')][1]", "//a[contains(.,'Entrar al estudio')][1]" ] for (const xp of xpaths) { try { const els = await page.$x(xp) if (els && els.length) { await els[0].click() return true } } catch (e) {} } return false } async function main() { try { const debugUrl = process.env.CHROME_DEBUG_URL || 'http://127.0.0.1:9222/json/version' log('[auto] debugUrl', debugUrl, 'target', TARGET, 'KEEP_OPEN', KEEP_OPEN) const ws = await findDebuggerWS(debugUrl) log('[auto] ws', ws) const puppeteer = require('puppeteer-core') const browser = await puppeteer.connect({ browserWSEndpoint: ws, defaultViewport: { width: 1280, height: 800 } }) const page = await findOrOpenPage(browser, TARGET) await page.bringToFront() // install lightweight listeners to log relevant console messages page.on('console', msg => { try { const txt = msg.text && typeof msg.text === 'function' ? msg.text() : '' if (txt && (txt.startsWith('__E2E_') || /Token recibido|Esperando token|LIVEKIT|LIVEKIT_TOKEN|Entrar al estudio|StudioPortal|useStudioLauncher/.test(txt))) log('[page]', txt) } catch (e) {} }) log('[auto] starting loop to click Entrar al estudio and wait for session token') const maxAttempts = 40 for (let i = 0; i < maxAttempts; i++) { log('[auto] attempt', i+1) // try to click enter studio const clicked = await clickEnterStudio(page) log('[auto] clickedEnter?', clicked) // after clicking, wait a bit for overlay and for sessionStorage to update try { await page.waitForTimeout(1000) // bring to front try { await page.bringToFront() } catch(e){} // check sessionStorage repeatedly for a short window const checkStart = Date.now() while (Date.now() - checkStart < 8000) { const found = await detectTokenInSession(page) if (found) { log('[auto] TOKEN FOUND', found.key, 'len', (found.value || '').length) // save screenshot and exit const out = path.resolve(process.cwd(), 'auto_token_found.png') await page.screenshot({ path: out, fullPage: true }) log('[auto] screenshot saved', out) if (!KEEP_OPEN) { await page.close() await browser.disconnect() process.exit(0) } else { log('[auto] KEEP_OPEN set; leaving page open. Ctrl+C to terminate.') await new Promise(()=>{}) } } await page.waitForTimeout(500) } } catch (e) { log('[auto] wait/check error', e && e.message) } // if not found, try to recreate the flow: click create -> skip -> start try { // try to click create transmission (if exists) const createXPath = "//button[.//span/text() = 'Transmisión en vivo' or .//span[contains(text(),'Transmisión en vivo')]]" try { const createEls = await page.$x(createXPath) if (createEls && createEls.length) { await createEls[0].click(); log('[auto] clicked create') } } catch (e) {} await page.waitForTimeout(500) // try skip try { const [skip] = await page.$x("//*[normalize-space(text())='Omitir por ahora' or normalize-space(text())='Omitar por ahora' or normalize-space(text())='Skip for now']") if (skip) { await skip.click(); log('[auto] clicked skip') } } catch(e) {} await page.waitForTimeout(300) // try start try { const [startBtn] = await page.$x("//button[normalize-space(text())='Empezar ahora' or normalize-space(text())='Crear transmisión en vivo' or normalize-space(text())='Start now']") if (startBtn) { await startBtn.click(); log('[auto] clicked start') } } catch (e) {} await page.waitForTimeout(800) } catch (e) { log('[auto] flow recreate err', e && e.message) } } log('[auto] max attempts reached; did not find token') const out = path.resolve(process.cwd(), 'auto_token_notfound.png') await page.screenshot({ path: out, fullPage: true }) log('[auto] screenshot saved', out) if (!KEEP_OPEN) { await page.close() await browser.disconnect() process.exit(2) } else { log('[auto] KEEP_OPEN set; leaving page open. Ctrl+C to terminate.') await new Promise(()=>{}) } } catch (err) { log('[auto] ERROR', err && (err.stack || err.message || String(err))) process.exit(2) } } main()