#!/usr/bin/env node // Playwright E2E test for Studio Panel (automates Broadcast -> Studio flow) import fetch from 'node-fetch'; import fs from 'fs'; import { chromium } from 'playwright'; const TOKEN_SERVER = process.env.TOKEN_SERVER_URL || 'http://localhost:4000'; const STUDIO_BASE = process.env.STUDIO_URL || 'http://localhost:3020'; const TIMEOUT = Number(process.env.E2E_TIMEOUT_MS || 30000); function log(...args) { console.log(new Date().toISOString(), ...args); } async function createSession() { log('POST /api/session ->', TOKEN_SERVER); const res = await fetch(`${TOKEN_SERVER}/api/session`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ room: 'e2e-room', username: 'pw-runner' }) }); const body = await res.json(); log('session response', res.status, body); if (!res.ok) throw new Error('session creation failed'); return body; } (async () => { try { const session = await createSession(); const studioUrl = session.redirectUrl || session.studioUrl || (session.id ? `${STUDIO_BASE}/${session.id}` : null); if (!studioUrl) throw new Error('No studio url returned'); log('Launching browser'); const browser = await chromium.launch({ headless: true }); const context = await browser.newContext(); const page = await context.newPage(); log('Opening', studioUrl); const resp = await page.goto(studioUrl, { waitUntil: 'domcontentloaded', timeout: TIMEOUT }); log('opened status', resp && resp.status()); // Wait for either a #status element or a known text try { const status = page.locator('#status'); await status.waitFor({ timeout: 7000 }); const txt = await status.textContent(); log('#status text:', txt && txt.slice(0,200)); } catch (e) { log('No #status or timeout, trying heuristics'); // look for explicit token string in page content const content = await page.content(); if (content.includes('Token recibido') || content.includes('LIVEKIT_TOKEN') || content.includes(session.token || '')) { log('Token text appears in page content'); } else { log('Token not obviously present in page content'); } } const s1 = '/tmp/pw_e2e_studio_' + Date.now() + '.png'; await page.screenshot({ path: s1, fullPage: true }); log('Saved screenshot', s1); // Also fetch session via API to assert token present if (session.id) { try { const lookup = await fetch(`${TOKEN_SERVER}/api/session/${session.id}`); const j = await lookup.json(); log('session lookup', lookup.status, Object.keys(j)); const sessionFile = `/tmp/session_${session.id}.json`; fs.writeFileSync(sessionFile, JSON.stringify(j, null, 2)); log('Saved session JSON to', sessionFile); } catch (err) { log('session lookup failed', err.message) } } await browser.close(); log('E2E success'); process.exit(0); } catch (err) { console.error('E2E error', err.message); process.exit(1); } })();