Harden explorer MetaMask data and navigation coverage

This commit is contained in:
defiQUG
2026-03-28 13:40:32 -07:00
parent 1e3a3f00ef
commit a2555b4149
8 changed files with 600 additions and 137 deletions

View File

@@ -1,121 +1,85 @@
/**
* Explorer Frontend E2E Tests
* Tests all links and path-based routing on explorer.d-bis.org
* Tests live SPA links, route coverage, and detail-page navigation on explorer.d-bis.org.
* Run: npx playwright test explorer-monorepo/scripts/e2e-explorer-frontend.spec.ts --project=chromium
*/
import { test, expect } from '@playwright/test';
import { expect, test } from '@playwright/test'
const EXPLORER_URL = process.env.EXPLORER_URL || 'https://explorer.d-bis.org';
const ADDRESS_TEST = '0x99b3511a2d315a497c8112c1fdd8d508d4b1e506';
const EXPLORER_URL = process.env.EXPLORER_URL || 'https://explorer.d-bis.org'
const ADDRESS_TEST = '0x99b3511a2d315a497c8112c1fdd8d508d4b1e506'
test.describe('Explorer Frontend - Path-Based URLs', () => {
test('address path /address/0x... loads address detail', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/address/${ADDRESS_TEST}`, { waitUntil: 'domcontentloaded', timeout: 20000 });
await page.waitForLoadState('domcontentloaded');
const hasBreadcrumb = await page.locator('#addressDetailBreadcrumb').count() > 0;
const bodyText = await page.locator('body').textContent().catch(() => '') || '';
expect(hasBreadcrumb || bodyText.length > 100).toBe(true);
expect(bodyText).toMatch(/Address|Balance|Transaction|0x99|Explorer|detail/i);
});
async function expectBodyToContain(page: Parameters<typeof test>[0], pattern: RegExp) {
const bodyText = await page.locator('body').textContent().catch(() => '') || ''
expect(bodyText).toMatch(pattern)
}
test('root path loads homepage', async ({ page }) => {
await page.goto(EXPLORER_URL, { waitUntil: 'load', timeout: 25000 });
// Logo and nav are always in the HTML; home view or stats appear once SPA runs
const body = await page.locator('body').textContent();
expect(body).toMatch(/SolaceScanScout|Explorer|Chain|Block|Transaction/i);
});
test.describe('Explorer Frontend - Path Coverage', () => {
for (const route of [
{ path: '/', matcher: /SolaceScanScout|Latest Blocks|Explorer/i },
{ path: '/blocks', matcher: /Blocks|Block/i },
{ path: '/transactions', matcher: /Transactions|Transaction/i },
{ path: '/addresses', matcher: /Addresses|Address/i },
{ path: '/watchlist', matcher: /Watchlist|Explorer/i },
{ path: '/pools', matcher: /Pools|Liquidity/i },
{ path: '/liquidity', matcher: /Liquidity|Route|Pool/i },
{ path: '/bridge', matcher: /Bridge|Explorer/i },
{ path: '/weth', matcher: /WETH|Explorer/i },
]) {
test(`${route.path} loads`, async ({ page }) => {
await page.goto(`${EXPLORER_URL}${route.path}`, { waitUntil: 'networkidle', timeout: 20000 })
await expect(page).toHaveURL(new RegExp(route.path === '/' ? '/?$' : route.path.replace('/', '\\/')), { timeout: 8000 })
await expectBodyToContain(page, route.matcher)
})
}
test('blocks path /blocks loads blocks list', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/blocks`, { waitUntil: 'domcontentloaded', timeout: 15000 });
await expect(page.locator('text=Block').first()).toBeVisible({ timeout: 8000 });
});
test('/address/:address loads address detail', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/address/${ADDRESS_TEST}`, { waitUntil: 'networkidle', timeout: 20000 })
await page.waitForSelector('#addressDetailBreadcrumb', { state: 'attached', timeout: 15000 })
await expectBodyToContain(page, /Address|Balance|Transaction|Explorer/i)
})
})
test('transactions path /transactions loads transactions list', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/transactions`, { waitUntil: 'domcontentloaded', timeout: 15000 });
await expect(page.locator('text=Transaction').first()).toBeVisible({ timeout: 8000 });
});
test.describe('Explorer Frontend - Nav and Detail Links', () => {
test('MetaMask Snap link is present', async ({ page }) => {
await page.goto(EXPLORER_URL, { waitUntil: 'domcontentloaded', timeout: 20000 })
const snapLink = page.locator('a[href="/snap/"]').first()
await expect(snapLink).toBeVisible({ timeout: 8000 })
await expect(snapLink).toHaveAttribute('href', '/snap/')
})
test('bridge path /bridge loads and shows bridge or explorer', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/bridge`, { waitUntil: 'networkidle', timeout: 20000 });
const url = page.url();
const body = await page.locator('body').textContent();
expect(url).toMatch(/\/bridge/);
expect(body).toMatch(/Bridge|SolaceScanScout|Explorer/i);
});
test('Address breadcrumb home link returns to root', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/address/${ADDRESS_TEST}`, { waitUntil: 'networkidle', timeout: 20000 })
const homeLink = page.locator('#addressDetailBreadcrumb a[href="/"]').first()
await expect(homeLink).toBeVisible({ timeout: 8000 })
await homeLink.click()
await expect(page).toHaveURL(new RegExp(`${EXPLORER_URL.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/?$`), { timeout: 8000 })
})
test('weth path /weth loads and shows WETH or explorer', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/weth`, { waitUntil: 'networkidle', timeout: 20000 });
const url = page.url();
const body = await page.locator('body').textContent();
expect(url).toMatch(/\/weth/);
expect(body).toMatch(/WETH|SolaceScanScout|Explorer/i);
});
test('Blocks list opens block detail view', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/blocks`, { waitUntil: 'networkidle', timeout: 20000 })
const blockLink = page.locator('[onclick*="showBlockDetail"]').first()
await expect(blockLink).toBeVisible({ timeout: 8000 })
await blockLink.click()
await expect(page.locator('#blockDetailView.active')).toBeVisible({ timeout: 8000 })
await expect(page.locator('#blockDetailBreadcrumb')).toBeVisible({ timeout: 8000 })
})
test('watchlist path /watchlist loads and shows watchlist or explorer', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/watchlist`, { waitUntil: 'networkidle', timeout: 20000 });
const url = page.url();
const body = await page.locator('body').textContent();
expect(url).toMatch(/\/watchlist/);
expect(body).toMatch(/Watchlist|SolaceScanScout|Explorer/i);
});
});
test('Transactions list opens transaction detail view', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/transactions`, { waitUntil: 'networkidle', timeout: 20000 })
const transactionLink = page.locator('[onclick*="showTransactionDetail"]').first()
await expect(transactionLink).toBeVisible({ timeout: 8000 })
await transactionLink.click()
await expect(page.locator('#transactionDetailView.active')).toBeVisible({ timeout: 8000 })
await expect(page.locator('#transactionDetailBreadcrumb')).toBeVisible({ timeout: 8000 })
})
test.describe('Explorer Frontend - Nav Links (path-based routing)', () => {
test('Root shows home content', async ({ page }) => {
await page.goto(EXPLORER_URL, { waitUntil: 'load', timeout: 20000 });
await expect(page.locator('#homeView').or(page.getByText('Latest Blocks')).first()).toBeVisible({ timeout: 10000 });
});
test('Blocks route /blocks loads', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/blocks`, { waitUntil: 'load', timeout: 20000 });
await expect(page).toHaveURL(/\/blocks/, { timeout: 5000 });
await expect(page.getByText('Block').first()).toBeVisible({ timeout: 8000 });
});
test('Transactions route /transactions loads', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/transactions`, { waitUntil: 'load', timeout: 20000 });
await expect(page).toHaveURL(/\/transactions/, { timeout: 5000 });
await expect(page.getByText('Transaction').first()).toBeVisible({ timeout: 8000 });
});
test('Bridge route /bridge has correct URL', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/bridge`, { waitUntil: 'load', timeout: 20000 });
await expect(page).toHaveURL(/\/bridge/, { timeout: 5000 });
});
test('WETH route /weth has correct URL', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/weth`, { waitUntil: 'load', timeout: 20000 });
await expect(page).toHaveURL(/\/weth/, { timeout: 5000 });
});
test('MetaMask Snap link has correct href', async ({ page }) => {
await page.goto(EXPLORER_URL, { waitUntil: 'domcontentloaded', timeout: 20000 });
const snapLink = page.locator('a[href="/snap/"]').first();
await expect(snapLink).toBeVisible({ timeout: 8000 });
await expect(snapLink).toHaveAttribute('href', '/snap/');
});
});
test.describe('Explorer Frontend - Breadcrumbs & Detail Links', () => {
test('Address page breadcrumb links work', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/address/${ADDRESS_TEST}`, { waitUntil: 'networkidle', timeout: 15000 });
await page.waitForSelector('#addressDetailBreadcrumb', { state: 'attached', timeout: 15000 });
const homeLink = page.locator('#addressDetailBreadcrumb a[href="/home"], #addressDetailBreadcrumb a[href="#/home"]').first();
if (await homeLink.isVisible({ timeout: 2000 }).catch(() => false)) {
await homeLink.click();
await page.waitForTimeout(500);
expect(page.url()).toContain('home');
}
});
test('Block number link from list opens block detail', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/blocks`, { waitUntil: 'networkidle', timeout: 15000 });
const blockLink = page.locator('a[href^="#/block/"], [onclick*="showBlockDetail"]').first();
if (await blockLink.isVisible({ timeout: 3000 })) {
await blockLink.click();
await page.waitForTimeout(1000);
await expect(page.locator('#blockDetail, .block-detail')).toBeVisible({ timeout: 3000 });
}
});
});
test('Addresses list opens address detail view', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/addresses`, { waitUntil: 'networkidle', timeout: 20000 })
const addressLink = page.locator('[onclick*="showAddressDetail"]').first()
await expect(addressLink).toBeVisible({ timeout: 8000 })
await addressLink.click()
await expect(page.locator('#addressDetailView.active')).toBeVisible({ timeout: 8000 })
await expect(page.locator('#addressDetailBreadcrumb')).toBeVisible({ timeout: 8000 })
})
})