/** * Test Context Overflow Dashboard * Tests the context overflow analytics page functionality */ const puppeteer = require('puppeteer'); const AuthHelper = require('./auth_helper'); const BASE_URL = process.env.BASE_URL || 'http://127.0.0.1:5000'; const HEADLESS = process.env.HEADLESS !== 'false'; const SLOW_MO = parseInt(process.env.SLOW_MO || '0', 10); describe('Context Overflow Dashboard Tests', () => { let browser; let page; let authHelper; beforeAll(async () => { browser = await puppeteer.launch({ headless: HEADLESS, slowMo: SLOW_MO, args: ['--no-sandbox', '--disable-setuid-sandbox'] }); }); afterAll(async () => { if (browser) { await browser.close(); } }); beforeEach(async () => { page = await browser.newPage(); await page.setViewport({ width: 1280, height: 800 }); authHelper = new AuthHelper(page, BASE_URL); // Set longer timeout for slower operations page.setDefaultTimeout(30000); }); afterEach(async () => { if (page) { await page.close(); } }); test('Context overflow dashboard loads and displays data', async () => { console.log('📊 Testing context overflow dashboard...'); // Login first await authHelper.ensureAuthenticated(); // Navigate to context overflow page console.log(' Navigating to context overflow page...'); await page.goto(`${BASE_URL}/metrics/context-overflow`, { waitUntil: 'domcontentloaded', timeout: 30000 }); // Wait for page to load await page.waitForSelector('#context-overflow', { timeout: 10000 }); console.log(' ✅ Context overflow page loaded'); // Sanity-check that the controller JS was extracted out of the // template into its own deferred file. If someone re-inlines the // IIFE later, the deferred-script ordering bug returns silently. const externalScripts = await page.$$eval('script[defer][src]', els => els.map(s => s.getAttribute('src')) ); expect(externalScripts).toContain('/static/js/components/context-overflow.js'); expect(externalScripts).toContain('/static/js/components/context-overflow-shared.js'); // Wait for loading to complete console.log(' Waiting for data to load...'); await page.waitForFunction( () => { const loading = document.getElementById('loading'); const content = document.getElementById('content'); return loading && loading.style.display === 'none' && content && content.style.display !== 'none'; }, { timeout: 15000 } ); console.log(' ✅ Data loading complete'); // Check for main sections const sections = await page.evaluate(() => { const results = { hasOverviewCards: false, hasContextChart: false, hasModelStats: false, hasTruncatedList: false, truncationRate: null, avgTokensLost: null, modelsTracked: null, dataCoverage: null }; // Check overview cards const overviewCards = document.querySelector('.overflow-grid'); results.hasOverviewCards = !!overviewCards; // Get metrics values const truncationRate = document.getElementById('truncation-rate'); if (truncationRate) { results.truncationRate = truncationRate.textContent.trim(); } const avgTokensLost = document.getElementById('avg-tokens-lost'); if (avgTokensLost) { results.avgTokensLost = avgTokensLost.textContent.trim(); } const modelsTracked = document.getElementById('models-tracked'); if (modelsTracked) { results.modelsTracked = modelsTracked.textContent.trim(); } const dataCoverage = document.getElementById('data-coverage'); if (dataCoverage) { results.dataCoverage = dataCoverage.textContent.trim(); } // Check charts const contextChart = document.getElementById('context-chart'); results.hasContextChart = !!contextChart; // Check model stats section const modelStats = document.getElementById('model-stats'); results.hasModelStats = !!modelStats; // Check truncated list section const truncatedList = document.getElementById('truncated-list'); results.hasTruncatedList = !!truncatedList; return results; }); console.log(' 📊 Dashboard sections found:'); console.log(` - Overview cards: ${sections.hasOverviewCards ? '✅' : '❌'}`); console.log(` - Context chart: ${sections.hasContextChart ? '✅' : '❌'}`); console.log(` - Model stats: ${sections.hasModelStats ? '✅' : '❌'}`); console.log(` - Truncated list: ${sections.hasTruncatedList ? '✅' : '❌'}`); console.log(' 📈 Metrics values:'); console.log(` - Truncation rate: ${sections.truncationRate}`); console.log(` - Avg tokens lost: ${sections.avgTokensLost}`); console.log(` - Models tracked: ${sections.modelsTracked}`); console.log(` - Data coverage: ${sections.dataCoverage}`); // Verify all main sections are present expect(sections.hasOverviewCards).toBe(true); expect(sections.hasContextChart).toBe(true); expect(sections.hasModelStats).toBe(true); expect(sections.hasTruncatedList).toBe(true); // Verify metrics are displayed (even if 0) expect(sections.truncationRate).not.toBeNull(); expect(sections.avgTokensLost).not.toBeNull(); expect(sections.modelsTracked).not.toBeNull(); expect(sections.dataCoverage).not.toBeNull(); }); test('Time range selector works', async () => { console.log('🕐 Testing time range selector...'); // Login first await authHelper.ensureAuthenticated(); // Navigate to context overflow page await page.goto(`${BASE_URL}/metrics/context-overflow`, { waitUntil: 'domcontentloaded', timeout: 30000 }); // Wait for page to load await page.waitForSelector('.time-range-selector', { timeout: 10000 }); // Get initial active period const initialPeriod = await page.evaluate(() => { const activeBtn = document.querySelector('.time-range-btn.active'); return activeBtn ? activeBtn.getAttribute('data-period') : null; }); console.log(` Initial period: ${initialPeriod}`); // Try clicking different time periods const periods = ['7d', '30d', '3m', '1y', 'all']; for (const period of periods) { console.log(` Testing period: ${period}`); // Click the time range button await page.click(`[data-period="${period}"]`); // Wait a moment for the request to start await new Promise(resolve => setTimeout(resolve, 500)); // Verify the button is now active const isActive = await page.evaluate((p) => { const btn = document.querySelector(`[data-period="${p}"]`); return btn && btn.classList.contains('active'); }, period); expect(isActive).toBe(true); console.log(` ✅ ${period} button activated`); } }); test('API endpoint returns data', async () => { console.log('🔌 Testing API endpoint...'); // Login first await authHelper.ensureAuthenticated(); // Get cookies for authenticated request const cookies = await page.cookies(); const sessionCookie = cookies.find(c => c.name === 'session'); if (!sessionCookie) { console.log(' ⚠️ No session cookie found'); return; } // Make API request const response = await page.evaluate(async (baseUrl) => { try { const res = await fetch(`${baseUrl}/metrics/api/context-overflow?period=30d`, { method: 'GET', credentials: 'include', headers: { 'Accept': 'application/json' } }); const data = await res.json(); return { status: res.status, ok: res.ok, data }; } catch (error) { return { error: error.message }; } }, BASE_URL); console.log(` API Response status: ${response.status}`); console.log(` API Response OK: ${response.ok}`); if (response.data) { console.log(` API Response status field: ${response.data.status}`); if (response.data.overview) { console.log(' 📊 Overview data:'); console.log(` - Total requests: ${response.data.overview.total_requests}`); console.log(` - Requests with context: ${response.data.overview.requests_with_context_data}`); console.log(` - Truncated requests: ${response.data.overview.truncated_requests}`); console.log(` - Truncation rate: ${response.data.overview.truncation_rate}%`); } if (response.data.model_stats) { console.log(` 📊 Model stats count: ${response.data.model_stats.length}`); } if (response.data.context_limits) { console.log(` 📊 Context limits count: ${response.data.context_limits.length}`); } if (response.data.chart_data) { console.log(` 📊 Chart data points: ${response.data.chart_data.length}`); } } // Verify response structure expect(response.status).toBe(200); expect(response.ok).toBe(true); expect(response.data).toBeDefined(); expect(response.data.status).toBe('success'); expect(response.data.overview).toBeDefined(); }); });