AvanzaCast/e2e/analyze_external_page_browserless.js
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

150 lines
6.5 KiB
JavaScript

// e2e/analyze_external_page_browserless.js
// Connects to Browserless and analyzes an external page (network, console, storage, ws, postMessage)
// Usage: set env TARGET_URL and BROWSERLESS_WS (and optional BROWSERLESS_TOKEN)
const fs = require('fs');
const path = require('path');
const puppeteer = require('puppeteer-core');
(async () => {
const outDir = path.resolve(__dirname);
const results = { startedAt: new Date().toISOString(), console: [], pageErrors: [], network: [], failures: [], wsEvents: [], postMessages: [], storage: { session: {}, local: {} }, cookies: [] };
const ws = process.env.BROWSERLESS_WS;
const btoken = process.env.BROWSERLESS_TOKEN || '';
if (!ws) {
console.error('BROWSERLESS_WS required');
process.exit(2);
}
let wsUrl = ws;
try { if (btoken) wsUrl = ws.includes('?') ? `${ws}&token=${encodeURIComponent(btoken)}` : `${ws}?token=${encodeURIComponent(btoken)}` } catch(e){}
const target = process.env.TARGET_URL || process.env.BROADCAST_URL || '';
if (!target) { console.error('TARGET_URL required'); process.exit(2); }
let browser;
try {
browser = await puppeteer.connect({ browserWSEndpoint: wsUrl, ignoreHTTPSErrors: true, defaultViewport: { width: 1366, height: 768 } });
} catch (err) {
console.error('Failed to connect to browserless', err && err.message ? err.message : err);
process.exit(2);
}
const page = await browser.newPage();
page.setDefaultNavigationTimeout(45000);
// capture console
page.on('console', msg => {
try { results.console.push({ type: msg.type(), text: msg.text(), location: msg.location() }); } catch(e){}
});
page.on('pageerror', err => results.pageErrors.push(String(err && err.stack ? err.stack : err)));
// capture network
page.on('request', req => {
results.network.push({ id: req._requestId || null, url: req.url(), method: req.method(), resourceType: req.resourceType(), headers: req.headers() });
});
page.on('requestfailed', req => {
results.failures.push({ url: req.url(), errorText: req.failure() && req.failure().errorText ? req.failure().errorText : 'failed' });
});
page.on('response', async res => {
try {
const req = res.request();
const url = res.url();
const status = res.status();
const ct = res.headers()['content-type'] || null;
results.network.push({ url, status, contentType: ct, method: req.method() });
} catch(e){}
});
// inject instrumentation for WebSocket/postMessage before any script runs
await page.evaluateOnNewDocument(() => {
try {
// WebSocket wrapper
(function(){
const OriginalWS = window.WebSocket;
window.__ws_events = [];
function MyWS(url, protocols){
const ws = protocols ? new OriginalWS(url, protocols) : new OriginalWS(url);
const id = Math.random().toString(36).slice(2,9);
window.__ws_events.push({ event: 'created', id, url, protocols: protocols || null, time: Date.now() });
ws.addEventListener('open', () => window.__ws_events.push({ event: 'open', id, time: Date.now() }));
ws.addEventListener('close', () => window.__ws_events.push({ event: 'close', id, time: Date.now() }));
ws.addEventListener('error', (e) => window.__ws_events.push({ event: 'error', id, time: Date.now(), detail: String(e && e.message ? e.message : e) }));
ws.addEventListener('message', (m) => {
let data = m.data;
try { data = typeof data === 'string' ? data : JSON.stringify(data); } catch(e){}
window.__ws_events.push({ event: 'message', id, time: Date.now(), data: data });
});
const origSend = ws.send.bind(ws);
ws.send = function(data){
try { window.__ws_events.push({ event: 'send', id, time: Date.now(), data: (typeof data==='string'?data:JSON.stringify(data)) }); } catch(e){}
return origSend(data);
}
return ws;
}
try { MyWS.prototype = OriginalWS.prototype } catch(e){}
window.WebSocket = MyWS;
})();
// postMessage wrapper
(function(){
window.__post_messages = [];
const origPost = window.postMessage.bind(window);
window.postMessage = function(msg, targetOrigin, transfer){
try { window.__post_messages.push({ time: Date.now(), msg: msg, targetOrigin: targetOrigin }); } catch(e){}
return origPost(msg, targetOrigin, transfer);
}
})();
} catch(e){}
});
try {
await page.goto(target, { waitUntil: 'networkidle2' });
} catch (err) {
// still continue to gather whatever we can
results.console.push({ type: 'error', text: 'navigation_failed: ' + String(err && err.message ? err.message : err) });
}
// wait a bit for scripts
await page.waitForTimeout(4000);
// gather sessionStorage/localStorage
try {
const storeKey = (await page.evaluate(() => (window && (window.__AVZ_STUDIO_SESSION_KEY__ || null)))) || null;
const session = await page.evaluate(() => {
try { const s = {}; for (let i=0;i<sessionStorage.length;i++){ const k=sessionStorage.key(i); s[k]=sessionStorage.getItem(k);} return s; } catch(e){ return {}; }
});
const local = await page.evaluate(() => { try { const s={}; for(let i=0;i<localStorage.length;i++){ const k=localStorage.key(i); s[k]=localStorage.getItem(k);} return s; } catch(e){ return {}; } });
results.storage.session = session;
results.storage.local = local;
} catch(e){}
// cookies
try { results.cookies = await page.cookies(); } catch(e){}
// collect ws events & postMessages from page context
try {
const events = await page.evaluate(() => { try { return window.__ws_events || []; } catch(e){ return []; } });
results.wsEvents = events;
} catch(e){}
try {
const pms = await page.evaluate(() => { try { return window.__post_messages || []; } catch(e){ return []; } });
results.postMessages = pms;
} catch(e){}
// screenshot
const shot = path.join(outDir, 'analyze_external_page_result.png');
try { await page.screenshot({ path: shot, fullPage: true }); results.screenshot = shot; } catch(e) { results.screenshot = null }
// write JSON
results.endedAt = new Date().toISOString();
const outJson = path.join(outDir, 'analyze_external_page_result.json');
fs.writeFileSync(outJson, JSON.stringify(results, null, 2));
console.log('Wrote results to', outJson);
try { await page.close(); } catch(e){}
try { await browser.disconnect(); } catch(e) { try { await browser.close(); } catch(e){} }
process.exit(0);
})();