const fs = require('fs'); const http = require('http'); const WebSocket = require('ws'); const path = require('path'); const ARTIFACTS_DIR = "C:\\Users\\encep\\.gemini\\antigravity\\brain\\8f83f382-c9ee-4ef3-8e9b-a7dec5371b5a"; function getTargets() { return new Promise((resolve, reject) => { http.get('http://127.0.0.1:9222/json/list', (res) => { let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => resolve(JSON.parse(data))); }).on('error', reject); }); } function sendCDP(ws, method, params = {}) { return new Promise((resolve, reject) => { const id = Math.floor(Math.random() * 1000000); const handler = (data) => { const msg = JSON.parse(data); if (msg.id === id) { ws.removeListener('message', handler); if (msg.error) reject(msg.error); else resolve(msg.result); } }; ws.on('message', handler); ws.send(JSON.stringify({ id, method, params })); }); } async function runFullSuite() { const targets = await getTargets(); const mainTarget = targets.find(t => t.title === 'D3RO Voice' || t.url.includes('index.html')); if (!mainTarget) { console.error('Target not found:', targets); return; } const wsUrl = mainTarget.webSocketDebuggerUrl.replace('localhost', '127.0.0.1'); const ws = new WebSocket(wsUrl); await new Promise((resolve) => ws.on('open', resolve)); console.log('--- STARTING FULL E2E VISUAL TEST SUITE ---'); await sendCDP(ws, 'Page.enable'); await sendCDP(ws, 'Runtime.enable'); const errors = []; ws.on('message', (data) => { try { const msg = JSON.parse(data); if (msg.method === 'Runtime.consoleAPICalled' && msg.params.type === 'error') { const errText = msg.params.args.map(a => a.value || a.description || JSON.stringify(a)).join(' '); console.error('[RENDERER CONSOLE ERROR DETECTED]:', errText); errors.push(errText); } } catch(e){} }); const captureScreen = async (filename) => { try { const shot = await sendCDP(ws, 'Page.captureScreenshot', { format: 'png' }); const targetPath = path.join(ARTIFACTS_DIR, filename); fs.writeFileSync(targetPath, Buffer.from(shot.data, 'base64')); console.log(`[Screen Captured] ${filename}`); } catch(e) { console.error(`Failed to capture ${filename}:`, e.message); } }; // 1. Initial Dashboard await captureScreen('page_1_dashboard.png'); // 2. Dismiss modal try { await sendCDP(ws, 'Runtime.evaluate', { expression: ` (() => { const btns = Array.from(document.querySelectorAll('button')); const laterBtn = btns.find(b => b.innerText.includes('나중에')); if (laterBtn) laterBtn.click(); })() ` }); } catch(e) { console.error('Modal dismiss error:', e.message); } await new Promise(r => setTimeout(r, 600)); await captureScreen('page_1_dashboard_clean.png'); // 3. Test Navigation Tabs const menuItems = ['히스토리', '사전', '명령어', '대화', '지식 베이스', '회의', '설정']; for (let i = 0; i < menuItems.length; i++) { const item = menuItems[i]; console.log(`Navigating to tab: ${item}`); try { await sendCDP(ws, 'Runtime.evaluate', { expression: ` (() => { const elements = Array.from(document.querySelectorAll('div, span, p, a, button')); const el = elements.find(e => e.innerText && e.innerText.trim() === '${item}'); if (el) { el.click(); return true; } return false; })() ` }); } catch(e) { console.error(`Tab click error (${item}):`, e.message); } await new Promise(r => setTimeout(r, 800)); await captureScreen(`page_${i + 2}_${item.replace(/\s+/g, '_')}.png`); } // 4. Test Subtab console.log('Testing Settings Sub-Tabs...'); try { await sendCDP(ws, 'Runtime.evaluate', { expression: ` (() => { const tabs = Array.from(document.querySelectorAll('button, div[role="tab"]')); if (tabs.length > 0) tabs[0].click(); })() ` }); } catch(e){} await new Promise(r => setTimeout(r, 500)); await captureScreen('page_settings_subtab.png'); console.log('--- E2E TEST SUITE FINISHED ---'); console.log('Total Console Errors Detected during flow:', errors.length); ws.close(); } runFullSuite().catch(err => { console.error('Suite error:', err); process.exit(1); });