Files
explorer-monorepo/scripts/e2e-explorer-frontend.spec.ts
defiQUG bdae5a9f6e feat: explorer API, wallet, CCIP scripts, and config refresh
- Backend REST/gateway/track routes, analytics, Blockscout proxy paths.
- Frontend wallet and liquidity surfaces; MetaMask token list alignment.
- Deployment docs, verification scripts, address inventory updates.

Check: go build ./... under backend/ (pass).
Made-with: Cursor
2026-04-07 23:22:12 -07:00

151 lines
6.5 KiB
TypeScript

import { expect, test, type Page } from '@playwright/test'
const EXPLORER_URL = process.env.EXPLORER_URL || 'https://explorer.d-bis.org'
const ADDRESS_TEST = '0x99b3511a2d315a497c8112c1fdd8d508d4b1e506'
async function expectHeading(page: Page, name: RegExp) {
await expect(page.getByRole('heading', { name })).toBeVisible({ timeout: 10000 })
}
function collectUnexpectedConsoleErrors(page: Page, allowlist: RegExp[] = []) {
const unexpectedErrors: string[] = []
page.on('console', (message) => {
if (message.type() !== 'error') {
return
}
const text = message.text()
if (allowlist.some((pattern) => pattern.test(text))) {
return
}
unexpectedErrors.push(text)
})
return async () => {
expect(unexpectedErrors).toEqual([])
}
}
test.describe('Explorer Frontend - Route Coverage', () => {
for (const route of [
{ path: '/', heading: /SolaceScanScout/i },
{ path: '/blocks', heading: /^Blocks$/i },
{ path: '/transactions', heading: /^Transactions$/i },
{ path: '/addresses', heading: /^Addresses$/i },
{ path: '/watchlist', heading: /^Watchlist$/i },
{ path: '/pools', heading: /^Pools$/i },
{ path: '/liquidity', heading: /Public liquidity, route discovery, and execution access points/i },
{ path: '/wallet', heading: /Wallet & MetaMask/i },
{ path: '/tokens', heading: /^Tokens$/i },
{ path: '/search', heading: /^Search$/i },
]) {
test(`${route.path} loads`, async ({ page }) => {
await page.goto(`${EXPLORER_URL}${route.path}`, { waitUntil: 'domcontentloaded', timeout: 20000 })
await expect(page).toHaveURL(new RegExp(route.path === '/' ? '/?$' : route.path.replace('/', '\\/')), { timeout: 8000 })
await expectHeading(page, route.heading)
})
}
test('/addresses/:address loads address detail', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/addresses/${ADDRESS_TEST}`, { waitUntil: 'domcontentloaded', timeout: 20000 })
await expectHeading(page, /Address/i)
await expect(page.getByText(/Back to addresses/i)).toBeVisible({ timeout: 10000 })
})
})
test.describe('Explorer Frontend - Current Navigation', () => {
test('global shell is present on both app and pages routes', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/wallet`, { waitUntil: 'domcontentloaded', timeout: 20000 })
await expect(page.getByRole('link', { name: /Go to explorer home/i })).toBeVisible({ timeout: 10000 })
await expect(page.getByText(/Support:/i)).toBeVisible({ timeout: 10000 })
await page.goto(`${EXPLORER_URL}/transactions`, { waitUntil: 'domcontentloaded', timeout: 20000 })
await expect(page.getByRole('link', { name: /Go to explorer home/i })).toBeVisible({ timeout: 10000 })
await expect(page.getByText(/Support:/i)).toBeVisible({ timeout: 10000 })
})
test('wallet page exposes the current Chain 138 Snap action', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/wallet`, { waitUntil: 'domcontentloaded', timeout: 20000 })
await expect(page.getByRole('button', { name: /Install Open Snap/i })).toBeVisible({ timeout: 10000 })
await expect(page.getByText(/Chain 138 Open Snap/i)).toBeVisible({ timeout: 10000 })
})
test('blocks list links to a current block detail route', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/blocks`, { waitUntil: 'networkidle', timeout: 20000 })
const blockLink = page.locator('a[href^="/blocks/"]').first()
await expect(blockLink).toBeVisible({ timeout: 10000 })
await blockLink.click()
await expect(page).toHaveURL(/\/blocks\/\d+$/, { timeout: 10000 })
await expect(page.getByText(/Back to blocks/i)).toBeVisible({ timeout: 10000 })
})
test('transactions list links to a current transaction detail route', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/transactions`, { waitUntil: 'networkidle', timeout: 20000 })
const transactionLinks = page.locator('a[href^="/transactions/"]')
const transactionCount = await transactionLinks.count()
if (transactionCount === 0) {
await expect(page.getByText(/Recent transactions are unavailable right now/i)).toBeVisible({ timeout: 10000 })
await expect(page.getByRole('button', { name: /^Next$/i })).toBeDisabled()
return
}
const href = await transactionLinks.first().getAttribute('href')
expect(href).toMatch(/^\/transactions\/0x[a-f0-9]+$/i)
await page.goto(`${EXPLORER_URL}${href}`, { waitUntil: 'domcontentloaded', timeout: 20000 })
await expect(page).toHaveURL(/\/transactions\/0x[a-f0-9]+$/i, { timeout: 10000 })
await expect(page.getByText(/Back to transactions/i)).toBeVisible({ timeout: 10000 })
})
test('addresses page opens a current address detail route', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/addresses`, { waitUntil: 'networkidle', timeout: 20000 })
await page.getByPlaceholder('0x...').fill(ADDRESS_TEST)
await page.getByRole('button', { name: /Open address/i }).click()
await expect(page).toHaveURL(new RegExp(`/addresses/${ADDRESS_TEST}$`, 'i'), { timeout: 10000 })
await expect(page.getByText(/Back to addresses/i)).toBeVisible({ timeout: 10000 })
})
test('homepage keeps recent blocks visible when stats are temporarily unavailable', async ({ page }) => {
const assertConsole = collectUnexpectedConsoleErrors(page, [
/api\/v2\/stats/i,
/503/i,
/service unavailable/i,
])
await page.route(`${EXPLORER_URL}/api/v2/stats`, async (route) => {
await route.fulfill({
status: 503,
contentType: 'application/json',
body: JSON.stringify({ error: 'service_unavailable' }),
})
})
await page.route(new RegExp(`${EXPLORER_URL.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/api/v2/blocks\\?.*`), async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
items: [
{
hash: '0xabc',
height: 4321,
timestamp: '2026-04-05T00:00:00.000Z',
miner: { hash: '0xdef' },
transaction_count: 7,
gas_used: 21000,
gas_limit: 30000000,
},
],
}),
})
})
await page.goto(`${EXPLORER_URL}/`, { waitUntil: 'domcontentloaded', timeout: 20000 })
await expect(page.getByText(/Live network stats are temporarily unavailable/i)).toBeVisible({ timeout: 10000 })
await expect(page.getByRole('link', { name: /Block #4321/i })).toBeVisible({ timeout: 10000 })
await assertConsole()
})
})