AvanzaCast/e2e/puppeteer-runner/debug-post-token.js
Cesar Mendivil 8b458a3ddf feat: add initial LiveKit Meet integration with utility scripts, configs, and core components
- Add Next.js app structure with base configs, linting, and formatting
- Implement LiveKit Meet page, types, and utility functions
- Add Docker, Compose, and deployment scripts for backend and token server
- Provide E2E and smoke test scaffolding with Puppeteer and Playwright helpers
- Include CSS modules and global styles for UI
- Add postMessage and studio integration utilities
- Update package.json with dependencies and scripts for development and testing
2025-11-20 12:50:38 -07:00

173 lines
6.8 KiB
JavaScript

// Debug helper: post a JWT token to BroadcastPanel and wait for StudioPortal to react
// Usage: E2E_TOKEN=... node debug-post-token.js
const puppeteer = require('puppeteer')
const fs = require('fs')
const path = require('path')
require('dotenv').config()
const BROWSERLESS_WS = process.env.BROWSERLESS_WS || process.env.BROWSERLESS || ''
const CHROME_PATH = process.env.CHROME_PATH || '/usr/bin/chromium'
const BROADCAST_URL = process.env.VITE_BROADCASTPANEL_URL || 'https://avanzacast-broadcastpanel.bfzqqk.easypanel.host'
let TOKEN = process.env.E2E_TOKEN || process.env.TOKEN || ''
const OUT = process.cwd()
const TOKEN_SERVER = (process.env.VITE_TOKEN_SERVER_URL || process.env.TOKEN_SERVER_URL || '').replace(/\/$/, '')
async function fetchTokenFromServer(room = 'e2e-room', username = 'cli-run') {
if (!TOKEN_SERVER) return null
try {
console.info('Requesting token from token server:', TOKEN_SERVER)
const res = await fetch(`${TOKEN_SERVER}/api/session`, {
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ room, username })
})
const text = await res.text()
let json = null
try { json = JSON.parse(text) } catch (e) { console.warn('Token server returned non-json:', text) }
if (!res.ok) {
console.warn('Token server returned', res.status, text)
return null
}
if (json) {
if (json.token) return json.token
if (json.redirectUrl) {
try {
const u = new URL(json.redirectUrl)
const t = u.searchParams.get('token')
if (t) return t
} catch (e) {}
}
if (json.id) {
// try GET /api/session/:id
try {
const r2 = await fetch(`${TOKEN_SERVER}/api/session/${encodeURIComponent(json.id)}`)
if (r2.ok) {
const j2 = await r2.json()
if (j2.token) return j2.token
}
} catch (e) {}
}
}
return null
} catch (e) {
console.warn('Failed to fetch token from server', e && e.message ? e.message : e)
return null
}
}
async function getBrowser() {
if (BROWSERLESS_WS) {
try {
console.info('Connecting to browserless:', BROWSERLESS_WS)
return await puppeteer.connect({ browserWSEndpoint: BROWSERLESS_WS, defaultViewport: { width: 1280, height: 800 } })
} catch (e) {
console.warn('browserless connect failed', e && e.message)
}
}
console.info('Launching local Chromium at', CHROME_PATH)
return await puppeteer.launch({ executablePath: CHROME_PATH, headless: 'new', args: ['--no-sandbox','--disable-setuid-sandbox','--disable-dev-shm-usage'], defaultViewport: { width: 1280, height: 800 } })
}
async function run() {
// if no token, try to fetch one from token server automatically
if (!TOKEN) {
console.log('No E2E token provided, attempting to fetch from token server...')
const fetched = await fetchTokenFromServer()
if (fetched) {
TOKEN = fetched
console.log('Fetched token length=', String(TOKEN).length)
} else {
console.error('No token provided and token server fetch failed. Set E2E_TOKEN env var or configure VITE_TOKEN_SERVER_URL')
process.exit(2)
}
}
if (!TOKEN) {
console.error('No token provided. Set E2E_TOKEN env var.')
process.exit(2)
}
const browser = await getBrowser()
const page = await browser.newPage()
try {
page.on('console', msg => console.log('[page]', msg.type(), msg.text()))
page.on('pageerror', err => console.error('[pageerr]', err && err.stack ? err.stack : err))
console.log('Navigating to', BROADCAST_URL)
await page.goto(BROADCAST_URL, { waitUntil: 'networkidle2', timeout: 60000 })
await page.waitForTimeout(1000)
// clear local state that may contain old tokens
await page.evaluate(() => {
try { localStorage.removeItem('lk-user-choices') } catch(e) {}
try { sessionStorage.clear() } catch(e) {}
})
const shotBefore = path.join(OUT, 'debug-before.png')
await page.screenshot({ path: shotBefore, fullPage: true })
console.log('Saved screenshot', shotBefore)
const payload = { type: 'LIVEKIT_TOKEN', token: TOKEN, url: process.env.VITE_LIVEKIT_WS_URL || '', room: 'e2e-room' }
console.log('Posting token to page... token length=', String(TOKEN).length)
// Attempt to post into iframe if present, else to window
await page.evaluate((payload) => {
try {
const iframe = document.querySelector('iframe#studio-portal')
if (iframe && iframe.contentWindow) {
iframe.contentWindow.postMessage(payload, '*')
window.__e2e_posted = 'iframe'
} else {
window.postMessage(payload, '*')
window.__e2e_posted = 'self'
}
} catch (e) {
try { window.postMessage(payload, '*'); window.__e2e_posted = 'self' } catch(_) { window.__e2e_posted = 'failed' }
}
}, payload)
// Wait for StudioPortal to show token received or show error/connected
const start = Date.now()
const timeout = 25000
let state = { status: 'unknown' }
while (Date.now() - start < timeout) {
const r = await page.evaluate(() => {
const out = { tokenFlag: !!(window as any).__e2e_posted, tokenTarget: (window as any).__e2e_posted || null }
try {
const tokenNotice = Array.from(document.querySelectorAll('div, span'))
.map(n => (n.textContent||'').toLowerCase())
.find(t => t.includes('token recibido') || t.includes('esperando token') || t.includes('token'))
if (tokenNotice) out.tokenNotice = tokenNotice
} catch(e) {}
// detect studio connected element (common selectors)
const connected = !!document.querySelector('.studio-status[data-status="connected"]')
if (connected) out.connected = true
// detect error modal
const err = document.querySelector('.studio-error-modal')
if (err) out.error = (err.textContent||'').trim()
return out
})
if (r.connected) { state = { status: 'connected', detail: r }; break }
if (r.error) { state = { status: 'error', detail: r }; break }
if (r.tokenFlag) state = { status: 'posted', detail: r }
await page.waitForTimeout(600)
}
const shotAfter = path.join(OUT, 'debug-after.png')
await page.screenshot({ path: shotAfter, fullPage: true })
console.log('Saved screenshot', shotAfter)
const outPath = path.join(OUT, 'debug-post-result.json')
await fs.promises.writeFile(outPath, JSON.stringify({ state, timestamp: Date.now() }, null, 2), 'utf8')
console.log('Wrote result to', outPath)
console.log('STATE:', JSON.stringify(state, null, 2))
await browser.close()
return state
} catch (err) {
console.error('Runner error', err && err.stack ? err.stack : err)
try { await browser.close() } catch(e) {}
process.exit(3)
}
}
run().catch(e=>{ console.error(e); process.exit(5) })