156 lines
6.9 KiB
JavaScript
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); });
|
|
|