From 9b0c740537016bfb608b718913037e12fdc7618a Mon Sep 17 00:00:00 2001 From: you Date: Tue, 24 Mar 2026 05:47:55 +0000 Subject: [PATCH] revert: aggressive coverage interactions dropped score from 42% to 39% The page.evaluate() calls corrupting localStorage and firing fake events caused page error-reloads, losing accumulated coverage. Reverting to the 42% version which was the actual high water mark. --- scripts/collect-frontend-coverage.js | 894 --------------------------- 1 file changed, 894 deletions(-) diff --git a/scripts/collect-frontend-coverage.js b/scripts/collect-frontend-coverage.js index 6e47001..635562e 100644 --- a/scripts/collect-frontend-coverage.js +++ b/scripts/collect-frontend-coverage.js @@ -950,900 +950,6 @@ async function collectCoverage() { await page.waitForTimeout(300); } catch {} - // ══════════════════════════════════════════════ - // DEEP BRANCH COVERAGE — page.evaluate() blitz - // ══════════════════════════════════════════════ - console.log(' [coverage] Deep branch coverage via evaluate...'); - - // --- app.js utility functions with edge cases --- - try { - await page.goto(`${BASE}/#/nodes`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(1000); - await page.evaluate(() => { - // timeAgo edge cases — exercise every branch - if (typeof timeAgo === 'function') { - timeAgo(null); - timeAgo(undefined); - timeAgo(''); - timeAgo('invalid-date'); - timeAgo(new Date().toISOString()); // just now - timeAgo(new Date(Date.now() - 5000).toISOString()); // seconds - timeAgo(new Date(Date.now() - 30000).toISOString()); // 30s - timeAgo(new Date(Date.now() - 60000).toISOString()); // 1 min - timeAgo(new Date(Date.now() - 120000).toISOString()); // 2 min - timeAgo(new Date(Date.now() - 3600000).toISOString()); // 1 hour - timeAgo(new Date(Date.now() - 7200000).toISOString()); // 2 hours - timeAgo(new Date(Date.now() - 86400000).toISOString()); // 1 day - timeAgo(new Date(Date.now() - 172800000).toISOString()); // 2 days - timeAgo(new Date(Date.now() - 604800000).toISOString()); // 1 week - timeAgo(new Date(Date.now() - 2592000000).toISOString()); // 30 days - timeAgo(new Date(Date.now() - 31536000000).toISOString()); // 1 year - } - - // truncate edge cases - if (typeof truncate === 'function') { - truncate('hello world', 5); - truncate('hi', 100); - truncate('', 5); - truncate(null, 5); - truncate(undefined, 5); - truncate('exactly5', 8); - } - - // escapeHtml edge cases - if (typeof escapeHtml === 'function') { - escapeHtml(''); - escapeHtml('& "quotes" '); - escapeHtml(null); - escapeHtml(undefined); - escapeHtml(''); - escapeHtml('normal text'); - } - - // routeTypeName / payloadTypeName / payloadTypeColor — all values + unknown - if (typeof routeTypeName === 'function') { - for (let i = -1; i <= 10; i++) routeTypeName(i); - routeTypeName(undefined); - routeTypeName(null); - routeTypeName(999); - } - if (typeof payloadTypeName === 'function') { - for (let i = -1; i <= 20; i++) payloadTypeName(i); - payloadTypeName(undefined); - payloadTypeName(null); - payloadTypeName(999); - } - if (typeof payloadTypeColor === 'function') { - for (let i = -1; i <= 20; i++) payloadTypeColor(i); - payloadTypeColor(undefined); - payloadTypeColor(null); - } - - // formatHex - if (typeof formatHex === 'function') { - formatHex('48656c6c6f'); - formatHex(''); - formatHex(null); - formatHex('abcdef0123456789'); - formatHex('zzzz'); // non-hex - } - - // createColoredHexDump - if (typeof createColoredHexDump === 'function') { - createColoredHexDump('48656c6c6f20576f726c64', []); - createColoredHexDump('48656c6c6f', [{start: 0, end: 2, label: 'test', cls: 'hex-header'}]); - createColoredHexDump('', []); - createColoredHexDump(null, []); - } - - // buildHexLegend - if (typeof buildHexLegend === 'function') { - buildHexLegend([{label: 'Header', cls: 'hex-header'}, {label: 'Payload', cls: 'hex-payload'}]); - buildHexLegend([]); - buildHexLegend(null); - } - - // debounce - if (typeof debounce === 'function') { - const fn = debounce(() => {}, 100); - fn(); fn(); fn(); - } - - // invalidateApiCache - if (typeof invalidateApiCache === 'function') { - invalidateApiCache(); - invalidateApiCache('/api/nodes'); - invalidateApiCache('/api/packets'); - invalidateApiCache('/nonexistent'); - } - - // apiPerf - if (typeof apiPerf === 'function' || window.apiPerf) { - window.apiPerf(); - } - - // Favorites functions - if (typeof getFavorites === 'function') { - getFavorites(); - } - if (typeof isFavorite === 'function') { - isFavorite('abc123'); - isFavorite(''); - isFavorite(null); - } - if (typeof toggleFavorite === 'function') { - toggleFavorite('test-pubkey-coverage-1'); - toggleFavorite('test-pubkey-coverage-1'); // toggle off - toggleFavorite('test-pubkey-coverage-2'); - } - if (typeof favStar === 'function') { - favStar('abc123', ''); - favStar('abc123', 'extra-class'); - favStar(null, ''); - } - - // syncBadgeColors - if (typeof syncBadgeColors === 'function') { - syncBadgeColors(); - } - - // getHealthThresholds — exercise both infra and non-infra - if (typeof getHealthThresholds === 'function') { - getHealthThresholds('repeater'); - getHealthThresholds('room'); - getHealthThresholds('companion'); - getHealthThresholds('sensor'); - getHealthThresholds('observer'); - getHealthThresholds('unknown'); - getHealthThresholds(null); - } - - // getNodeStatus — exercise all branches - if (typeof getNodeStatus === 'function') { - getNodeStatus('repeater', Date.now()); - getNodeStatus('repeater', Date.now() - 400000000); // stale - getNodeStatus('companion', Date.now()); - getNodeStatus('companion', Date.now() - 100000000); // stale - getNodeStatus('room', Date.now()); - getNodeStatus('sensor', Date.now()); - getNodeStatus('observer', Date.now()); - getNodeStatus('unknown', null); - getNodeStatus('repeater', undefined); - } - - // getTileUrl - if (typeof getTileUrl === 'function') { - document.documentElement.setAttribute('data-theme', 'dark'); - getTileUrl(); - document.documentElement.setAttribute('data-theme', 'light'); - getTileUrl(); - } - }); - await page.waitForTimeout(300); - } catch (e) { console.log(' [coverage] evaluate utility error:', e.message); } - - // --- roles.js deep exercise --- - try { - await page.evaluate(() => { - // ROLE_COLORS, TYPE_COLORS, ROLE_LABELS, ROLE_STYLE, ROLE_EMOJI, ROLE_SORT - // Access all to ensure coverage - var roles = ['repeater', 'companion', 'room', 'sensor', 'observer', 'unknown']; - roles.forEach(function(r) { - var _ = window.ROLE_COLORS[r]; - _ = window.ROLE_LABELS[r]; - _ = window.ROLE_STYLE[r]; - _ = window.ROLE_EMOJI[r]; - }); - // TYPE_COLORS access - var types = ['ADVERT', 'GRP_TXT', 'TXT_MSG', 'ACK', 'REQUEST', 'RESPONSE', 'TRACE', 'PATH', 'ANON_REQ', 'UNKNOWN']; - types.forEach(function(t) { var _ = window.TYPE_COLORS[t]; }); - }); - } catch {} - - // --- WebSocket reconnection --- - console.log(' [coverage] WebSocket reconnect...'); - try { - await page.evaluate(() => { - // Trigger WS close to exercise reconnection logic - if (window._ws) { - window._ws.close(); - } - // Also try direct ws variable if exposed - var wsList = document.querySelectorAll('[class*="connected"]'); - // Simulate a WS message event - try { - var fakeMsg = new MessageEvent('message', { data: JSON.stringify({ type: 'packet', data: {} }) }); - } catch {} - }); - await page.waitForTimeout(3000); // Let reconnect happen - } catch {} - - // --- Keyboard shortcuts --- - console.log(' [coverage] Keyboard shortcuts...'); - try { - await page.keyboard.press('Escape'); - await page.waitForTimeout(200); - await page.keyboard.press('Control+k'); - await page.waitForTimeout(500); - await page.keyboard.press('Escape'); - await page.waitForTimeout(200); - await page.keyboard.press('Meta+k'); - await page.waitForTimeout(300); - await page.keyboard.press('Escape'); - await page.waitForTimeout(200); - } catch {} - - // --- Window resize for responsive branches --- - console.log(' [coverage] Window resize...'); - try { - await page.setViewportSize({ width: 375, height: 667 }); // iPhone SE - await page.waitForTimeout(500); - await page.evaluate(() => window.dispatchEvent(new Event('resize'))); - await page.waitForTimeout(300); - - await page.setViewportSize({ width: 768, height: 1024 }); // iPad - await page.waitForTimeout(500); - await page.evaluate(() => window.dispatchEvent(new Event('resize'))); - await page.waitForTimeout(300); - - await page.setViewportSize({ width: 1920, height: 1080 }); // Desktop - await page.waitForTimeout(500); - await page.evaluate(() => window.dispatchEvent(new Event('resize'))); - await page.waitForTimeout(300); - } catch {} - - // --- Navigate to error/invalid routes --- - console.log(' [coverage] Error routes...'); - try { - await page.goto(`${BASE}/#/nodes/nonexistent-pubkey-12345`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(1500); - await page.goto(`${BASE}/#/packets/nonexistent-hash-abc`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(1500); - await page.goto(`${BASE}/#/observers/nonexistent-obs-id`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(1500); - await page.goto(`${BASE}/#/channels/nonexistent-channel`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(1500); - // node-analytics with bad key - await page.goto(`${BASE}/#/nodes/fake-key-999/analytics`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(1500); - // packet detail standalone - await page.goto(`${BASE}/#/packet/fake-hash-123`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(1500); - // Totally unknown route - await page.goto(`${BASE}/#/this-does-not-exist`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(1000); - } catch {} - - // --- HopResolver exercise --- - console.log(' [coverage] HopResolver...'); - try { - await page.evaluate(() => { - if (window.HopResolver) { - var HR = window.HopResolver; - if (HR.ready) HR.ready(); - if (HR.resolve) { - try { HR.resolve([], 0, 0, 0, 0, null); } catch {} - try { HR.resolve(['AB', 'CD', 'EF'], 37.3, -121.9, 37.4, -121.8, 'obs1'); } catch {} - try { HR.resolve(null, 0, 0, 0, 0, null); } catch {} - } - if (HR.init) { - try { HR.init([], {}); } catch {} - try { HR.init([{public_key: 'abc', name: 'Test', lat: 37.3, lon: -121.9, role: 'repeater'}], {}); } catch {} - } - } - }); - } catch {} - - // --- HopDisplay exercise --- - console.log(' [coverage] HopDisplay...'); - try { - await page.evaluate(() => { - if (window.HopDisplay) { - var HD = window.HopDisplay; - if (HD.renderPath) { - try { HD.renderPath([], {}, {}); } catch {} - try { HD.renderPath(['AB', 'CD'], {AB: {name: 'Node1', conflicts: []}, CD: {name: 'Node2'}}, {}); } catch {} - try { HD.renderPath(['XX'], {XX: {name: 'N', conflicts: [{name: 'C1'}, {name: 'C2'}]}}, {}); } catch {} - } - if (HD.renderHop) { - try { HD.renderHop('AB', {name: 'TestNode', conflicts: []}, {}); } catch {} - try { HD.renderHop('XY', null, {}); } catch {} - try { HD.renderHop('ZZ', {name: 'Multi', conflicts: [{name: 'A'}, {name: 'B'}]}, {globalFallback: true}); } catch {} - } - } - }); - } catch {} - - // --- PacketFilter deep exercise --- - console.log(' [coverage] PacketFilter deep...'); - try { - await page.evaluate(() => { - if (window.PacketFilter) { - var PF = window.PacketFilter; - // compile + match with mock packet data - var mockPkt = { - payload_type: 0, route_type: 0, snr: 5.5, rssi: -70, - hop_count: 2, packet_hash: 'abc123', from_name: 'Node1', - to_name: 'Node2', observer_id: 'obs1', decoded_text: 'hello world', - is_encrypted: false - }; - var exprs = [ - 'type == ADVERT', 'type != ADVERT', 'type == GRP_TXT', - 'snr > 0', 'snr < 0', 'snr >= 5.5', 'snr <= 5.5', 'snr == 5.5', - 'hops > 1', 'hops == 2', 'hops < 3', - 'rssi > -80', 'rssi < -60', 'rssi >= -70', - 'route == FLOOD', 'route == DIRECT', - 'from == "Node1"', 'to == "Node2"', 'observer == "obs1"', - 'has_text', 'is_encrypted', '!is_encrypted', - 'type == ADVERT && snr > 0', 'type == ADVERT || snr > 0', - '!(type == ADVERT)', 'NOT type == GRP_TXT', - '(type == ADVERT || type == GRP_TXT) && snr > 0', - 'type contains ADV', 'from contains Node', - 'hash == "abc123"', 'hash contains abc', - ]; - for (var i = 0; i < exprs.length; i++) { - try { - var fn = PF.compile(exprs[i]); - if (fn) fn(mockPkt); - } catch {} - } - // Bad expressions - var bad = ['', ' ', '@@@', '== ==', '(((', 'type ==', '))', 'type !! ADVERT', null]; - for (var j = 0; j < bad.length; j++) { - try { PF.compile(bad[j]); } catch {} - } - - // Test match with different packet types - for (var t = 0; t <= 15; t++) { - var p = Object.assign({}, mockPkt, {payload_type: t}); - try { - var fn2 = PF.compile('type == ADVERT'); - if (fn2) fn2(p); - } catch {} - } - } - }); - } catch {} - - // --- RegionFilter deep exercise --- - console.log(' [coverage] RegionFilter deep...'); - try { - await page.evaluate(() => { - if (window.RegionFilter) { - var RF = window.RegionFilter; - if (RF.onChange) { - var unsub = RF.onChange(function() {}); - if (typeof unsub === 'function') unsub(); - } - if (RF.getSelected) RF.getSelected(); - if (RF.isEnabled) RF.isEnabled(); - if (RF.setRegions) { - try { RF.setRegions(['US-W', 'US-E', 'EU']); } catch {} - } - if (RF.render) { - try { RF.render(document.createElement('div')); } catch {} - } - } - }); - } catch {} - - // --- Customize deep exercise --- - console.log(' [coverage] Customize deep branches...'); - try { - await page.goto(BASE, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(500); - await safeClick('#customizeToggle'); - await page.waitForTimeout(800); - - // Exercise export/import - await page.evaluate(() => { - // Try to call internal customize functions - // Trigger autoSave by changing theme vars - document.documentElement.style.setProperty('--bg-primary', '#111111'); - document.documentElement.style.setProperty('--bg-secondary', '#222222'); - document.documentElement.style.setProperty('--text-primary', '#ffffff'); - - // Trigger theme-changed to exercise reapplyUserThemeVars - window.dispatchEvent(new Event('theme-changed')); - }); - await page.waitForTimeout(500); - - // Click through ALL customizer tabs again and interact - for (const tab of ['branding', 'theme', 'nodes', 'home', 'export']) { - try { await page.click(`.cust-tab[data-tab="${tab}"]`); await page.waitForTimeout(300); } catch {} - } - - // Try import with bad JSON - try { - await page.click('.cust-tab[data-tab="export"]'); - await page.waitForTimeout(300); - const importArea = await page.$('textarea[data-import], #custImportArea, textarea'); - if (importArea) { - await importArea.fill('{"theme":{"--bg-primary":"#ff0000"}}'); - await page.waitForTimeout(200); - await safeClick('#custImportBtn, [data-action="import"], button:has-text("Import")'); - await page.waitForTimeout(300); - // Bad JSON - await importArea.fill('not json at all {{{'); - await page.waitForTimeout(200); - await safeClick('#custImportBtn, [data-action="import"], button:has-text("Import")'); - await page.waitForTimeout(300); - } - } catch {} - - // Reset all - await safeClick('#custResetPreview'); - await page.waitForTimeout(300); - await safeClick('#custResetUser'); - await page.waitForTimeout(300); - await safeClick('.cust-close'); - await page.waitForTimeout(300); - } catch {} - - // --- Channels deep exercise --- - console.log(' [coverage] Channels deep...'); - try { - await page.goto(`${BASE}/#/channels`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(2000); - - // Exercise channel-internal functions - await page.evaluate(() => { - // Trigger resize handle drag - var handle = document.querySelector('.ch-resize, #chResizeHandle, [class*="resize"]'); - if (handle) { - handle.dispatchEvent(new MouseEvent('mousedown', { clientX: 300, bubbles: true })); - document.dispatchEvent(new MouseEvent('mousemove', { clientX: 200, bubbles: true })); - document.dispatchEvent(new MouseEvent('mouseup', { bubbles: true })); - } - - // Exercise theme observer on channels page - document.documentElement.setAttribute('data-theme', 'dark'); - document.documentElement.setAttribute('data-theme', 'light'); - }); - await page.waitForTimeout(500); - - // Click sidebar items to trigger node tooltips - await clickAll('.ch-sender, .msg-sender, [data-sender]', 3); - await page.waitForTimeout(300); - - // Click back button in channel detail - await safeClick('.ch-back, #chBack, [data-action="back"]'); - await page.waitForTimeout(300); - } catch {} - - // --- Live page deep exercise --- - console.log(' [coverage] Live page deep branches...'); - try { - await page.goto(`${BASE}/#/live`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(3000); - - // Exercise VCR deeply - await page.evaluate(() => { - // Trigger resize - window.dispatchEvent(new Event('resize')); - - // Theme switch on live page - document.documentElement.setAttribute('data-theme', 'dark'); - document.documentElement.setAttribute('data-theme', 'light'); - document.documentElement.setAttribute('data-theme', 'dark'); - }); - await page.waitForTimeout(500); - - // Click VCR rewind button - await safeClick('#vcrRewindBtn'); - await page.waitForTimeout(500); - - // Timeline click at different positions - await page.evaluate(() => { - var canvas = document.getElementById('vcrTimeline'); - if (canvas) { - var rect = canvas.getBoundingClientRect(); - // Click at start - canvas.dispatchEvent(new MouseEvent('click', { clientX: rect.left + 5, clientY: rect.top + rect.height/2, bubbles: true })); - // Click at end - canvas.dispatchEvent(new MouseEvent('click', { clientX: rect.right - 5, clientY: rect.top + rect.height/2, bubbles: true })); - // Click in middle - canvas.dispatchEvent(new MouseEvent('click', { clientX: rect.left + rect.width * 0.3, clientY: rect.top + rect.height/2, bubbles: true })); - } - }); - await page.waitForTimeout(500); - - // Exercise all VCR speed values - for (let i = 0; i < 6; i++) { - await safeClick('#vcrSpeedBtn'); - await page.waitForTimeout(200); - } - - // Toggle every live option - for (const id of ['liveHeatToggle', 'liveGhostToggle', 'liveRealisticToggle', 'liveFavoritesToggle', 'liveMatrixToggle', 'liveMatrixRainToggle']) { - await safeClick(`#${id}`); - await page.waitForTimeout(200); - await safeClick(`#${id}`); - await page.waitForTimeout(200); - } - - // VCR pause/unpause/resume cycle - await safeClick('#vcrPauseBtn'); - await page.waitForTimeout(300); - await safeClick('#vcrPauseBtn'); - await page.waitForTimeout(300); - - // Simulate receiving packets while in different VCR modes - await page.evaluate(() => { - // Fake a WS message to trigger bufferPacket in different modes - if (window._ws || true) { - var fakePackets = [ - { type: 'packet', data: { packet_hash: 'fake1', payload_type: 0, route_type: 0, snr: 5, rssi: -70, hop_count: 1, from_short: 'AA', to_short: 'BB', observer_id: 'obs', ts: Date.now() } }, - { type: 'packet', data: { packet_hash: 'fake2', payload_type: 1, route_type: 1, snr: -3, rssi: -90, hop_count: 3, from_short: 'CC', to_short: 'DD', observer_id: 'obs', ts: Date.now() } }, - ]; - // Dispatch as custom events in case WS listeners are registered - fakePackets.forEach(function(p) { - try { - window.dispatchEvent(new CustomEvent('ws-message', { detail: p })); - } catch {} - }); - } - }); - await page.waitForTimeout(500); - } catch {} - - // --- Audio Lab exercise --- - console.log(' [coverage] Audio Lab...'); - try { - await page.goto(`${BASE}/#/audio-lab`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(2000); - - // Click various audio lab controls - await safeClick('#alabPlay'); - await page.waitForTimeout(300); - await safeClick('#alabStop'); - await page.waitForTimeout(300); - await safeClick('#alabLoop'); - await page.waitForTimeout(300); - - // Change BPM and volume sliders - await page.evaluate(() => { - var bpm = document.getElementById('alabBPM'); - if (bpm) { bpm.value = '80'; bpm.dispatchEvent(new Event('input', { bubbles: true })); } - var vol = document.getElementById('alabVol'); - if (vol) { vol.value = '0.3'; vol.dispatchEvent(new Event('input', { bubbles: true })); } - var voice = document.getElementById('alabVoice'); - if (voice) { voice.value = voice.options[0]?.value || ''; voice.dispatchEvent(new Event('change', { bubbles: true })); } - }); - await page.waitForTimeout(300); - - // Click voice buttons - await clickAll('#alabVoices button, [data-voice]', 5); - await page.waitForTimeout(300); - - // Click sidebar packets - await clickAll('#alabSidebar tr, .alab-pkt-row', 3); - await page.waitForTimeout(300); - - // Click hex bytes - await clickAll('[id^="hexByte"]', 10); - await page.waitForTimeout(300); - } catch {} - - // --- Traces page deep --- - console.log(' [coverage] Traces deep...'); - try { - await page.goto(`${BASE}/#/traces`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(2000); - // Click sort headers - await clickAll('th[data-sort], th.sortable, table thead th', 6); - await page.waitForTimeout(300); - // Click trace rows - await clickAll('table tbody tr', 5); - await page.waitForTimeout(500); - // Click trace detail links - await clickAll('a[href*="trace"], a[href*="packet"]', 3); - await page.waitForTimeout(300); - } catch {} - - // --- Observers page deep --- - console.log(' [coverage] Observers deep...'); - try { - await page.goto(`${BASE}/#/observers`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(2000); - // Click observer cards/rows - await clickAll('table tbody tr, .observer-card', 3); - await page.waitForTimeout(300); - // Sort columns - await clickAll('th[data-sort], th.sortable, table thead th', 5); - await page.waitForTimeout(300); - // Navigate to observer detail - await clickAll('a[href*="observers/"]', 2); - await page.waitForTimeout(1500); - // Cycle days select on detail - await cycleSelect('#obsDaysSelect'); - await page.waitForTimeout(300); - } catch {} - - // --- Observer Detail deep --- - console.log(' [coverage] Observer detail...'); - try { - await page.goto(`${BASE}/#/observers/test-obs`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(2000); - // Click tabs - await clickAll('[data-tab], .tab-btn', 5); - await page.waitForTimeout(300); - // Cycle day selects - await cycleSelect('#obsDaysSelect'); - await cycleSelect('select[data-days]'); - await page.waitForTimeout(300); - } catch {} - - // --- Node Analytics deep --- - console.log(' [coverage] Node Analytics...'); - try { - // Try getting a real node key first - await page.goto(`${BASE}/#/nodes`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(1500); - const nodeKey = await page.$eval('#nodesBody tr td:nth-child(2)', el => el.textContent.trim()).catch(() => 'fake-key'); - await page.goto(`${BASE}/#/nodes/${nodeKey}/analytics`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(2000); - // Click day buttons - for (const days of ['1', '7', '30', '365']) { - try { await page.click(`[data-days="${days}"]`); await page.waitForTimeout(800); } catch {} - } - // Click tabs - await clickAll('[data-tab], .tab-btn', 5); - await page.waitForTimeout(300); - } catch {} - - // --- Perf page deep --- - console.log(' [coverage] Perf deep...'); - try { - await page.goto(`${BASE}/#/perf`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(2000); - await safeClick('#perfRefresh'); - await page.waitForTimeout(1000); - await safeClick('#perfReset'); - await page.waitForTimeout(500); - // Exercise apiPerf from perf page context - await page.evaluate(() => { if (window.apiPerf) window.apiPerf(); }); - await page.waitForTimeout(300); - } catch {} - - // --- localStorage corruption / edge cases --- - console.log(' [coverage] localStorage edge cases...'); - try { - await page.evaluate(() => { - // Corrupt favorites to trigger catch branch - localStorage.setItem('meshcore-favorites', 'not-json'); - if (typeof getFavorites === 'function') getFavorites(); - - // Corrupt user theme - localStorage.setItem('meshcore-user-theme', 'not-json'); - window.dispatchEvent(new Event('theme-changed')); - - // Clean up - localStorage.removeItem('meshcore-favorites'); - localStorage.removeItem('meshcore-user-theme'); - }); - await page.waitForTimeout(500); - } catch {} - - // --- DOMContentLoaded / theme edge cases --- - console.log(' [coverage] Theme edge cases...'); - try { - await page.evaluate(() => { - // Exercise reapplyUserThemeVars with valid theme - localStorage.setItem('meshcore-user-theme', JSON.stringify({ - '--bg-primary': '#1a1a2e', - '--bg-secondary': '#16213e', - '--text-primary': '#e94560' - })); - window.dispatchEvent(new Event('theme-changed')); - - // Switch dark/light rapidly - for (var i = 0; i < 4; i++) { - document.documentElement.setAttribute('data-theme', i % 2 === 0 ? 'dark' : 'light'); - window.dispatchEvent(new Event('theme-changed')); - } - - // Clean up - localStorage.removeItem('meshcore-user-theme'); - }); - await page.waitForTimeout(500); - } catch {} - - // --- Map deep exercise --- - console.log(' [coverage] Map deep branches...'); - try { - await page.goto(`${BASE}/#/map`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(3000); - - // Toggle dark mode on map to exercise tile swap - await page.evaluate(() => { - document.documentElement.setAttribute('data-theme', 'dark'); - window.dispatchEvent(new Event('theme-changed')); - }); - await page.waitForTimeout(500); - await page.evaluate(() => { - document.documentElement.setAttribute('data-theme', 'light'); - window.dispatchEvent(new Event('theme-changed')); - }); - await page.waitForTimeout(500); - - // Zoom events - await page.evaluate(() => { - // Trigger map resize - window.dispatchEvent(new Event('resize')); - }); - await page.waitForTimeout(300); - - // Click legend items if present - await clickAll('.legend-item, .leaflet-legend-item', 5); - await page.waitForTimeout(300); - - // Search on map page - await safeFill('#mapSearch', 'test'); - await page.waitForTimeout(500); - await safeFill('#mapSearch', ''); - await page.waitForTimeout(300); - } catch {} - - // --- Analytics deep per-tab exercise --- - console.log(' [coverage] Analytics deep per-tab...'); - try { - // Distance tab with interactions - await page.goto(`${BASE}/#/analytics?tab=distance`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(2000); - await clickAll('th[data-sort], th.sortable, table thead th', 5); - await page.waitForTimeout(300); - - // RF tab - await page.goto(`${BASE}/#/analytics?tab=rf`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(2000); - await clickAll('th[data-sort], table thead th', 5); - await page.waitForTimeout(300); - - // Channels analytics - await page.goto(`${BASE}/#/analytics?tab=channels`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(2000); - await clickAll('table tbody tr', 3); - await page.waitForTimeout(300); - - // Hash sizes - await page.goto(`${BASE}/#/analytics?tab=hashsizes`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(2000); - - // Overview - await page.goto(`${BASE}/#/analytics?tab=overview`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(2000); - } catch {} - - // --- Packets page deep branches --- - console.log(' [coverage] Packets deep branches...'); - try { - await page.goto(`${BASE}/#/packets`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(2000); - - // Exercise all sort columns - await clickAll('#pktHead th', 10); - await page.waitForTimeout(300); - // Click again for reverse - await clickAll('#pktHead th', 10); - await page.waitForTimeout(300); - - // Scroll to bottom to trigger lazy loading - await page.evaluate(() => { - var table = document.querySelector('#pktBody'); - if (table) table.parentElement.scrollTop = table.parentElement.scrollHeight; - }); - await page.waitForTimeout(1000); - - // Exercise filter with complex expressions - await safeFill('#packetFilterInput', 'type == ADVERT && (snr > 0 || hops > 1) && rssi < -50'); - await page.waitForTimeout(500); - await safeFill('#packetFilterInput', 'from contains "Node" || to contains "Node"'); - await page.waitForTimeout(500); - await safeFill('#packetFilterInput', ''); - await page.waitForTimeout(300); - - // Double-click packet row - try { - const firstRow = await page.$('#pktBody tr'); - if (firstRow) { - await firstRow.dblclick(); - await page.waitForTimeout(500); - } - } catch {} - } catch {} - - // --- Nodes page deep branches --- - console.log(' [coverage] Nodes deep branches...'); - try { - await page.goto(`${BASE}/#/nodes`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {}); - await page.waitForTimeout(2000); - - // Exercise search with special characters - await safeFill('#nodeSearch', '