Defer heavy getServerSideProps on home, operator, addresses, and wallet to cut TTFB; centralize locale-safe formatters with client-only relative times; add compact ops UX, bridge/operator relay pagination, and Playwright route/scroll smoke in deploy. Co-authored-by: Cursor <cursoragent@cursor.com>
131 lines
4.3 KiB
JavaScript
131 lines
4.3 KiB
JavaScript
import { chromium } from 'playwright'
|
|
|
|
const baseUrl = (process.env.BASE_URL || 'https://explorer.d-bis.org').replace(/\/$/, '')
|
|
const addressUnderTest = process.env.SMOKE_ADDRESS || '0x99b3511a2d315a497c8112c1fdd8d508d4b1e506'
|
|
|
|
const checks = [
|
|
{ path: '/', expectTexts: ['DBIS Explorer', 'Institutional quick paths'] },
|
|
{ path: '/bridge', expectTexts: ['Bridge'] },
|
|
{ path: '/routes', expectTexts: ['Routes'] },
|
|
{ path: '/operations', expectTexts: ['Operations'] },
|
|
{ path: '/system', expectTexts: ['System'] },
|
|
{ path: '/operator', expectTexts: ['Operator'] },
|
|
{ path: '/analytics', expectTexts: ['Analytics'] },
|
|
{ path: '/liquidity', expectTexts: ['Liquidity'] },
|
|
{ path: '/blocks', expectTexts: ['Blocks'] },
|
|
{ path: '/transactions', expectTexts: ['Transactions'] },
|
|
{ path: '/addresses', expectTexts: ['Addresses', 'Open address'] },
|
|
{ path: '/watchlist', expectTexts: ['Watchlist', 'Saved addresses'] },
|
|
{ path: '/pools', expectTexts: ['Pools'] },
|
|
{ path: '/wallet', expectTexts: ['Wallet Tools', 'WalletConnect'] },
|
|
{ path: '/access', expectTexts: ['Wallet Login', 'RPC Access'] },
|
|
{ path: '/tokens', expectTexts: ['Tokens'] },
|
|
{ path: '/search', expectTexts: ['Search'], placeholder: 'Search by address, transaction hash, block number...' },
|
|
{ path: `/addresses/${addressUnderTest}`, expectTexts: [], anyOfTexts: ['Back to addresses', 'Address not found'] },
|
|
]
|
|
|
|
async function bodyText(page) {
|
|
return (await page.textContent('body')) || ''
|
|
}
|
|
|
|
async function hasShell(page) {
|
|
const homeLink = await page
|
|
.getByRole('link', { name: /Go to DBIS Explorer home|Go to explorer home/i })
|
|
.isVisible()
|
|
.catch(() => false)
|
|
const dbisTitle = await page
|
|
.getByText(/DBIS Explorer/i)
|
|
.first()
|
|
.isVisible()
|
|
.catch(() => false)
|
|
return homeLink || dbisTitle
|
|
}
|
|
|
|
async function waitForBodyText(page, snippets, timeoutMs = 20000) {
|
|
if (!snippets || snippets.length === 0) {
|
|
return bodyText(page)
|
|
}
|
|
|
|
const deadline = Date.now() + timeoutMs
|
|
let lastText = ''
|
|
|
|
while (Date.now() < deadline) {
|
|
lastText = await bodyText(page)
|
|
if (snippets.every((snippet) => lastText.includes(snippet))) {
|
|
return lastText
|
|
}
|
|
await page.waitForTimeout(250)
|
|
}
|
|
|
|
return lastText
|
|
}
|
|
|
|
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}`
|
|
|
|
try {
|
|
const response = await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 20000 })
|
|
await page.waitForLoadState('networkidle', { timeout: 8000 }).catch(() => {})
|
|
|
|
if (!response || !response.ok()) {
|
|
console.error(`FAIL ${check.path}: HTTP ${response ? response.status() : 'no-response'}`)
|
|
failures += 1
|
|
continue
|
|
}
|
|
|
|
const text = await waitForBodyText(page, check.expectTexts)
|
|
const missing = check.expectTexts.filter((snippet) => !text.includes(snippet))
|
|
if (missing.length > 0) {
|
|
console.error(`FAIL ${check.path}: missing text ${missing.join(' | ')}`)
|
|
failures += 1
|
|
continue
|
|
}
|
|
|
|
if (check.anyOfTexts && !check.anyOfTexts.some((snippet) => text.includes(snippet))) {
|
|
console.error(`FAIL ${check.path}: expected one of ${check.anyOfTexts.join(' | ')}`)
|
|
failures += 1
|
|
continue
|
|
}
|
|
|
|
if (check.placeholder) {
|
|
const placeholderVisible = await page.getByPlaceholder(check.placeholder).isVisible().catch(() => false)
|
|
if (!placeholderVisible) {
|
|
console.error(`FAIL ${check.path}: missing placeholder ${check.placeholder}`)
|
|
failures += 1
|
|
continue
|
|
}
|
|
}
|
|
|
|
if (!(await hasShell(page))) {
|
|
console.error(`FAIL ${check.path}: shared explorer shell not visible`)
|
|
failures += 1
|
|
continue
|
|
}
|
|
|
|
console.log(`OK ${check.path}`)
|
|
} catch (error) {
|
|
console.error(`FAIL ${check.path}: ${error instanceof Error ? error.message : String(error)}`)
|
|
failures += 1
|
|
}
|
|
}
|
|
|
|
await browser.close()
|
|
|
|
if (failures > 0) {
|
|
process.exitCode = 1
|
|
return
|
|
}
|
|
|
|
console.log(`All ${checks.length} route checks passed for ${baseUrl}`)
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error(error instanceof Error ? error.stack || error.message : String(error))
|
|
process.exitCode = 1
|
|
})
|