156 lines
6.9 KiB
JavaScript

#!/usr/bin/env node
// E2E script: connect to remote Chrome on localhost:9222 and run token flow (create session -> open Broadcast Panel -> Entrar al Estudio)
// Usage: BROWSERLESS_WS not used. Instead set REMOTE_DEBUGGER_URL (e.g. http://localhost:9222) or default to http://localhost:9222
// Environment variables:
// TOKEN_SERVER (default: https://avanzacast-servertokens.bfzqqk.easypanel.host)
// ROOM, USERNAME
// OUT_DIR to save artifacts
const fetch = require('node-fetch');
const puppeteer = require('puppeteer-core');
const fs = require('fs');
const path = require('path');
async function main() {
const REMOTE_DEBUGGER = process.env.REMOTE_DEBUGGER_URL || 'http://localhost:9222';
const TOKEN_SERVER = process.env.TOKEN_SERVER || 'https://avanzacast-servertokens.bfzqqk.easypanel.host';
const ROOM = process.env.ROOM || 'e2e-room';
const USERNAME = process.env.USERNAME || 'e2e-runner';
const OUT_DIR = process.env.OUT_DIR || '/tmp';
function outLog(...args) {
console.log(...args);
if (OUT_DIR) {
try { fs.appendFileSync(path.join(OUT_DIR, 'e2e_remote_9222.log'), args.map(String).join(' ') + '\n'); } catch (e) {}
}
}
outLog('E2E remote 9222 starting', { REMOTE_DEBUGGER, TOKEN_SERVER, ROOM, USERNAME, OUT_DIR });
// Create session on token server
let sessResp;
try {
sessResp = await fetch(`${TOKEN_SERVER.replace(/\/$/, '')}/api/session`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ room: ROOM, username: USERNAME, ttl: 300 })
});
} catch (err) {
outLog('Network error when creating session:', String(err));
process.exit(3);
}
outLog('Token server status', sessResp.status);
let sessBodyText = await sessResp.text().catch(() => '');
let sessJson = null;
try { sessJson = JSON.parse(sessBodyText); } catch(e) { sessJson = null }
outLog('Session response body start:', String(sessBodyText).slice(0,400));
if (!sessResp.ok) {
outLog('Failed to create session');
process.exit(4);
}
if (!sessJson) {
outLog('Invalid JSON from token server');
process.exit(5);
}
const sessionId = sessJson.id || sessJson.sessionId || null;
const token = sessJson.token || null;
const studioUrl = sessJson.studioUrl || sessJson.redirectUrl || sessJson.url || null;
outLog('Session created', { sessionId: sessionId ? sessionId.slice(0,10) + '...' : null, studioUrl, hasToken: !!token });
// Connect to remote Chrome DevTools
// We expect a running Chrome with --remote-debugging-port=9222. puppeteer.connect accepts websocket endpoint; fetch the ws endpoint from /json/version
let wsEndpoint;
try {
const versionResp = await fetch(`${REMOTE_DEBUGGER.replace(/\/$/, '')}/json/version`);
const verJson = await versionResp.json();
wsEndpoint = verJson.webSocketDebuggerUrl || null;
} catch (err) {
outLog('Failed to query remote debugger /json/version:', String(err));
process.exit(6);
}
if (!wsEndpoint) {
outLog('Remote debugger did not return webSocketDebuggerUrl. Check Chrome is running with --remote-debugging-port=9222');
process.exit(7);
}
outLog('Connecting to remote chrome wsEndpoint', wsEndpoint);
let browser;
try {
browser = await puppeteer.connect({ browserWSEndpoint: wsEndpoint, defaultViewport: { width: 1366, height: 768 }, timeout: 20000 });
} catch (err) {
outLog('puppeteer.connect failed:', String(err));
process.exit(8);
}
try {
const page = await browser.newPage();
page.on('console', msg => outLog('[BROWSER]', msg.type(), msg.text()));
page.on('pageerror', err => outLog('[PAGEERROR]', String(err)));
// Navigate to either the studioUrl returned or to broadcast panel root and click 'Entrar al Estudio'
let targetUrl = studioUrl;
if (!targetUrl && sessionId) {
// use broadcast panel host from environment or default
const BROADCAST_HOST = process.env.BROADCAST_HOST || 'https://avanzacast-broadcastpanel.bfzqqk.easypanel.host';
targetUrl = `${BROADCAST_HOST.replace(/\/$/, '')}/${encodeURIComponent(sessionId)}`;
}
if (!targetUrl) {
outLog('No target URL to open');
process.exit(9);
}
outLog('Opening target URL:', targetUrl);
await page.goto(targetUrl, { waitUntil: 'domcontentloaded', timeout: 30000 }).catch(err => { outLog('page.goto error', String(err)); throw err; });
// If page has a visible button/link text 'Entrar' or 'Entrar al Estudio', click it
const clickSelectors = ["button[aria-label='Entrar al Estudio']","button:has-text('Entrar al Estudio')","button:has-text('Entrar')","a:has-text('Entrar al Estudio')","a:has-text('Entrar')"];
let clicked = false;
for (const sel of clickSelectors) {
try {
// Puppeteer does not support :has-text in older versions; use evaluate to find by text
const found = await page.evaluate((text) => {
const els = Array.from(document.querySelectorAll('button,a'));
const el = els.find(e => (e.innerText || '').trim().toLowerCase().includes(text.toLowerCase()));
if (el) { el.scrollIntoView(); el.click(); return true; }
return false;
}, sel.replace(/.*:has-text\('(.+)'\).*/, '$1'));
if (found) { clicked = true; outLog('Clicked element matching', sel); break; }
} catch (e) { /* ignore */ }
}
// If token is present, postMessage it
if (token) {
outLog('Posting token via postMessage (len=' + String(token.length) + ')');
try {
await page.evaluate((tk) => { window.postMessage({ type: 'LIVEKIT_TOKEN', token: tk, url: window.location.href }, window.location.origin); }, token);
} catch (e) { outLog('postMessage failed', String(e)); }
} else {
outLog('No token present in session response; relying on redirect flow');
}
// Wait for token received indicator in page
const gotToken = await page.waitForFunction(() => document.body && document.body.innerText && (document.body.innerText.includes('Token recibido') || document.body.innerText.includes('Token recibido desde Broadcast Panel') || document.body.innerText.includes('Esperando token')), { timeout: 10000 }).catch(() => false);
outLog('Token indicator found:', !!gotToken);
// Save artifacts
if (OUT_DIR) {
try { const html = await page.content(); fs.writeFileSync(path.join(OUT_DIR, `page_${sessionId || 'noid'}.html`), html); outLog('Saved page HTML'); } catch(e) { outLog('Failed to save HTML', String(e)); }
try { await page.screenshot({ path: path.join(OUT_DIR, `e2e_${sessionId || 'noid'}.png`), fullPage: true }); outLog('Saved screenshot'); } catch(e) { outLog('Failed to save screenshot', String(e)); }
}
await page.close();
} finally {
try { await browser.disconnect(); } catch(e) {}
}
outLog('E2E remote 9222 finished');
}
main().catch(err => { console.error('Unhandled error in main:', err && err.stack ? err.stack : String(err)); process.exit(99); });