mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-03-29 08:29:55 +00:00
perf: replace 169 blind sleeps with Playwright waits in coverage script
Remove all 169 waitForTimeout() calls (totaling 104.1s of blind sleeping)
from scripts/collect-frontend-coverage.js:
- Helper functions (safeClick, safeFill, safeSelect, clickAll, cycleSelect):
removed 300-400ms waits after every interaction — Playwright's built-in
actionability checks handle waiting for elements automatically
- Post-navigation waits: removed redundant sleeps after page.goto() calls
that already use waitUntil: 'networkidle'
- Hash-change navigations: replaced waitForTimeout with
waitForLoadState('networkidle') for proper SPA route settling
- Toggle/button waits: removed — event handlers execute synchronously
before click() resolves
- Post-evaluate waits: removed — evaluate() is synchronous
Local benchmark (Windows, sparse test data):
Before: 744.8s
After: 484.8s (35% faster, 260s saved)
On CI runner (ARM Linux with real mesh data), savings will be
proportionally better since most elements exist and the 104s
of blind sleeping was the dominant bottleneck.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -22,7 +22,6 @@ async function collectCoverage() {
|
|||||||
async function safeClick(selector, timeout) {
|
async function safeClick(selector, timeout) {
|
||||||
try {
|
try {
|
||||||
await page.click(selector, { timeout: timeout || 3000 });
|
await page.click(selector, { timeout: timeout || 3000 });
|
||||||
await page.waitForTimeout(300);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +29,6 @@ async function collectCoverage() {
|
|||||||
async function safeFill(selector, text) {
|
async function safeFill(selector, text) {
|
||||||
try {
|
try {
|
||||||
await page.fill(selector, text);
|
await page.fill(selector, text);
|
||||||
await page.waitForTimeout(300);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +36,6 @@ async function collectCoverage() {
|
|||||||
async function safeSelect(selector, value) {
|
async function safeSelect(selector, value) {
|
||||||
try {
|
try {
|
||||||
await page.selectOption(selector, value);
|
await page.selectOption(selector, value);
|
||||||
await page.waitForTimeout(300);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +44,7 @@ async function collectCoverage() {
|
|||||||
try {
|
try {
|
||||||
const els = await page.$$(selector);
|
const els = await page.$$(selector);
|
||||||
for (let i = 0; i < Math.min(els.length, max); i++) {
|
for (let i = 0; i < Math.min(els.length, max); i++) {
|
||||||
try { await els[i].click(); await page.waitForTimeout(300); } catch {}
|
try { await els[i].click(); } catch {}
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
@@ -57,7 +54,7 @@ async function collectCoverage() {
|
|||||||
try {
|
try {
|
||||||
const options = await page.$$eval(`${selector} option`, opts => opts.map(o => o.value));
|
const options = await page.$$eval(`${selector} option`, opts => opts.map(o => o.value));
|
||||||
for (const val of options) {
|
for (const val of options) {
|
||||||
try { await page.selectOption(selector, val); await page.waitForTimeout(400); } catch {}
|
try { await page.selectOption(selector, val); } catch {}
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
@@ -70,34 +67,26 @@ async function collectCoverage() {
|
|||||||
await page.goto(BASE, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(BASE, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.evaluate(() => localStorage.clear()).catch(() => {});
|
await page.evaluate(() => localStorage.clear()).catch(() => {});
|
||||||
await page.goto(`${BASE}/#/home`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${BASE}/#/home`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(1500);
|
|
||||||
|
|
||||||
// Click "I'm new"
|
// Click "I'm new"
|
||||||
await safeClick('#chooseNew');
|
await safeClick('#chooseNew');
|
||||||
await page.waitForTimeout(1000);
|
|
||||||
|
|
||||||
// Now on home page as "new" user — interact with search
|
// Now on home page as "new" user — interact with search
|
||||||
await safeFill('#homeSearch', 'test');
|
await safeFill('#homeSearch', 'test');
|
||||||
await page.waitForTimeout(600);
|
|
||||||
// Click suggest items if any
|
// Click suggest items if any
|
||||||
await clickAll('.suggest-item', 3);
|
await clickAll('.suggest-item', 3);
|
||||||
// Click suggest claim buttons
|
// Click suggest claim buttons
|
||||||
await clickAll('.suggest-claim', 2);
|
await clickAll('.suggest-claim', 2);
|
||||||
await safeFill('#homeSearch', '');
|
await safeFill('#homeSearch', '');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// Click my-node-card elements
|
// Click my-node-card elements
|
||||||
await clickAll('.my-node-card', 3);
|
await clickAll('.my-node-card', 3);
|
||||||
await page.waitForTimeout(300);
|
|
||||||
// Click health/packets buttons on cards
|
// Click health/packets buttons on cards
|
||||||
await clickAll('[data-action="health"]', 2);
|
await clickAll('[data-action="health"]', 2);
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await clickAll('[data-action="packets"]', 2);
|
await clickAll('[data-action="packets"]', 2);
|
||||||
await page.waitForTimeout(500);
|
|
||||||
|
|
||||||
// Click toggle level
|
// Click toggle level
|
||||||
await safeClick('#toggleLevel');
|
await safeClick('#toggleLevel');
|
||||||
await page.waitForTimeout(500);
|
|
||||||
|
|
||||||
// Click FAQ items
|
// Click FAQ items
|
||||||
await clickAll('.faq-q, .question, [class*="accordion"]', 5);
|
await clickAll('.faq-q, .question, [class*="accordion"]', 5);
|
||||||
@@ -117,46 +106,40 @@ async function collectCoverage() {
|
|||||||
// Switch to experienced mode
|
// Switch to experienced mode
|
||||||
await page.evaluate(() => localStorage.clear()).catch(() => {});
|
await page.evaluate(() => localStorage.clear()).catch(() => {});
|
||||||
await page.goto(`${BASE}/#/home`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${BASE}/#/home`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(1000);
|
|
||||||
await safeClick('#chooseExp');
|
await safeClick('#chooseExp');
|
||||||
await page.waitForTimeout(1000);
|
|
||||||
|
|
||||||
// Interact with experienced home page
|
// Interact with experienced home page
|
||||||
await safeFill('#homeSearch', 'a');
|
await safeFill('#homeSearch', 'a');
|
||||||
await page.waitForTimeout(600);
|
|
||||||
await clickAll('.suggest-item', 2);
|
await clickAll('.suggest-item', 2);
|
||||||
await safeFill('#homeSearch', '');
|
await safeFill('#homeSearch', '');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// Click outside to dismiss suggest
|
// Click outside to dismiss suggest
|
||||||
await page.evaluate(() => document.body.click()).catch(() => {});
|
await page.evaluate(() => document.body.click()).catch(() => {});
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
// NODES PAGE
|
// NODES PAGE
|
||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
console.log(' [coverage] Nodes page...');
|
console.log(' [coverage] Nodes page...');
|
||||||
await page.goto(`${BASE}/#/nodes`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${BASE}/#/nodes`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(2000);
|
|
||||||
|
|
||||||
// Sort by EVERY column
|
// Sort by EVERY column
|
||||||
for (const col of ['name', 'public_key', 'role', 'last_seen', 'advert_count']) {
|
for (const col of ['name', 'public_key', 'role', 'last_seen', 'advert_count']) {
|
||||||
try { await page.click(`th[data-sort="${col}"]`); await page.waitForTimeout(300); } catch {}
|
try { await page.click(`th[data-sort="${col}"]`); } catch {}
|
||||||
// Click again for reverse sort
|
// Click again for reverse sort
|
||||||
try { await page.click(`th[data-sort="${col}"]`); await page.waitForTimeout(300); } catch {}
|
try { await page.click(`th[data-sort="${col}"]`); } catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click EVERY role tab
|
// Click EVERY role tab
|
||||||
const roleTabs = await page.$$('.node-tab[data-tab]');
|
const roleTabs = await page.$$('.node-tab[data-tab]');
|
||||||
for (const tab of roleTabs) {
|
for (const tab of roleTabs) {
|
||||||
try { await tab.click(); await page.waitForTimeout(500); } catch {}
|
try { await tab.click(); } catch {}
|
||||||
}
|
}
|
||||||
// Go back to "all"
|
// Go back to "all"
|
||||||
try { await page.click('.node-tab[data-tab="all"]'); await page.waitForTimeout(400); } catch {}
|
try { await page.click('.node-tab[data-tab="all"]'); } catch {}
|
||||||
|
|
||||||
// Click EVERY status filter
|
// Click EVERY status filter
|
||||||
for (const status of ['active', 'stale', 'all']) {
|
for (const status of ['active', 'stale', 'all']) {
|
||||||
try { await page.click(`#nodeStatusFilter .btn[data-status="${status}"]`); await page.waitForTimeout(400); } catch {}
|
try { await page.click(`#nodeStatusFilter .btn[data-status="${status}"]`); } catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cycle EVERY Last Heard option
|
// Cycle EVERY Last Heard option
|
||||||
@@ -164,33 +147,28 @@ async function collectCoverage() {
|
|||||||
|
|
||||||
// Search
|
// Search
|
||||||
await safeFill('#nodeSearch', 'test');
|
await safeFill('#nodeSearch', 'test');
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await safeFill('#nodeSearch', '');
|
await safeFill('#nodeSearch', '');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// Click node rows to open side pane — try multiple
|
// Click node rows to open side pane — try multiple
|
||||||
const nodeRows = await page.$$('#nodesBody tr');
|
const nodeRows = await page.$$('#nodesBody tr');
|
||||||
for (let i = 0; i < Math.min(nodeRows.length, 4); i++) {
|
for (let i = 0; i < Math.min(nodeRows.length, 4); i++) {
|
||||||
try { await nodeRows[i].click(); await page.waitForTimeout(600); } catch {}
|
try { await nodeRows[i].click(); } catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In side pane — click detail/analytics links
|
// In side pane — click detail/analytics links
|
||||||
await safeClick('a[href*="/nodes/"]', 2000);
|
await safeClick('a[href*="/nodes/"]', 2000);
|
||||||
await page.waitForTimeout(1500);
|
|
||||||
// Click fav star
|
// Click fav star
|
||||||
await clickAll('.fav-star', 2);
|
await clickAll('.fav-star', 2);
|
||||||
|
|
||||||
// On node detail page — interact
|
// On node detail page — interact
|
||||||
// Click back button
|
// Click back button
|
||||||
await safeClick('#nodeBackBtn');
|
await safeClick('#nodeBackBtn');
|
||||||
await page.waitForTimeout(500);
|
|
||||||
|
|
||||||
// Navigate to a node detail page via hash
|
// Navigate to a node detail page via hash
|
||||||
try {
|
try {
|
||||||
const firstNodeKey = await page.$eval('#nodesBody tr td:nth-child(2)', el => el.textContent.trim());
|
const firstNodeKey = await page.$eval('#nodesBody tr td:nth-child(2)', el => el.textContent.trim());
|
||||||
if (firstNodeKey) {
|
if (firstNodeKey) {
|
||||||
await page.goto(`${BASE}/#/nodes/${firstNodeKey}`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${BASE}/#/nodes/${firstNodeKey}`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(2000);
|
|
||||||
|
|
||||||
// Click tabs on detail page
|
// Click tabs on detail page
|
||||||
await clickAll('.tab-btn, [data-tab]', 10);
|
await clickAll('.tab-btn, [data-tab]', 10);
|
||||||
@@ -204,7 +182,7 @@ async function collectCoverage() {
|
|||||||
|
|
||||||
// Click node analytics day buttons
|
// Click node analytics day buttons
|
||||||
for (const days of ['1', '7', '30', '365']) {
|
for (const days of ['1', '7', '30', '365']) {
|
||||||
try { await page.click(`[data-days="${days}"]`); await page.waitForTimeout(800); } catch {}
|
try { await page.click(`[data-days="${days}"]`); } catch {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
@@ -214,7 +192,6 @@ async function collectCoverage() {
|
|||||||
const firstKey = await page.$eval('#nodesBody tr td:nth-child(2)', el => el.textContent.trim()).catch(() => null);
|
const firstKey = await page.$eval('#nodesBody tr td:nth-child(2)', el => el.textContent.trim()).catch(() => null);
|
||||||
if (firstKey) {
|
if (firstKey) {
|
||||||
await page.goto(`${BASE}/#/nodes/${firstKey}?scroll=paths`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${BASE}/#/nodes/${firstKey}?scroll=paths`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(1500);
|
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
@@ -223,11 +200,9 @@ async function collectCoverage() {
|
|||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
console.log(' [coverage] Packets page...');
|
console.log(' [coverage] Packets page...');
|
||||||
await page.goto(`${BASE}/#/packets`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${BASE}/#/packets`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(2000);
|
|
||||||
|
|
||||||
// Open filter bar
|
// Open filter bar
|
||||||
await safeClick('#filterToggleBtn');
|
await safeClick('#filterToggleBtn');
|
||||||
await page.waitForTimeout(500);
|
|
||||||
|
|
||||||
// Type various filter expressions
|
// Type various filter expressions
|
||||||
const filterExprs = [
|
const filterExprs = [
|
||||||
@@ -237,7 +212,6 @@ async function collectCoverage() {
|
|||||||
];
|
];
|
||||||
for (const expr of filterExprs) {
|
for (const expr of filterExprs) {
|
||||||
await safeFill('#packetFilterInput', expr);
|
await safeFill('#packetFilterInput', expr);
|
||||||
await page.waitForTimeout(500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cycle ALL time window options
|
// Cycle ALL time window options
|
||||||
@@ -245,70 +219,52 @@ async function collectCoverage() {
|
|||||||
|
|
||||||
// Toggle group by hash
|
// Toggle group by hash
|
||||||
await safeClick('#fGroup');
|
await safeClick('#fGroup');
|
||||||
await page.waitForTimeout(600);
|
|
||||||
await safeClick('#fGroup');
|
await safeClick('#fGroup');
|
||||||
await page.waitForTimeout(600);
|
|
||||||
|
|
||||||
// Toggle My Nodes filter
|
// Toggle My Nodes filter
|
||||||
await safeClick('#fMyNodes');
|
await safeClick('#fMyNodes');
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await safeClick('#fMyNodes');
|
await safeClick('#fMyNodes');
|
||||||
await page.waitForTimeout(500);
|
|
||||||
|
|
||||||
// Click observer menu trigger
|
// Click observer menu trigger
|
||||||
await safeClick('#observerTrigger');
|
await safeClick('#observerTrigger');
|
||||||
await page.waitForTimeout(400);
|
|
||||||
// Click items in observer menu
|
// Click items in observer menu
|
||||||
await clickAll('#observerMenu input[type="checkbox"]', 5);
|
await clickAll('#observerMenu input[type="checkbox"]', 5);
|
||||||
await safeClick('#observerTrigger');
|
await safeClick('#observerTrigger');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// Click type filter trigger
|
// Click type filter trigger
|
||||||
await safeClick('#typeTrigger');
|
await safeClick('#typeTrigger');
|
||||||
await page.waitForTimeout(400);
|
|
||||||
await clickAll('#typeMenu input[type="checkbox"]', 5);
|
await clickAll('#typeMenu input[type="checkbox"]', 5);
|
||||||
await safeClick('#typeTrigger');
|
await safeClick('#typeTrigger');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// Hash input
|
// Hash input
|
||||||
await safeFill('#fHash', 'abc123');
|
await safeFill('#fHash', 'abc123');
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await safeFill('#fHash', '');
|
await safeFill('#fHash', '');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// Node filter
|
// Node filter
|
||||||
await safeFill('#fNode', 'test');
|
await safeFill('#fNode', 'test');
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await clickAll('.node-filter-option', 3);
|
await clickAll('.node-filter-option', 3);
|
||||||
await safeFill('#fNode', '');
|
await safeFill('#fNode', '');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// Observer sort
|
// Observer sort
|
||||||
await cycleSelect('#fObsSort');
|
await cycleSelect('#fObsSort');
|
||||||
|
|
||||||
// Column toggle menu
|
// Column toggle menu
|
||||||
await safeClick('#colToggleBtn');
|
await safeClick('#colToggleBtn');
|
||||||
await page.waitForTimeout(400);
|
|
||||||
await clickAll('#colToggleMenu input[type="checkbox"]', 8);
|
await clickAll('#colToggleMenu input[type="checkbox"]', 8);
|
||||||
await safeClick('#colToggleBtn');
|
await safeClick('#colToggleBtn');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// Hex hash toggle
|
// Hex hash toggle
|
||||||
await safeClick('#hexHashToggle');
|
await safeClick('#hexHashToggle');
|
||||||
await page.waitForTimeout(400);
|
|
||||||
await safeClick('#hexHashToggle');
|
await safeClick('#hexHashToggle');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// Pause button
|
// Pause button
|
||||||
await safeClick('#pktPauseBtn');
|
await safeClick('#pktPauseBtn');
|
||||||
await page.waitForTimeout(400);
|
|
||||||
await safeClick('#pktPauseBtn');
|
await safeClick('#pktPauseBtn');
|
||||||
await page.waitForTimeout(400);
|
|
||||||
|
|
||||||
// Click packet rows to open detail pane
|
// Click packet rows to open detail pane
|
||||||
const pktRows = await page.$$('#pktBody tr');
|
const pktRows = await page.$$('#pktBody tr');
|
||||||
for (let i = 0; i < Math.min(pktRows.length, 5); i++) {
|
for (let i = 0; i < Math.min(pktRows.length, 5); i++) {
|
||||||
try { await pktRows[i].click(); await page.waitForTimeout(500); } catch {}
|
try { await pktRows[i].click(); } catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize handle drag simulation
|
// Resize handle drag simulation
|
||||||
@@ -321,63 +277,50 @@ async function collectCoverage() {
|
|||||||
document.dispatchEvent(new MouseEvent('mouseup', { bubbles: true }));
|
document.dispatchEvent(new MouseEvent('mouseup', { bubbles: true }));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await page.waitForTimeout(300);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// Click outside filter menus to close them
|
// Click outside filter menus to close them
|
||||||
try {
|
try {
|
||||||
await page.evaluate(() => document.body.click());
|
await page.evaluate(() => document.body.click());
|
||||||
await page.waitForTimeout(300);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// Navigate to specific packet by hash
|
// Navigate to specific packet by hash
|
||||||
await page.goto(`${BASE}/#/packets/deadbeef`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${BASE}/#/packets/deadbeef`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(1500);
|
|
||||||
|
|
||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
// MAP PAGE
|
// MAP PAGE
|
||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
console.log(' [coverage] Map page...');
|
console.log(' [coverage] Map page...');
|
||||||
await page.goto(`${BASE}/#/map`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${BASE}/#/map`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(3000);
|
|
||||||
|
|
||||||
// Toggle controls panel
|
// Toggle controls panel
|
||||||
await safeClick('#mapControlsToggle');
|
await safeClick('#mapControlsToggle');
|
||||||
await page.waitForTimeout(500);
|
|
||||||
|
|
||||||
// Toggle each role checkbox on/off
|
// Toggle each role checkbox on/off
|
||||||
try {
|
try {
|
||||||
const roleChecks = await page.$$('#mcRoleChecks input[type="checkbox"]');
|
const roleChecks = await page.$$('#mcRoleChecks input[type="checkbox"]');
|
||||||
for (const cb of roleChecks) {
|
for (const cb of roleChecks) {
|
||||||
try { await cb.click(); await page.waitForTimeout(300); } catch {}
|
try { await cb.click(); } catch {}
|
||||||
try { await cb.click(); await page.waitForTimeout(300); } catch {}
|
try { await cb.click(); } catch {}
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// Toggle clusters, heatmap, neighbors, hash labels
|
// Toggle clusters, heatmap, neighbors, hash labels
|
||||||
await safeClick('#mcClusters');
|
await safeClick('#mcClusters');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await safeClick('#mcClusters');
|
await safeClick('#mcClusters');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await safeClick('#mcHeatmap');
|
await safeClick('#mcHeatmap');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await safeClick('#mcHeatmap');
|
await safeClick('#mcHeatmap');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await safeClick('#mcNeighbors');
|
await safeClick('#mcNeighbors');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await safeClick('#mcNeighbors');
|
await safeClick('#mcNeighbors');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await safeClick('#mcHashLabels');
|
await safeClick('#mcHashLabels');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await safeClick('#mcHashLabels');
|
await safeClick('#mcHashLabels');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// Last heard dropdown on map
|
// Last heard dropdown on map
|
||||||
await cycleSelect('#mcLastHeard');
|
await cycleSelect('#mcLastHeard');
|
||||||
|
|
||||||
// Status filter buttons on map
|
// Status filter buttons on map
|
||||||
for (const st of ['active', 'stale', 'all']) {
|
for (const st of ['active', 'stale', 'all']) {
|
||||||
try { await page.click(`#mcStatusFilter .btn[data-status="${st}"]`); await page.waitForTimeout(400); } catch {}
|
try { await page.click(`#mcStatusFilter .btn[data-status="${st}"]`); } catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click jump buttons (region jumps)
|
// Click jump buttons (region jumps)
|
||||||
@@ -392,78 +335,61 @@ async function collectCoverage() {
|
|||||||
|
|
||||||
// Zoom controls
|
// Zoom controls
|
||||||
await safeClick('.leaflet-control-zoom-in');
|
await safeClick('.leaflet-control-zoom-in');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await safeClick('.leaflet-control-zoom-out');
|
await safeClick('.leaflet-control-zoom-out');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// Toggle dark mode while on map (triggers tile layer swap)
|
// Toggle dark mode while on map (triggers tile layer swap)
|
||||||
await safeClick('#darkModeToggle');
|
await safeClick('#darkModeToggle');
|
||||||
await page.waitForTimeout(800);
|
|
||||||
await safeClick('#darkModeToggle');
|
await safeClick('#darkModeToggle');
|
||||||
await page.waitForTimeout(500);
|
|
||||||
|
|
||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
// ANALYTICS PAGE
|
// ANALYTICS PAGE
|
||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
console.log(' [coverage] Analytics page...');
|
console.log(' [coverage] Analytics page...');
|
||||||
await page.goto(`${BASE}/#/analytics`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${BASE}/#/analytics`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(3000);
|
|
||||||
|
|
||||||
// Click EVERY analytics tab
|
// Click EVERY analytics tab
|
||||||
const analyticsTabs = ['overview', 'rf', 'topology', 'channels', 'hashsizes', 'collisions', 'subpaths', 'nodes', 'distance'];
|
const analyticsTabs = ['overview', 'rf', 'topology', 'channels', 'hashsizes', 'collisions', 'subpaths', 'nodes', 'distance'];
|
||||||
for (const tabName of analyticsTabs) {
|
for (const tabName of analyticsTabs) {
|
||||||
try {
|
try {
|
||||||
await page.click(`#analyticsTabs [data-tab="${tabName}"]`, { timeout: 2000 });
|
await page.click(`#analyticsTabs [data-tab="${tabName}"]`, { timeout: 2000 });
|
||||||
await page.waitForTimeout(1500);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// On topology tab — click observer selector buttons
|
// On topology tab — click observer selector buttons
|
||||||
try {
|
try {
|
||||||
await page.click('#analyticsTabs [data-tab="topology"]', { timeout: 2000 });
|
await page.click('#analyticsTabs [data-tab="topology"]', { timeout: 2000 });
|
||||||
await page.waitForTimeout(1500);
|
|
||||||
await clickAll('#obsSelector .tab-btn', 5);
|
await clickAll('#obsSelector .tab-btn', 5);
|
||||||
// Click the "All Observers" button
|
// Click the "All Observers" button
|
||||||
await safeClick('[data-obs="__all"]');
|
await safeClick('[data-obs="__all"]');
|
||||||
await page.waitForTimeout(500);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// On collisions tab — click navigate rows
|
// On collisions tab — click navigate rows
|
||||||
try {
|
try {
|
||||||
await page.click('#analyticsTabs [data-tab="collisions"]', { timeout: 2000 });
|
await page.click('#analyticsTabs [data-tab="collisions"]', { timeout: 2000 });
|
||||||
await page.waitForTimeout(1500);
|
|
||||||
await clickAll('tr[data-action="navigate"]', 3);
|
await clickAll('tr[data-action="navigate"]', 3);
|
||||||
await page.waitForTimeout(500);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// On subpaths tab — click rows
|
// On subpaths tab — click rows
|
||||||
try {
|
try {
|
||||||
await page.click('#analyticsTabs [data-tab="subpaths"]', { timeout: 2000 });
|
await page.click('#analyticsTabs [data-tab="subpaths"]', { timeout: 2000 });
|
||||||
await page.waitForTimeout(1500);
|
|
||||||
await clickAll('tr[data-action="navigate"]', 3);
|
await clickAll('tr[data-action="navigate"]', 3);
|
||||||
await page.waitForTimeout(500);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// On nodes tab — click sortable headers
|
// On nodes tab — click sortable headers
|
||||||
try {
|
try {
|
||||||
await page.click('#analyticsTabs [data-tab="nodes"]', { timeout: 2000 });
|
await page.click('#analyticsTabs [data-tab="nodes"]', { timeout: 2000 });
|
||||||
await page.waitForTimeout(1500);
|
|
||||||
await clickAll('.analytics-table th', 8);
|
await clickAll('.analytics-table th', 8);
|
||||||
await page.waitForTimeout(300);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// Deep-link to each analytics tab via URL
|
// Deep-link to each analytics tab via URL
|
||||||
for (const tab of analyticsTabs) {
|
for (const tab of analyticsTabs) {
|
||||||
await page.goto(`${BASE}/#/analytics?tab=${tab}`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${BASE}/#/analytics?tab=${tab}`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(1500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Region filter on analytics
|
// Region filter on analytics
|
||||||
try {
|
try {
|
||||||
await page.click('#analyticsRegionFilter');
|
await page.click('#analyticsRegionFilter');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await clickAll('#analyticsRegionFilter input[type="checkbox"]', 3);
|
await clickAll('#analyticsRegionFilter input[type="checkbox"]', 3);
|
||||||
await page.waitForTimeout(300);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
@@ -471,19 +397,16 @@ async function collectCoverage() {
|
|||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
console.log(' [coverage] Customizer...');
|
console.log(' [coverage] Customizer...');
|
||||||
await page.goto(BASE, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(BASE, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await safeClick('#customizeToggle');
|
await safeClick('#customizeToggle');
|
||||||
await page.waitForTimeout(1000);
|
|
||||||
|
|
||||||
// Click EVERY customizer tab
|
// Click EVERY customizer tab
|
||||||
for (const tab of ['branding', 'theme', 'nodes', 'home', 'export']) {
|
for (const tab of ['branding', 'theme', 'nodes', 'home', 'export']) {
|
||||||
try { await page.click(`.cust-tab[data-tab="${tab}"]`); await page.waitForTimeout(500); } catch {}
|
try { await page.click(`.cust-tab[data-tab="${tab}"]`); } catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// On branding tab — change text inputs
|
// On branding tab — change text inputs
|
||||||
try {
|
try {
|
||||||
await page.click('.cust-tab[data-tab="branding"]');
|
await page.click('.cust-tab[data-tab="branding"]');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await safeFill('input[data-key="branding.siteName"]', 'Test Site');
|
await safeFill('input[data-key="branding.siteName"]', 'Test Site');
|
||||||
await safeFill('input[data-key="branding.tagline"]', 'Test Tagline');
|
await safeFill('input[data-key="branding.tagline"]', 'Test Tagline');
|
||||||
await safeFill('input[data-key="branding.logoUrl"]', 'https://example.com/logo.png');
|
await safeFill('input[data-key="branding.logoUrl"]', 'https://example.com/logo.png');
|
||||||
@@ -493,10 +416,9 @@ async function collectCoverage() {
|
|||||||
// On theme tab — click EVERY preset
|
// On theme tab — click EVERY preset
|
||||||
try {
|
try {
|
||||||
await page.click('.cust-tab[data-tab="theme"]');
|
await page.click('.cust-tab[data-tab="theme"]');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
const presets = await page.$$('.cust-preset-btn[data-preset]');
|
const presets = await page.$$('.cust-preset-btn[data-preset]');
|
||||||
for (const preset of presets) {
|
for (const preset of presets) {
|
||||||
try { await preset.click(); await page.waitForTimeout(400); } catch {}
|
try { await preset.click(); } catch {}
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
@@ -509,7 +431,6 @@ async function collectCoverage() {
|
|||||||
el.value = '#ff5500';
|
el.value = '#ff5500';
|
||||||
el.dispatchEvent(new Event('input', { bubbles: true }));
|
el.dispatchEvent(new Event('input', { bubbles: true }));
|
||||||
});
|
});
|
||||||
await page.waitForTimeout(200);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
@@ -522,7 +443,6 @@ async function collectCoverage() {
|
|||||||
// On nodes tab — change node color inputs
|
// On nodes tab — change node color inputs
|
||||||
try {
|
try {
|
||||||
await page.click('.cust-tab[data-tab="nodes"]');
|
await page.click('.cust-tab[data-tab="nodes"]');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
const nodeColors = await page.$$('input[type="color"][data-node]');
|
const nodeColors = await page.$$('input[type="color"][data-node]');
|
||||||
for (let i = 0; i < Math.min(nodeColors.length, 3); i++) {
|
for (let i = 0; i < Math.min(nodeColors.length, 3); i++) {
|
||||||
try {
|
try {
|
||||||
@@ -530,7 +450,6 @@ async function collectCoverage() {
|
|||||||
el.value = '#00ff00';
|
el.value = '#00ff00';
|
||||||
el.dispatchEvent(new Event('input', { bubbles: true }));
|
el.dispatchEvent(new Event('input', { bubbles: true }));
|
||||||
});
|
});
|
||||||
await page.waitForTimeout(200);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
// Type color inputs
|
// Type color inputs
|
||||||
@@ -541,7 +460,6 @@ async function collectCoverage() {
|
|||||||
el.value = '#0000ff';
|
el.value = '#0000ff';
|
||||||
el.dispatchEvent(new Event('input', { bubbles: true }));
|
el.dispatchEvent(new Event('input', { bubbles: true }));
|
||||||
});
|
});
|
||||||
await page.waitForTimeout(200);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
@@ -549,7 +467,6 @@ async function collectCoverage() {
|
|||||||
// On home tab — edit home customization fields
|
// On home tab — edit home customization fields
|
||||||
try {
|
try {
|
||||||
await page.click('.cust-tab[data-tab="home"]');
|
await page.click('.cust-tab[data-tab="home"]');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await safeFill('input[data-key="home.heroTitle"]', 'Test Hero');
|
await safeFill('input[data-key="home.heroTitle"]', 'Test Hero');
|
||||||
await safeFill('input[data-key="home.heroSubtitle"]', 'Test Subtitle');
|
await safeFill('input[data-key="home.heroSubtitle"]', 'Test Subtitle');
|
||||||
// Edit journey steps
|
// Edit journey steps
|
||||||
@@ -564,7 +481,6 @@ async function collectCoverage() {
|
|||||||
for (let i = 0; i < Math.min(stepTitles.length, 2); i++) {
|
for (let i = 0; i < Math.min(stepTitles.length, 2); i++) {
|
||||||
try {
|
try {
|
||||||
await stepTitles[i].fill('Test Step ' + i);
|
await stepTitles[i].fill('Test Step ' + i);
|
||||||
await page.waitForTimeout(200);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
@@ -572,27 +488,22 @@ async function collectCoverage() {
|
|||||||
// On export tab
|
// On export tab
|
||||||
try {
|
try {
|
||||||
await page.click('.cust-tab[data-tab="export"]');
|
await page.click('.cust-tab[data-tab="export"]');
|
||||||
await page.waitForTimeout(500);
|
|
||||||
// Click export/import buttons if present
|
// Click export/import buttons if present
|
||||||
await clickAll('.cust-panel[data-panel="export"] button', 3);
|
await clickAll('.cust-panel[data-panel="export"] button', 3);
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// Reset preview and user theme
|
// Reset preview and user theme
|
||||||
await safeClick('#custResetPreview');
|
await safeClick('#custResetPreview');
|
||||||
await page.waitForTimeout(400);
|
|
||||||
await safeClick('#custResetUser');
|
await safeClick('#custResetUser');
|
||||||
await page.waitForTimeout(400);
|
|
||||||
|
|
||||||
// Close customizer
|
// Close customizer
|
||||||
await safeClick('.cust-close');
|
await safeClick('.cust-close');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
// CHANNELS PAGE
|
// CHANNELS PAGE
|
||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
console.log(' [coverage] Channels page...');
|
console.log(' [coverage] Channels page...');
|
||||||
await page.goto(`${BASE}/#/channels`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${BASE}/#/channels`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(2000);
|
|
||||||
// Click channel rows/items
|
// Click channel rows/items
|
||||||
await clickAll('.channel-item, .channel-row, .channel-card', 3);
|
await clickAll('.channel-item, .channel-row, .channel-card', 3);
|
||||||
await clickAll('table tbody tr', 3);
|
await clickAll('table tbody tr', 3);
|
||||||
@@ -602,7 +513,6 @@ async function collectCoverage() {
|
|||||||
const channelHash = await page.$eval('table tbody tr td:first-child', el => el.textContent.trim()).catch(() => null);
|
const channelHash = await page.$eval('table tbody tr td:first-child', el => el.textContent.trim()).catch(() => null);
|
||||||
if (channelHash) {
|
if (channelHash) {
|
||||||
await page.goto(`${BASE}/#/channels/${channelHash}`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${BASE}/#/channels/${channelHash}`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(1500);
|
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
@@ -611,78 +521,53 @@ async function collectCoverage() {
|
|||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
console.log(' [coverage] Live page...');
|
console.log(' [coverage] Live page...');
|
||||||
await page.goto(`${BASE}/#/live`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${BASE}/#/live`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(3000);
|
|
||||||
|
|
||||||
// VCR controls
|
// VCR controls
|
||||||
await safeClick('#vcrPauseBtn');
|
await safeClick('#vcrPauseBtn');
|
||||||
await page.waitForTimeout(400);
|
|
||||||
await safeClick('#vcrPauseBtn');
|
await safeClick('#vcrPauseBtn');
|
||||||
await page.waitForTimeout(400);
|
|
||||||
|
|
||||||
// VCR speed cycle
|
// VCR speed cycle
|
||||||
await safeClick('#vcrSpeedBtn');
|
await safeClick('#vcrSpeedBtn');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await safeClick('#vcrSpeedBtn');
|
await safeClick('#vcrSpeedBtn');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await safeClick('#vcrSpeedBtn');
|
await safeClick('#vcrSpeedBtn');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// VCR mode / missed
|
// VCR mode / missed
|
||||||
await safeClick('#vcrMissed');
|
await safeClick('#vcrMissed');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// VCR prompt buttons
|
// VCR prompt buttons
|
||||||
await safeClick('#vcrPromptReplay');
|
await safeClick('#vcrPromptReplay');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await safeClick('#vcrPromptSkip');
|
await safeClick('#vcrPromptSkip');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// Toggle visualization options
|
// Toggle visualization options
|
||||||
await safeClick('#liveHeatToggle');
|
await safeClick('#liveHeatToggle');
|
||||||
await page.waitForTimeout(400);
|
|
||||||
await safeClick('#liveHeatToggle');
|
await safeClick('#liveHeatToggle');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
await safeClick('#liveGhostToggle');
|
await safeClick('#liveGhostToggle');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await safeClick('#liveGhostToggle');
|
await safeClick('#liveGhostToggle');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
await safeClick('#liveRealisticToggle');
|
await safeClick('#liveRealisticToggle');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await safeClick('#liveRealisticToggle');
|
await safeClick('#liveRealisticToggle');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
await safeClick('#liveFavoritesToggle');
|
await safeClick('#liveFavoritesToggle');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await safeClick('#liveFavoritesToggle');
|
await safeClick('#liveFavoritesToggle');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
await safeClick('#liveMatrixToggle');
|
await safeClick('#liveMatrixToggle');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await safeClick('#liveMatrixToggle');
|
await safeClick('#liveMatrixToggle');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
await safeClick('#liveMatrixRainToggle');
|
await safeClick('#liveMatrixRainToggle');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await safeClick('#liveMatrixRainToggle');
|
await safeClick('#liveMatrixRainToggle');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// Audio toggle and controls
|
// Audio toggle and controls
|
||||||
await safeClick('#liveAudioToggle');
|
await safeClick('#liveAudioToggle');
|
||||||
await page.waitForTimeout(400);
|
|
||||||
try {
|
try {
|
||||||
await page.fill('#audioBpmSlider', '120');
|
await page.fill('#audioBpmSlider', '120');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
// Dispatch input event on slider
|
// Dispatch input event on slider
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
const s = document.getElementById('audioBpmSlider');
|
const s = document.getElementById('audioBpmSlider');
|
||||||
if (s) { s.value = '140'; s.dispatchEvent(new Event('input', { bubbles: true })); }
|
if (s) { s.value = '140'; s.dispatchEvent(new Event('input', { bubbles: true })); }
|
||||||
});
|
});
|
||||||
await page.waitForTimeout(300);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
await safeClick('#liveAudioToggle');
|
await safeClick('#liveAudioToggle');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// VCR timeline click
|
// VCR timeline click
|
||||||
try {
|
try {
|
||||||
@@ -697,7 +582,6 @@ async function collectCoverage() {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await page.waitForTimeout(500);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// VCR LCD canvas
|
// VCR LCD canvas
|
||||||
@@ -706,7 +590,6 @@ async function collectCoverage() {
|
|||||||
const canvas = document.getElementById('vcrLcdCanvas');
|
const canvas = document.getElementById('vcrLcdCanvas');
|
||||||
if (canvas) canvas.getContext('2d');
|
if (canvas) canvas.getContext('2d');
|
||||||
});
|
});
|
||||||
await page.waitForTimeout(300);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// Resize the live page panel
|
// Resize the live page panel
|
||||||
@@ -714,7 +597,6 @@ async function collectCoverage() {
|
|||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
window.dispatchEvent(new Event('resize'));
|
window.dispatchEvent(new Event('resize'));
|
||||||
});
|
});
|
||||||
await page.waitForTimeout(300);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
@@ -722,7 +604,6 @@ async function collectCoverage() {
|
|||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
console.log(' [coverage] Traces page...');
|
console.log(' [coverage] Traces page...');
|
||||||
await page.goto(`${BASE}/#/traces`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${BASE}/#/traces`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(2000);
|
|
||||||
await clickAll('table tbody tr', 3);
|
await clickAll('table tbody tr', 3);
|
||||||
|
|
||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
@@ -730,11 +611,10 @@ async function collectCoverage() {
|
|||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
console.log(' [coverage] Observers page...');
|
console.log(' [coverage] Observers page...');
|
||||||
await page.goto(`${BASE}/#/observers`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${BASE}/#/observers`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(2000);
|
|
||||||
// Click observer rows
|
// Click observer rows
|
||||||
const obsRows = await page.$$('table tbody tr, .observer-card, .observer-row');
|
const obsRows = await page.$$('table tbody tr, .observer-card, .observer-row');
|
||||||
for (let i = 0; i < Math.min(obsRows.length, 3); i++) {
|
for (let i = 0; i < Math.min(obsRows.length, 3); i++) {
|
||||||
try { await obsRows[i].click(); await page.waitForTimeout(500); } catch {}
|
try { await obsRows[i].click(); } catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigate to observer detail page
|
// Navigate to observer detail page
|
||||||
@@ -742,7 +622,6 @@ async function collectCoverage() {
|
|||||||
const obsLink = await page.$('a[href*="/observers/"]');
|
const obsLink = await page.$('a[href*="/observers/"]');
|
||||||
if (obsLink) {
|
if (obsLink) {
|
||||||
await obsLink.click();
|
await obsLink.click();
|
||||||
await page.waitForTimeout(2000);
|
|
||||||
// Change days select
|
// Change days select
|
||||||
await cycleSelect('#obsDaysSelect');
|
await cycleSelect('#obsDaysSelect');
|
||||||
}
|
}
|
||||||
@@ -753,11 +632,8 @@ async function collectCoverage() {
|
|||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
console.log(' [coverage] Perf page...');
|
console.log(' [coverage] Perf page...');
|
||||||
await page.goto(`${BASE}/#/perf`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${BASE}/#/perf`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(2000);
|
|
||||||
await safeClick('#perfRefresh');
|
await safeClick('#perfRefresh');
|
||||||
await page.waitForTimeout(1000);
|
|
||||||
await safeClick('#perfReset');
|
await safeClick('#perfReset');
|
||||||
await page.waitForTimeout(500);
|
|
||||||
|
|
||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
// APP.JS — Router, theme, global features
|
// APP.JS — Router, theme, global features
|
||||||
@@ -766,14 +642,13 @@ async function collectCoverage() {
|
|||||||
|
|
||||||
// Navigate to bad route to trigger error/404
|
// Navigate to bad route to trigger error/404
|
||||||
await page.goto(`${BASE}/#/nonexistent-route`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${BASE}/#/nonexistent-route`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(1000);
|
|
||||||
|
|
||||||
// Navigate to every route via hash
|
// Navigate to every route via hash
|
||||||
const allRoutes = ['home', 'nodes', 'packets', 'map', 'live', 'channels', 'traces', 'observers', 'analytics', 'perf'];
|
const allRoutes = ['home', 'nodes', 'packets', 'map', 'live', 'channels', 'traces', 'observers', 'analytics', 'perf'];
|
||||||
for (const route of allRoutes) {
|
for (const route of allRoutes) {
|
||||||
try {
|
try {
|
||||||
await page.evaluate((r) => { location.hash = '#/' + r; }, route);
|
await page.evaluate((r) => { location.hash = '#/' + r; }, route);
|
||||||
await page.waitForTimeout(800);
|
await page.waitForLoadState('networkidle').catch(() => {});
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -782,13 +657,11 @@ async function collectCoverage() {
|
|||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
window.dispatchEvent(new HashChangeEvent('hashchange'));
|
window.dispatchEvent(new HashChangeEvent('hashchange'));
|
||||||
});
|
});
|
||||||
await page.waitForTimeout(500);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// Theme toggle multiple times
|
// Theme toggle multiple times
|
||||||
for (let i = 0; i < 4; i++) {
|
for (let i = 0; i < 4; i++) {
|
||||||
await safeClick('#darkModeToggle');
|
await safeClick('#darkModeToggle');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch theme-changed event
|
// Dispatch theme-changed event
|
||||||
@@ -796,65 +669,49 @@ async function collectCoverage() {
|
|||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
window.dispatchEvent(new Event('theme-changed'));
|
window.dispatchEvent(new Event('theme-changed'));
|
||||||
});
|
});
|
||||||
await page.waitForTimeout(300);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// Hamburger menu
|
// Hamburger menu
|
||||||
await safeClick('#hamburger');
|
await safeClick('#hamburger');
|
||||||
await page.waitForTimeout(400);
|
|
||||||
// Click nav links in mobile menu
|
// Click nav links in mobile menu
|
||||||
await clickAll('.nav-links .nav-link', 5);
|
await clickAll('.nav-links .nav-link', 5);
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// Favorites
|
// Favorites
|
||||||
await safeClick('#favToggle');
|
await safeClick('#favToggle');
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await clickAll('.fav-dd-item', 3);
|
await clickAll('.fav-dd-item', 3);
|
||||||
// Click outside to close
|
// Click outside to close
|
||||||
try { await page.evaluate(() => document.body.click()); await page.waitForTimeout(300); } catch {}
|
try { await page.evaluate(() => document.body.click()); } catch {}
|
||||||
await safeClick('#favToggle');
|
await safeClick('#favToggle');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// Global search
|
// Global search
|
||||||
await safeClick('#searchToggle');
|
await safeClick('#searchToggle');
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await safeFill('#searchInput', 'test');
|
await safeFill('#searchInput', 'test');
|
||||||
await page.waitForTimeout(1000);
|
|
||||||
// Click search result items
|
// Click search result items
|
||||||
await clickAll('.search-result-item', 3);
|
await clickAll('.search-result-item', 3);
|
||||||
await page.waitForTimeout(500);
|
|
||||||
// Close search
|
// Close search
|
||||||
try { await page.keyboard.press('Escape'); } catch {}
|
try { await page.keyboard.press('Escape'); } catch {}
|
||||||
await page.waitForTimeout(300);
|
|
||||||
|
|
||||||
// Ctrl+K shortcut
|
// Ctrl+K shortcut
|
||||||
try {
|
try {
|
||||||
await page.keyboard.press('Control+k');
|
await page.keyboard.press('Control+k');
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await safeFill('#searchInput', 'node');
|
await safeFill('#searchInput', 'node');
|
||||||
await page.waitForTimeout(800);
|
|
||||||
await page.keyboard.press('Escape');
|
await page.keyboard.press('Escape');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// Click search overlay background to close
|
// Click search overlay background to close
|
||||||
try {
|
try {
|
||||||
await safeClick('#searchToggle');
|
await safeClick('#searchToggle');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await page.click('#searchOverlay', { position: { x: 5, y: 5 } });
|
await page.click('#searchOverlay', { position: { x: 5, y: 5 } });
|
||||||
await page.waitForTimeout(300);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// Navigate via nav links with data-route
|
// Navigate via nav links with data-route
|
||||||
for (const route of allRoutes) {
|
for (const route of allRoutes) {
|
||||||
await safeClick(`a[data-route="${route}"]`);
|
await safeClick(`a[data-route="${route}"]`);
|
||||||
await page.waitForTimeout(600);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exercise apiPerf console function
|
// Exercise apiPerf console function
|
||||||
try {
|
try {
|
||||||
await page.evaluate(() => { if (window.apiPerf) window.apiPerf(); });
|
await page.evaluate(() => { if (window.apiPerf) window.apiPerf(); });
|
||||||
await page.waitForTimeout(300);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// Exercise utility functions
|
// Exercise utility functions
|
||||||
@@ -890,7 +747,6 @@ async function collectCoverage() {
|
|||||||
invalidateApiCache('/test');
|
invalidateApiCache('/test');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await page.waitForTimeout(300);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
@@ -933,21 +789,15 @@ async function collectCoverage() {
|
|||||||
try {
|
try {
|
||||||
// Open region filter on nodes page
|
// Open region filter on nodes page
|
||||||
await page.goto(`${BASE}/#/nodes`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${BASE}/#/nodes`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(1500);
|
|
||||||
await safeClick('#nodesRegionFilter');
|
await safeClick('#nodesRegionFilter');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await clickAll('#nodesRegionFilter input[type="checkbox"]', 3);
|
await clickAll('#nodesRegionFilter input[type="checkbox"]', 3);
|
||||||
await page.waitForTimeout(300);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// Region filter on packets
|
// Region filter on packets
|
||||||
try {
|
try {
|
||||||
await page.goto(`${BASE}/#/packets`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${BASE}/#/packets`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
await page.waitForTimeout(1500);
|
|
||||||
await safeClick('#packetsRegionFilter');
|
await safeClick('#packetsRegionFilter');
|
||||||
await page.waitForTimeout(300);
|
|
||||||
await clickAll('#packetsRegionFilter input[type="checkbox"]', 3);
|
await clickAll('#packetsRegionFilter input[type="checkbox"]', 3);
|
||||||
await page.waitForTimeout(300);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════
|
// ══════════════════════════════════════════════
|
||||||
@@ -957,7 +807,7 @@ async function collectCoverage() {
|
|||||||
for (const route of allRoutes) {
|
for (const route of allRoutes) {
|
||||||
try {
|
try {
|
||||||
await page.evaluate((r) => { location.hash = '#/' + r; }, route);
|
await page.evaluate((r) => { location.hash = '#/' + r; }, route);
|
||||||
await page.waitForTimeout(500);
|
await page.waitForLoadState('networkidle').catch(() => {});
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user