- 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
127 lines
4.8 KiB
JavaScript
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);
|
|
}
|
|
})();
|
|
|