// 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 { try { const s={}; for(let i=0;i { 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); })();