Prune explorer dead ends and harden route smoke
This commit is contained in:
@@ -2232,29 +2232,77 @@
|
||||
}
|
||||
window._showAddresses = renderAddressesView;
|
||||
|
||||
// Analytics view (Track 3+)
|
||||
function buildAnalyticsViewHtml() {
|
||||
var html = '';
|
||||
html += '<div style="display:grid; gap:1rem;">';
|
||||
html += '<div class="card" style="margin:0; box-shadow:none;">';
|
||||
html += '<div class="card-header" style="align-items:flex-start; gap:0.75rem;">';
|
||||
html += '<div>';
|
||||
html += '<h3 class="card-title" style="margin-bottom:0.25rem;"><i class="fas fa-chart-line"></i> Live Network Analytics</h3>';
|
||||
html += '<div style="color:var(--text-light); line-height:1.55;">Analytics surfaces are consolidated into the live explorer dashboards instead of a separate unfinished panel. Use this page as a hub to the active gas, block, bridge, and route monitoring views.</div>';
|
||||
html += '</div>';
|
||||
html += '<div style="display:flex; gap:0.5rem; flex-wrap:wrap; margin-left:auto;">';
|
||||
html += '<button type="button" class="btn btn-primary" onclick="showHome(); updatePath(\'/\')" aria-label="Open network dashboard"><i class="fas fa-gauge-high"></i> Network dashboard</button>';
|
||||
html += '<button type="button" class="btn btn-secondary" onclick="showBridgeMonitoring(); updatePath(\'/bridge\')" aria-label="Open bridge monitoring"><i class="fas fa-bridge"></i> Bridge monitoring</button>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(220px, 1fr)); gap:0.75rem;">';
|
||||
html += '<a href="/" onclick="event.preventDefault(); showHome(); updatePath(\'/\');" style="display:block; text-decoration:none; color:inherit; border:1px solid var(--border); border-radius:14px; padding:1rem; background:var(--muted-surface);"><div style="font-weight:700; margin-bottom:0.3rem;">Gas & Network</div><div style="color:var(--text-light); line-height:1.5;">Open the live home dashboard for gas price, TPS, block time, validator count, and latest-chain activity.</div></a>';
|
||||
html += '<a href="/blocks" onclick="event.preventDefault(); showBlocks(); updatePath(\'/blocks\');" style="display:block; text-decoration:none; color:inherit; border:1px solid var(--border); border-radius:14px; padding:1rem; background:var(--muted-surface);"><div style="font-weight:700; margin-bottom:0.3rem;">Block cadence</div><div style="color:var(--text-light); line-height:1.5;">Inspect live block production, miner attribution, gas usage, and exportable block history.</div></a>';
|
||||
html += '<a href="/transactions" onclick="event.preventDefault(); showTransactions(); updatePath(\'/transactions\');" style="display:block; text-decoration:none; color:inherit; border:1px solid var(--border); border-radius:14px; padding:1rem; background:var(--muted-surface);"><div style="font-weight:700; margin-bottom:0.3rem;">Transaction flow</div><div style="color:var(--text-light); line-height:1.5;">Review the recent transaction stream and drill into decoded execution details and internal calls.</div></a>';
|
||||
html += '<a href="/routes" onclick="event.preventDefault(); showRoutes(); updatePath(\'/routes\');" style="display:block; text-decoration:none; color:inherit; border:1px solid var(--border); border-radius:14px; padding:1rem; background:var(--muted-surface);"><div style="font-weight:700; margin-bottom:0.3rem;">Route coverage</div><div style="color:var(--text-light); line-height:1.5;">Open the dedicated route-decision tree for swap-path coverage, bridge branches, and missing quote-token diagnostics.</div></a>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
// Analytics view
|
||||
function renderAnalyticsView() {
|
||||
if (!hasAccess(3)) {
|
||||
showToast('Analytics features require Track 3 access. Please connect your wallet and ensure you are approved.', 'error');
|
||||
return;
|
||||
}
|
||||
switchToView('analytics');
|
||||
showView('analytics');
|
||||
if ((window.location.pathname || '').replace(/^\//, '').split('/')[0] !== 'analytics') updatePath('/analytics');
|
||||
var container = document.getElementById('analyticsContent');
|
||||
if (!container) return;
|
||||
container.innerHTML = buildAnalyticsViewHtml();
|
||||
}
|
||||
window._showAnalytics = renderAnalyticsView;
|
||||
|
||||
// Operator view (Track 4)
|
||||
function buildOperatorViewHtml() {
|
||||
var html = '';
|
||||
html += '<div style="display:grid; gap:1rem;">';
|
||||
html += '<div class="card" style="margin:0; box-shadow:none;">';
|
||||
html += '<div class="card-header" style="align-items:flex-start; gap:0.75rem;">';
|
||||
html += '<div>';
|
||||
html += '<h3 class="card-title" style="margin-bottom:0.25rem;"><i class="fas fa-server"></i> Operator Access Hub</h3>';
|
||||
html += '<div style="color:var(--text-light); line-height:1.55;">The explorer does not expose raw privileged controls here. Instead, this page collects the live operator-facing observability and execution surfaces that are safe to browse from the public UI.</div>';
|
||||
html += '</div>';
|
||||
html += '<div style="display:flex; gap:0.5rem; flex-wrap:wrap; margin-left:auto;">';
|
||||
html += '<button type="button" class="btn btn-primary" onclick="showBridgeMonitoring(); updatePath(\'/bridge\')" aria-label="Open bridge monitoring"><i class="fas fa-bridge"></i> Bridge status</button>';
|
||||
html += '<button type="button" class="btn btn-secondary" onclick="showLiquidityAccess(); updatePath(\'/liquidity\')" aria-label="Open liquidity access"><i class="fas fa-wave-square"></i> Liquidity APIs</button>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(220px, 1fr)); gap:0.75rem;">';
|
||||
html += '<a href="/bridge" onclick="event.preventDefault(); showBridgeMonitoring(); updatePath(\'/bridge\');" style="display:block; text-decoration:none; color:inherit; border:1px solid var(--border); border-radius:14px; padding:1rem; background:var(--muted-surface);"><div style="font-weight:700; margin-bottom:0.3rem;">Bridge monitoring</div><div style="color:var(--text-light); line-height:1.5;">Inspect bridge balances, fee-token posture, destination configuration, and live contract references.</div></a>';
|
||||
html += '<a href="/liquidity" onclick="event.preventDefault(); showLiquidityAccess(); updatePath(\'/liquidity\');" style="display:block; text-decoration:none; color:inherit; border:1px solid var(--border); border-radius:14px; padding:1rem; background:var(--muted-surface);"><div style="font-weight:700; margin-bottom:0.3rem;">Liquidity access</div><div style="color:var(--text-light); line-height:1.5;">Jump to partner payload routes, ingestion APIs, and public execution-plan endpoints without leaving the explorer.</div></a>';
|
||||
html += '<a href="/pools" onclick="event.preventDefault(); showPools(); updatePath(\'/pools\');" style="display:block; text-decoration:none; color:inherit; border:1px solid var(--border); border-radius:14px; padding:1rem; background:var(--muted-surface);"><div style="font-weight:700; margin-bottom:0.3rem;">Pool inventory</div><div style="color:var(--text-light); line-height:1.5;">Review canonical PMM addresses, funding state, registry status, and exportable pool snapshots.</div></a>';
|
||||
html += '<a href="/weth" onclick="event.preventDefault(); showWETHUtilities(); updatePath(\'/weth\');" style="display:block; text-decoration:none; color:inherit; border:1px solid var(--border); border-radius:14px; padding:1rem; background:var(--muted-surface);"><div style="font-weight:700; margin-bottom:0.3rem;">WETH utilities</div><div style="color:var(--text-light); line-height:1.5;">Open the WETH9/WETH10 utilities, bridge contract references, and balance tools that operators often need during support.</div></a>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
// Operator view
|
||||
function renderOperatorView() {
|
||||
if (!hasAccess(4)) {
|
||||
showToast('Operator features require Track 4 access. Please connect your wallet and ensure you are approved.', 'error');
|
||||
return;
|
||||
}
|
||||
switchToView('operator');
|
||||
showView('operator');
|
||||
if ((window.location.pathname || '').replace(/^\//, '').split('/')[0] !== 'operator') updatePath('/operator');
|
||||
var container = document.getElementById('operatorContent');
|
||||
if (!container) return;
|
||||
container.innerHTML = buildOperatorViewHtml();
|
||||
}
|
||||
window._showOperator = renderOperatorView;
|
||||
|
||||
function showView(viewName) {
|
||||
currentView = viewName;
|
||||
var detailViews = ['blockDetail','transactionDetail','addressDetail','tokenDetail','nftDetail','watchlist','searchResults','tokens','addresses','pools','liquidity','more'];
|
||||
var detailViews = ['blockDetail','transactionDetail','addressDetail','tokenDetail','nftDetail','watchlist','searchResults','tokens','addresses','pools','routes','liquidity','more','analytics','operator'];
|
||||
if (detailViews.indexOf(viewName) === -1) currentDetailKey = '';
|
||||
document.querySelectorAll('.detail-view').forEach(v => v.classList.remove('active'));
|
||||
const homeView = document.getElementById('homeView');
|
||||
@@ -3995,9 +4043,9 @@
|
||||
key: 'explore',
|
||||
title: 'Explore',
|
||||
items: [
|
||||
{ title: 'Gas Tracker', icon: 'fa-gas-pump', status: 'Live', badgeClass: 'badge-success', desc: 'Review live gas, block time, TPS, and chain health from the analytics and home dashboards.', action: 'showAnalytics();', href: '/analytics' },
|
||||
{ title: 'Gas Tracker', icon: 'fa-gas-pump', status: 'Live', badgeClass: 'badge-success', desc: 'Review live gas, block time, TPS, and chain health from the home network dashboard.', action: 'showHome();', href: '/' },
|
||||
{ title: 'DEX Tracker', icon: 'fa-chart-line', status: 'Live', badgeClass: 'badge-success', desc: 'Open liquidity discovery, PMM pool status, live route trees, and partner payload access points.', action: 'showRoutes();', href: '/routes' },
|
||||
{ title: 'Node Tracker', icon: 'fa-server', status: 'Live', badgeClass: 'badge-success', desc: 'Inspect bridge and operator infrastructure surfaces already exposed in the Bridge and Operator panels.', action: 'showOperator();', href: '/operator' },
|
||||
{ title: 'Node Tracker', icon: 'fa-server', status: 'Live', badgeClass: 'badge-success', desc: 'Inspect bridge balances, destination configuration, and operator-facing chain references from the live bridge monitoring panel.', action: 'showBridgeMonitoring();', href: '/bridge' },
|
||||
{ title: 'Label Cloud', icon: 'fa-tags', status: 'Live', badgeClass: 'badge-success', desc: 'Browse labeled addresses, contracts, and address activity through the explorer address index.', action: 'showAddresses();', href: '/addresses' },
|
||||
{ title: 'Domain Name Lookup', icon: 'fa-magnifying-glass', status: 'Live', badgeClass: 'badge-success', desc: 'Use the smart search launcher to resolve ENS-style names, domains, addresses, hashes, and token symbols.', action: 'openSmartSearchModal(\'\');', href: '/more' }
|
||||
]
|
||||
@@ -4010,7 +4058,7 @@
|
||||
{ title: 'Verified Signature', icon: 'fa-signature', status: 'Live', badgeClass: 'badge-success', desc: 'Use wallet sign-in and verified address flows already built into the explorer authentication surfaces.', action: 'showWalletModal();', href: '/more' },
|
||||
{ title: 'Input Data Messages', icon: 'fa-message', status: 'Live', badgeClass: 'badge-info', desc: 'Transaction detail pages already surface decoded input data, event logs, and contract interaction context.', action: 'showTransactionsList();', href: '/transactions' },
|
||||
{ title: 'Advanced Filter', icon: 'fa-filter', status: 'Live', badgeClass: 'badge-success', desc: 'Block, transaction, address, token, pool, bridge, and watchlist screens all support focused page-level filtering.', action: 'showTransactionsList();', href: '/transactions' },
|
||||
{ title: 'Blockscan Chat', icon: 'fa-comments', status: 'Soon', badgeClass: 'badge-muted', desc: 'Messaging and collaborative address discussion are not exposed yet in SolaceScanScout.', disabled: true, href: '/more' }
|
||||
{ title: 'MetaMask Snap', icon: 'fa-wallet', status: 'Live', badgeClass: 'badge-success', desc: 'Open the Chain 138 MetaMask Snap companion for network setup, token list access, and wallet integration guidance.', action: 'window.location.href=\'/snap/\';', href: '/snap/' }
|
||||
]
|
||||
}
|
||||
];
|
||||
@@ -4022,6 +4070,7 @@
|
||||
html += '<div style="display:grid; gap:0.75rem;">';
|
||||
html += '<div style="padding:0.85rem; border:1px solid var(--border); border-radius:14px; background:var(--muted-surface);"><div style="font-size:0.82rem; text-transform:uppercase; letter-spacing:0.08em; color:var(--text-light); margin-bottom:0.35rem;">Now live</div><div style="font-weight:700;">Route matrix, ingestion APIs, smart search, pool exports, and live Mainnet stable bridge discovery.</div></div>';
|
||||
html += '<div style="padding:0.85rem; border:1px solid var(--border); border-radius:14px; background:var(--muted-surface);"><div style="font-size:0.82rem; text-transform:uppercase; letter-spacing:0.08em; color:var(--text-light); margin-bottom:0.35rem;">Good entry points</div><div style="display:flex; flex-wrap:wrap; gap:0.5rem;">';
|
||||
html += '<button type="button" class="btn btn-primary" onclick="showRoutes(); updatePath(\'/routes\');"><i class="fas fa-diagram-project"></i> Routes</button>';
|
||||
html += '<button type="button" class="btn btn-primary" onclick="showLiquidityAccess(); updatePath(\'/liquidity\');"><i class="fas fa-wave-square"></i> Liquidity</button>';
|
||||
html += '<button type="button" class="btn btn-secondary" onclick="openSmartSearchModal(\'\');"><i class="fas fa-magnifying-glass"></i> Search</button>';
|
||||
html += '<button type="button" class="btn btn-secondary" onclick="showAddresses(); updatePath(\'/addresses\');"><i class="fas fa-address-book"></i> Addresses</button>';
|
||||
|
||||
@@ -1540,7 +1540,7 @@
|
||||
<h2 class="card-title"><i class="fas fa-chart-line"></i> Analytics Dashboard</h2>
|
||||
</div>
|
||||
<div id="analyticsContent">
|
||||
<p style="color: var(--text-light);">Analytics dashboard (Track 3). Network stats, flow tracking, and bridge analytics coming soon.</p>
|
||||
<div class="loading"><i class="fas fa-spinner"></i> Loading analytics access...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1552,7 +1552,7 @@
|
||||
<h2 class="card-title"><i class="fas fa-cog"></i> Operator Panel</h2>
|
||||
</div>
|
||||
<div id="operatorContent">
|
||||
<p style="color: var(--text-light);">Operator panel (Track 4). Configuration and operational controls coming soon.</p>
|
||||
<div class="loading"><i class="fas fa-spinner"></i> Loading operator access...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,65 +1,96 @@
|
||||
const baseUrl = (process.env.BASE_URL || 'http://127.0.0.1:3000').replace(/\/$/, '')
|
||||
import { chromium } from 'playwright';
|
||||
|
||||
const baseUrl = (process.env.BASE_URL || 'https://explorer.d-bis.org').replace(/\/$/, '');
|
||||
|
||||
const checks = [
|
||||
{ path: '/', expect: ['/addresses', '/transactions', '/watchlist', '/pools'] },
|
||||
{ path: '/blocks', expect: ['Latest Blocks', 'View blockchain blocks', 'Blocks'] },
|
||||
{ path: '/transactions', expect: ['Latest Transactions', 'Recent Transactions', 'Transactions'] },
|
||||
{ path: '/addresses', expect: ['Open An Address', 'Recently Active Addresses', 'Saved Watchlist'] },
|
||||
{ path: '/tokens', expect: ['Find A Token', 'Common token searches', 'Tokens'] },
|
||||
{ path: '/pools', expect: ['Canonical PMM routes', 'Pool operation shortcuts', 'Pools'] },
|
||||
{ path: '/routes', expect: ['Live Route Decision Tree', 'Coverage sweep + priority route cards', 'Routes'] },
|
||||
{ path: '/watchlist', expect: ['Saved Addresses', 'Watchlist', 'Export JSON'] },
|
||||
{ path: '/search?q=cUSDT', expect: ['Search Results', 'Results for', 'Search'] },
|
||||
{ path: '/blocks/1', expect: ['Loading block details', 'Block Details'] },
|
||||
{ path: '/', homeVisible: true, expectTexts: ['Gas & Network', 'Latest Blocks', 'Latest Transactions'] },
|
||||
{ path: '/blocks', activeView: 'blocksView', expectTexts: ['All Blocks'] },
|
||||
{ path: '/transactions', activeView: 'transactionsView', expectTexts: ['All Transactions'] },
|
||||
{ path: '/addresses', activeView: 'addressesView', expectTexts: ['All Addresses'] },
|
||||
{ path: '/tokens', activeView: 'tokensView', expectTexts: ['Tokens'] },
|
||||
{ path: '/pools', activeView: 'poolsView', expectTexts: ['Pools', 'Canonical PMM routes'] },
|
||||
{ path: '/routes', activeView: 'routesView', expectTexts: ['Routes', 'Live Route Decision Tree'] },
|
||||
{ path: '/watchlist', activeView: 'watchlistView', expectTexts: ['Watchlist'] },
|
||||
{ path: '/bridge', activeView: 'bridgeView', expectTexts: ['Bridge Monitoring'] },
|
||||
{ path: '/weth', activeView: 'wethView', expectTexts: ['WETH', 'Wrap ETH to WETH9'] },
|
||||
{ path: '/liquidity', activeView: 'liquidityView', expectTexts: ['Liquidity Access', 'Public Explorer Access Points'] },
|
||||
{ path: '/more', activeView: 'moreView', expectTexts: ['More', 'Tools & Services'] },
|
||||
{ path: '/analytics', activeView: 'analyticsView', expectTexts: ['Analytics Dashboard', 'Live Network Analytics'] },
|
||||
{ path: '/operator', activeView: 'operatorView', expectTexts: ['Operator Panel', 'Operator Access Hub'] },
|
||||
{ path: '/block/1', activeView: 'blockDetailView', expectTexts: ['Block Details'] },
|
||||
{
|
||||
path: '/transactions/0x0000000000000000000000000000000000000000000000000000000000000000',
|
||||
expect: ['Loading transaction details', 'Transaction Details', 'Transaction not found'],
|
||||
path: '/tx/0x0000000000000000000000000000000000000000000000000000000000000000',
|
||||
activeView: 'transactionDetailView',
|
||||
expectTexts: ['Transaction Details', 'Transaction not found'],
|
||||
},
|
||||
{
|
||||
path: '/addresses/0x0000000000000000000000000000000000000000',
|
||||
expect: ['Loading address details', 'Address Details', 'Open An Address'],
|
||||
path: '/address/0x93E66202A11B1772E55407B32B44e5Cd8eda7f22',
|
||||
activeView: 'addressDetailView',
|
||||
expectTexts: ['Address Details', 'Address'],
|
||||
},
|
||||
]
|
||||
];
|
||||
|
||||
function hasExpectedBody(text, expectedSnippets) {
|
||||
return expectedSnippets.some((snippet) => text.includes(snippet))
|
||||
function hasExpectedText(text, snippets) {
|
||||
return snippets.some((snippet) => text.includes(snippet));
|
||||
}
|
||||
|
||||
async function run() {
|
||||
let failures = 0
|
||||
async function main() {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await browser.newPage();
|
||||
let failures = 0;
|
||||
|
||||
for (const check of checks) {
|
||||
const url = `${baseUrl}${check.path}`
|
||||
const url = `${baseUrl}${check.path}`;
|
||||
|
||||
try {
|
||||
const response = await fetch(url, { redirect: 'follow' })
|
||||
const body = await response.text()
|
||||
|
||||
if (!response.ok) {
|
||||
console.error(`FAIL ${check.path}: HTTP ${response.status}`)
|
||||
failures += 1
|
||||
continue
|
||||
const response = await page.goto(url, { waitUntil: 'networkidle' });
|
||||
if (!response || !response.ok()) {
|
||||
console.error(`FAIL ${check.path}: HTTP ${response ? response.status() : 'no-response'}`);
|
||||
failures += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!hasExpectedBody(body, check.expect)) {
|
||||
console.error(`FAIL ${check.path}: expected one of ${check.expect.join(' | ')}`)
|
||||
failures += 1
|
||||
continue
|
||||
const bodyText = await page.textContent('body');
|
||||
if (check.homeVisible) {
|
||||
const homeVisible = await page.$eval('#homeView', (el) => getComputedStyle(el).display !== 'none').catch(() => false);
|
||||
if (!homeVisible) {
|
||||
console.error(`FAIL ${check.path}: home view not visible`);
|
||||
failures += 1;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
const activeView = await page.$eval('.detail-view.active', (el) => el.id).catch(() => null);
|
||||
if (activeView !== check.activeView) {
|
||||
console.error(`FAIL ${check.path}: expected active view ${check.activeView}, got ${activeView}`);
|
||||
failures += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`OK ${check.path}`)
|
||||
if (!hasExpectedText(bodyText || '', check.expectTexts)) {
|
||||
console.error(`FAIL ${check.path}: expected one of ${check.expectTexts.join(' | ')}`);
|
||||
failures += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`OK ${check.path}`);
|
||||
} catch (error) {
|
||||
console.error(`FAIL ${check.path}: ${error instanceof Error ? error.message : String(error)}`)
|
||||
failures += 1
|
||||
console.error(`FAIL ${check.path}: ${error instanceof Error ? error.message : String(error)}`);
|
||||
failures += 1;
|
||||
}
|
||||
}
|
||||
|
||||
await browser.close();
|
||||
|
||||
if (failures > 0) {
|
||||
process.exitCode = 1
|
||||
return
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`All ${checks.length} route checks passed for ${baseUrl}`)
|
||||
console.log(`All ${checks.length} route checks passed for ${baseUrl}`);
|
||||
}
|
||||
|
||||
run()
|
||||
main().catch((error) => {
|
||||
console.error(error instanceof Error ? error.stack || error.message : String(error));
|
||||
process.exitCode = 1;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user