const puppeteer = require('puppeteer'); const AuthHelper = require('./auth_helper'); const fs = require('fs').promises; const path = require('path'); const { getPuppeteerLaunchOptions } = require('./puppeteer_config'); const BASE_URL = 'http://127.0.0.1:5000'; const TIMEOUT = 120000; // 2 minutes for research to complete // Color codes for output const colors = { info: '\x1b[36m', success: '\x1b[32m', warning: '\x1b[33m', error: '\x1b[31m', section: '\x1b[34m', reset: '\x1b[0m' }; function log(message, type = 'info') { const timestamp = new Date().toLocaleTimeString(); console.log(`${colors[type]}[${timestamp}] ${message}${colors.reset}`); } async function configureSettingsForTest(page) { log('Configuring settings for faster test...', 'info'); await page.goto(`${BASE_URL}/settings`, { waitUntil: 'domcontentloaded' }); // Set minimal iterations and questions await page.evaluate(() => { // Set iterations to 1 const iterationsInput = document.querySelector('input[name="app.research_iterations"]') || document.querySelector('#app\\.research_iterations'); if (iterationsInput) { iterationsInput.value = '1'; iterationsInput.dispatchEvent(new Event('change', { bubbles: true })); } // Set questions per iteration to 1 const questionsInput = document.querySelector('input[name="app.questions_per_iteration"]') || document.querySelector('#app\\.questions_per_iteration'); if (questionsInput) { questionsInput.value = '1'; questionsInput.dispatchEvent(new Event('change', { bubbles: true })); } // Set direct mode if available const directModeCheckbox = document.querySelector('input[name="app.direct_mode"]') || document.querySelector('#app\\.direct_mode'); if (directModeCheckbox && !directModeCheckbox.checked) { directModeCheckbox.click(); } }); // Save settings const saveButton = await page.$('button[type="submit"]'); if (saveButton) { await saveButton.click(); await new Promise(r => setTimeout(r, 2000)); } log('Settings configured', 'success'); } async function startAndCompleteResearch(page, query) { log(`Starting research: "${query}"`, 'info'); // Navigate to home page await page.goto(`${BASE_URL}/`, { waitUntil: 'domcontentloaded' }); // Wait for research form await page.waitForSelector('#research-form', { visible: true }); // Clear and type research query - use correct selector const queryInput = await page.$('#query') || await page.$('#research_query') || await page.$('input[name="query"]'); if (queryInput) { await queryInput.click({ clickCount: 3 }); // Select all await queryInput.type(query); } else { throw new Error('Could not find query input field'); } // Submit research await Promise.all([ page.waitForNavigation({ waitUntil: 'domcontentloaded' }), page.click('#start-research-btn, #submit-research, button[type="submit"]') ]); // Get research ID from URL const url = page.url(); const match = url.match(/\/research\/(\d+)/); const researchId = match ? match[1] : null; log(`Research started with ID: ${researchId}`, 'success'); // Wait for research to complete or make significant progress log('Waiting for research to generate metrics...', 'info'); const startTime = Date.now(); const maxWaitTime = 60000; // 1 minute max let metricsGenerated = false; // In CI, don't wait long for research while (Date.now() - startTime < 10000 && !metricsGenerated) { // Only wait 10 seconds try { // Check research status with error handling const status = await page.evaluate(() => { const statusEl = document.querySelector('.status-text, .research-status, .status'); const progressEl = document.querySelector('.progress-bar, .search-progress'); const searchCount = document.querySelector('.search-count, .searches-completed'); const tokenCount = document.querySelector('.token-count, .tokens-used'); const statusText = statusEl ? statusEl.textContent.toLowerCase() : ''; const hasProgress = Boolean(progressEl); const hasSearches = Boolean(searchCount); const hasTokens = Boolean(tokenCount); return { text: statusText, isComplete: statusText.includes('complet') || statusText.includes('finish'), hasProgress, hasSearches, hasTokens, hasMetrics: hasSearches || hasTokens }; }); if (status.isComplete) { log('Research completed!', 'success'); metricsGenerated = true; break; } if (status.hasMetrics) { log('Research has generated some metrics', 'info'); metricsGenerated = true; } // Log progress if (status.text) { log(`Status: ${status.text}`, 'info'); } } catch { // Page might be reloading } await new Promise(r => setTimeout(r, 1000)); // Check more frequently } if (!metricsGenerated) { log('Research is still running, but proceeding to check metrics...', 'warning'); } return researchId; } async function captureMetricsDashboard(page, timestamp) { log('Navigating to metrics dashboard...', 'info'); // Navigate to metrics page await page.goto(`${BASE_URL}/metrics`, { waitUntil: 'domcontentloaded' }); // Wait for page to load await page.waitForSelector('body'); await new Promise(r => setTimeout(r, 3000)); // Give time for data to load // Capture all metrics data const metricsData = await page.evaluate(() => { const data = { timestamp: new Date().toISOString(), url: window.location.href, pageTitle: document.title, tokenMetrics: {}, searchMetrics: {}, researchMetrics: {}, chartData: {}, rawContent: '', elements: {} }; // Get page content data.rawContent = document.body.innerText.substring(0, 2000); // Look for token usage metrics const tokenElements = [ { selector: '.total-tokens', label: 'Total Tokens' }, { selector: '.prompt-tokens', label: 'Prompt Tokens' }, { selector: '.completion-tokens', label: 'Completion Tokens' }, { selector: '.token-count', label: 'Token Count' }, { selector: '.usage-value', label: 'Usage Value' }, { selector: '[data-metric="tokens"]', label: 'Token Metric' } ]; tokenElements.forEach(({ selector, label }) => { const el = document.querySelector(selector); if (el) { data.tokenMetrics[label] = el.textContent.trim(); } }); // Look for search metrics const searchElements = [ { selector: '.search-count', label: 'Search Count' }, { selector: '.searches-completed', label: 'Searches Completed' }, { selector: '.search-calls', label: 'Search Calls' }, { selector: '[data-metric="searches"]', label: 'Search Metric' } ]; searchElements.forEach(({ selector, label }) => { const el = document.querySelector(selector); if (el) { data.searchMetrics[label] = el.textContent.trim(); } }); // Look for metric cards or stat boxes const metricCards = document.querySelectorAll('.metric-card, .stat-box, .metric-item, .dashboard-card'); metricCards.forEach(card => { const title = card.querySelector('.metric-title, .card-title, h3, h4, .label'); const value = card.querySelector('.metric-value, .card-value, .value, .number, span'); if (title && value) { const key = title.textContent.trim(); const val = value.textContent.trim(); if (key.toLowerCase().includes('token')) { data.tokenMetrics[key] = val; } else if (key.toLowerCase().includes('search')) { data.searchMetrics[key] = val; } else { data.researchMetrics[key] = val; } } }); // Look for tables with metrics const tables = document.querySelectorAll('table'); tables.forEach((table, index) => { const headers = Array.from(table.querySelectorAll('th')).map(th => th.textContent.trim()); const rows = Array.from(table.querySelectorAll('tbody tr')).map(tr => { return Array.from(tr.querySelectorAll('td')).map(td => td.textContent.trim()); }); if (headers.length > 0 || rows.length > 0) { data.chartData[`table_${index}`] = { headers, rows }; } }); // Look for canvas elements (charts) const canvases = document.querySelectorAll('canvas'); data.elements.canvasCount = canvases.length; // Check for Chart.js data.elements.hasChartJS = typeof Chart !== 'undefined'; return data; }); // Log findings log('\n=== METRICS DASHBOARD ANALYSIS ===', 'section'); log('\nToken Metrics Found:', 'info'); if (Object.keys(metricsData.tokenMetrics).length > 0) { console.log(JSON.stringify(metricsData.tokenMetrics, null, 2)); } else { log('No token metrics found', 'warning'); } log('\nSearch Metrics Found:', 'info'); if (Object.keys(metricsData.searchMetrics).length > 0) { console.log(JSON.stringify(metricsData.searchMetrics, null, 2)); } else { log('No search metrics found', 'warning'); } log('\nOther Research Metrics:', 'info'); if (Object.keys(metricsData.researchMetrics).length > 0) { console.log(JSON.stringify(metricsData.researchMetrics, null, 2)); } else { log('No other metrics found', 'warning'); } log('\nPage Elements:', 'info'); console.log(`- Canvas elements: ${metricsData.elements.canvasCount}`); console.log(`- Chart.js loaded: ${metricsData.elements.hasChartJS}`); console.log(`- Tables found: ${Object.keys(metricsData.chartData).length}`); // Show snippet of raw content log('\nRaw page content preview:', 'info'); console.log(metricsData.rawContent.substring(0, 500) + '...'); // Save full data const dataPath = path.join(__dirname, `metrics-full-data-${timestamp}.json`); await fs.writeFile(dataPath, JSON.stringify(metricsData, null, 2)); log(`Full data saved: ${dataPath}`, 'success'); // Determine success const hasTokenData = Object.keys(metricsData.tokenMetrics).length > 0; const hasSearchData = Object.keys(metricsData.searchMetrics).length > 0; const hasAnyData = hasTokenData || hasSearchData || Object.keys(metricsData.researchMetrics).length > 0; return { hasTokenData, hasSearchData, hasAnyData, metricsData }; } async function testMetricsFullFlow() { const browser = await puppeteer.launch(getPuppeteerLaunchOptions()); const page = await browser.newPage(); page.setDefaultTimeout(TIMEOUT); // Log console errors page.on('console', msg => { if (msg.type() === 'error') { log(`Browser error: ${msg.text()}`, 'error'); } }); try { // Create test user with special characters in password const timestamp = Date.now(); const username = `metrics_${timestamp}`; const password = `test'pass"with$pecial`; log('\n=== FULL METRICS FLOW TEST ===', 'section'); log(`Username: ${username}`, 'info'); log(`Password: ${password} (with special characters)`, 'info'); // Step 1: Register/Login log('\n=== AUTHENTICATION ===', 'section'); const auth = new AuthHelper(page, BASE_URL); await auth.register(username, password); // Step 2: Configure settings for fast test log('\n=== CONFIGURING SETTINGS ===', 'section'); await configureSettingsForTest(page); // Step 3: Run a research log('\n=== RUNNING RESEARCH ===', 'section'); const researchId = await startAndCompleteResearch(page, "What are the top 3 benefits of renewable energy?"); // Step 4: Navigate around to ensure metrics are saved log('\n=== GENERATING ADDITIONAL ACTIVITY ===', 'section'); // Visit history await page.goto(`${BASE_URL}/history`, { waitUntil: 'domcontentloaded' }); log('Visited history page', 'success'); await new Promise(r => setTimeout(r, 2000)); // Go back to research if (researchId) { await page.goto(`${BASE_URL}/research/${researchId}`, { waitUntil: 'domcontentloaded' }); log('Returned to research page', 'success'); await new Promise(r => setTimeout(r, 2000)); } // Step 5: Check metrics dashboard log('\n=== CHECKING METRICS DASHBOARD ===', 'section'); const { hasTokenData, hasSearchData, hasAnyData } = await captureMetricsDashboard(page, timestamp); // Final verdict log('\n=== TEST RESULTS ===', 'section'); if (hasTokenData && hasSearchData) { log('✅ METRICS DASHBOARD IS FULLY WORKING!', 'success'); log('✅ Both token and search metrics are displayed', 'success'); } else if (hasAnyData) { log('⚠️ METRICS DASHBOARD IS PARTIALLY WORKING', 'warning'); log(`Token metrics: ${hasTokenData ? '✅' : '❌'}`, hasTokenData ? 'success' : 'error'); log(`Search metrics: ${hasSearchData ? '✅' : '❌'}`, hasSearchData ? 'success' : 'error'); } else { log('❌ METRICS DASHBOARD IS NOT SHOWING ANY DATA', 'error'); } } catch (error) { log(`\n❌ Test failed: ${error.message}`, 'error'); console.error(error); } finally { // Close browser properly await browser.close(); } } // Run the test testMetricsFullFlow().catch(console.error);