// ============================================================================ // State & Config // ============================================================================ let currentDevice = 'desktop'; // ============================================================================ // UI Functions // ============================================================================ function setDevice(device) { currentDevice = device; document.querySelectorAll('.toggle-option').forEach(el => { el.classList.toggle('active', el.dataset.value === device); }); } function setLoading(isLoading) { const btn = document.getElementById('run-btn'); const spinner = document.getElementById('loading-spinner'); const btnText = btn.querySelector('span'); if (isLoading) { btn.disabled = true; spinner.style.display = 'block'; btnText.textContent = 'Running Test...'; } else { btn.disabled = false; spinner.style.display = 'none'; btnText.textContent = 'Run Test'; } } // ============================================================================ // API Handlers // ============================================================================ async function runTest() { console.log('Run Test triggered'); const urlInput = document.getElementById('test-url'); const url = urlInput.value.trim(); const errorMsg = document.getElementById('error-msg'); const resultsArea = document.getElementById('results-area'); if (!url) { showError('Please enter a valid URL'); return; } try { new URL(url); } catch { showError('Invalid URL format (include http:// or https://)'); return; } // Reset UI errorMsg.style.display = 'none'; resultsArea.classList.remove('visible'); setLoading(true); try { const response = await fetch('/api/run-test', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-user-uuid': getUserUuid() }, body: JSON.stringify({ url: url, isMobile: currentDevice === 'mobile' }) }); if (!response.ok) throw new Error('Test failed to start'); const data = await response.json(); displayResults(data); loadHistory(); // Refresh history } catch (error) { console.error(error); showError('Test execution failed. Check console for details.'); } finally { setLoading(false); } } function displayResults(data) { const resultsArea = document.getElementById('results-area'); // Update Metrics updateMetric('score-perf', Math.round(data.scores.performance), true); updateMetric('metric-lcp', Math.round(data.metrics.lcp)); updateMetric('metric-cls', data.metrics.cls.toFixed(3)); updateMetric('metric-tbt', Math.round(data.metrics.tbt)); // Remove existing actions if any const existingActions = resultsArea.querySelector('.report-actions'); if (existingActions) existingActions.remove(); // Add Report Button const actionsDiv = document.createElement('div'); actionsDiv.className = 'report-actions'; actionsDiv.innerHTML = ` 📄 View Full Report `; resultsArea.appendChild(actionsDiv); // Show waterfall link const waterfallContainer = document.getElementById('waterfall-link-container'); if (waterfallContainer) { waterfallContainer.style.display = 'block'; document.getElementById('view-waterfall').onclick = (e) => { e.preventDefault(); window.open(`/waterfall.html?id=${data.id}`, '_blank'); }; } // Load content breakdown if (typeof renderContentBreakdown === 'function') { renderContentBreakdown(data.id); } resultsArea.classList.add('visible'); // Scroll to results resultsArea.scrollIntoView({ behavior: 'smooth' }); } function updateMetric(id, value, isScore = false) { const el = document.getElementById(id); el.textContent = value; if (isScore) { el.className = 'metric-value'; // Reset if (value >= 90) el.classList.add('score-good'); else if (value >= 50) el.classList.add('score-average'); else el.classList.add('score-poor'); } } function showError(msg) { const el = document.getElementById('error-msg'); el.textContent = msg; el.style.display = 'block'; } async function loadHistory() { try { const response = await fetch('/api/history', { headers: { 'x-user-uuid': getUserUuid() } }); const history = await response.json(); const container = document.getElementById('history-list'); container.innerHTML = '
No tests run yet.
'; return; } history.slice(0, 10).forEach(test => { const date = new Date(test.timestamp).toLocaleString(); const perfScore = Math.round(test.scores.performance); const colorClass = perfScore >= 90 ? 'score-good' : (perfScore >= 50 ? 'score-average' : 'score-poor'); const html = `