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

127 lines
4.8 KiB
JavaScript

// Script: browserless_e2e.cjs
// Conecta a Browserless via WebSocket y navega al broadcast panel para hacer click en "Entrar al Estudio".
// Uso:
// BROWSERLESS_TOKEN=e2e098863b912f6a178b68e71ec3c58d BROADCAST_URL=http://localhost:5175 node packages/broadcast-panel/scripts/browserless_e2e.cjs
const fs = require('fs');
const path = require('path');
const puppeteer = require('puppeteer-core');
const LOG_DIR = path.resolve(process.cwd(), 'packages', 'broadcast-panel', 'tmp');
const LOG_FILE = path.join(LOG_DIR, 'browserless_e2e.log');
if (!fs.existsSync(LOG_DIR)) fs.mkdirSync(LOG_DIR, { recursive: true });
function log(...args) {
const s = `[${new Date().toISOString()}] ${args.join(' ')}\n`;
fs.appendFileSync(LOG_FILE, s);
console.log(...args);
}
(async () => {
try {
const token = process.env.BROWSERLESS_TOKEN;
if (!token) {
log('ERROR: No BROWSERLESS_TOKEN provided in env.');
process.exit(1);
}
const broadcastUrl = process.env.BROADCAST_URL || process.env.VITE_BROADCASTPANEL_URL || 'http://localhost:5175';
const wsEndpoint = process.env.BROWSERLESS_WS || `wss://browserless.bfzqqk.easypanel.host?token=${token}`;
log('Connecting to Browserless at', wsEndpoint);
const browser = await puppeteer.connect({ browserWSEndpoint: wsEndpoint, defaultViewport: { width: 1280, height: 800 } });
log('Connected to browserless. Opening new page...');
const page = await browser.newPage();
// capture console messages from page into log
page.on('console', msg => log('PAGE_CONSOLE:', msg.type(), msg.text()));
page.on('pageerror', err => log('PAGE_ERROR:', err && err.stack ? err.stack : err));
log('Navigating to', broadcastUrl);
await page.goto(broadcastUrl, { waitUntil: 'networkidle2', timeout: 60000 });
log('Page loaded:', page.url());
// Try several XPaths / selectors matching localized button text
const xpaths = [
"//button[contains(normalize-space(.), 'Entrar al Estudio')]",
"//button[contains(normalize-space(.), 'Entrar al estudio')]",
"//button[contains(normalize-space(.), 'Entrar al Studio')]",
"//button[contains(normalize-space(.), 'Entrar')]",
"//a[contains(normalize-space(.), 'Entrar al Estudio')]",
"//a[contains(normalize-space(.), 'Entrar')]",
"//button[contains(translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'entrar')]",
];
let handle = null;
for (const xp of xpaths) {
try {
log('Trying XPath:', xp);
handle = await page.waitForXPath(xp, { timeout: 3000 });
if (handle) {
log('Found element for XPath:', xp);
break;
}
} catch (e) {
// continue
}
}
if (!handle) {
log('ERROR: No "Entrar" button found on page. Dumping top-level buttons for debugging...');
const buttons = await page.$$eval('button, a', els => els.slice(0,50).map(e => ({ text: e.innerText.trim().slice(0,80), html: e.outerHTML.slice(0,200) })));
log('BUTTONS_SNAPSHOT:', JSON.stringify(buttons, null, 2));
await browser.disconnect();
process.exit(2);
}
// Click the control
try {
await handle.click();
log('Clicked the Entrar control. Waiting for navigation or popup...');
} catch (e) {
log('Click failed, trying evaluate click()...', e && e.message);
try {
await page.evaluate(el => el.click(), handle);
log('Clicked via evaluate.');
} catch (e2) {
log('Failed to click element:', e2 && e2.message);
}
}
// Wait for possible navigation or popup
try {
await page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 8000 });
log('Navigation occurred. New URL:', page.url());
} catch (e) {
log('No navigation after click (timed out). Checking if a new popup opened...');
}
const allPages = await browser.pages();
log('Open pages count:', allPages.length);
for (const p of allPages) {
log('PAGE:', p.target()._targetId || p.url(), p.url());
}
// If a popup or new tab includes /studio or a session id, log it
const studioPage = allPages.find(p => /studio|session|y5n0wsr|\/\w{6}/i.test(p.url()));
if (studioPage) {
log('Detected candidate studio page:', studioPage.url());
// Could attempt to read token from URL param or DOM
const url = studioPage.url();
const search = new URL(url).searchParams;
const sessionParam = search.get('session') || search.get('token') || null;
log('Session param found in URL (if any):', sessionParam);
} else {
log('No obvious studio page detected by URL pattern. Current page URL:', page.url());
}
log('Done. Disconnecting from browserless. Logs saved to', LOG_FILE);
await browser.disconnect();
process.exit(0);
} catch (err) {
log('FATAL ERROR:', err && err.stack ? err.stack : err);
process.exit(10);
}
})();