Complete UX audit P3: API copy URLs, labels, retry, and smoke sync.
Add footer copy-to-clipboard for public APIs, align ops page labels, improve mobile brand lockup, surface WalletConnect posture on wallet tools, add account access discovery, liquidity retry alerts, and refresh smoke-route expectations. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -152,8 +152,8 @@
|
||||
|
||||
<div class="status-note" id="command-center-fallback">
|
||||
If diagram rendering is unavailable, use the main explorer operational surfaces directly:
|
||||
<a href="/operations">Operations Hub</a>,
|
||||
<a href="/bridge">Bridge Monitoring</a>,
|
||||
<a href="/operations">Operations hub</a>,
|
||||
<a href="/bridge">Bridge</a>,
|
||||
<a href="/routes">Routes</a>,
|
||||
<a href="/system">System</a>,
|
||||
and <a href="/operator">Operator</a>.
|
||||
|
||||
@@ -4,15 +4,15 @@ 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', 'Recent Blocks', 'Open wallet tools'] },
|
||||
{ path: '/', expectTexts: ['DBIS Explorer', 'Recent Blocks', 'Network overview'] },
|
||||
{ path: '/blocks', expectTexts: ['Blocks'] },
|
||||
{ path: '/transactions', expectTexts: ['Transactions'] },
|
||||
{ path: '/addresses', expectTexts: ['Addresses', 'Open An Address'] },
|
||||
{ path: '/watchlist', expectTexts: ['Watchlist', 'Saved Addresses'] },
|
||||
{ path: '/pools', expectTexts: ['Pools', 'Pool operation shortcuts'] },
|
||||
{ path: '/liquidity', expectTexts: ['Chain 138 Liquidity Access', 'Explorer Access Points'] },
|
||||
{ path: '/wallet', expectTexts: ['Wallet & MetaMask', 'Install Open Snap'] },
|
||||
{ path: '/tokens', expectTexts: ['Tokens', 'Find A Token'] },
|
||||
{ path: '/liquidity', expectTexts: ['Liquidity', 'Explorer Access Points'] },
|
||||
{ path: '/wallet', expectTexts: ['Wallet Tools', 'WalletConnect v2 posture'] },
|
||||
{ path: '/tokens', expectTexts: ['Tokens', 'Find a token'] },
|
||||
{ path: '/search', expectTexts: ['Search'], placeholder: 'Search by address, transaction hash, block number...' },
|
||||
{ path: `/addresses/${addressUnderTest}`, expectTexts: [], anyOfTexts: ['Back to addresses', 'Address not found'] },
|
||||
]
|
||||
@@ -22,7 +22,10 @@ async function bodyText(page) {
|
||||
}
|
||||
|
||||
async function hasShell(page) {
|
||||
const homeLink = await page.getByRole('link', { name: /Go to explorer home/i }).isVisible().catch(() => false)
|
||||
const homeLink = await page
|
||||
.getByRole('link', { name: /Go to DBIS Explorer home|Go to explorer home/i })
|
||||
.isVisible()
|
||||
.catch(() => false)
|
||||
const supportText = await page.getByText(/Support:/i).isVisible().catch(() => false)
|
||||
return homeLink && supportText
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export default function BrandLockup({ compact = false }: { compact?: boolean })
|
||||
</span>
|
||||
<span
|
||||
className={[
|
||||
'block truncate font-medium uppercase text-gray-500 dark:text-gray-400',
|
||||
'block truncate font-medium uppercase text-gray-500 dark:text-gray-400 max-sm:hidden',
|
||||
compact ? 'text-[0.64rem] tracking-[0.13em]' : 'text-[0.68rem] tracking-[0.12em]',
|
||||
].join(' ')}
|
||||
>
|
||||
|
||||
34
frontend/src/components/common/ExplorerRetryAlert.tsx
Normal file
34
frontend/src/components/common/ExplorerRetryAlert.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
interface ExplorerRetryAlertProps {
|
||||
message: string
|
||||
onRetry?: () => void
|
||||
retryLabel?: string
|
||||
className?: string
|
||||
}
|
||||
|
||||
export default function ExplorerRetryAlert({
|
||||
message,
|
||||
onRetry,
|
||||
retryLabel = 'Retry',
|
||||
className = '',
|
||||
}: ExplorerRetryAlertProps) {
|
||||
return (
|
||||
<div
|
||||
role="alert"
|
||||
className={[
|
||||
'flex flex-col gap-3 rounded-xl border border-red-200 bg-red-50/70 px-4 py-3 dark:border-red-900/50 dark:bg-red-950/20 sm:flex-row sm:items-center sm:justify-between',
|
||||
className,
|
||||
].join(' ')}
|
||||
>
|
||||
<p className="text-sm leading-6 text-red-900 dark:text-red-100">{message}</p>
|
||||
{onRetry ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onRetry}
|
||||
className="shrink-0 rounded-lg border border-red-300 bg-white px-3 py-1.5 text-sm font-semibold text-red-800 transition-colors hover:bg-red-50 dark:border-red-800 dark:bg-red-950 dark:text-red-100 dark:hover:bg-red-900/40"
|
||||
>
|
||||
{retryLabel}
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import Link from 'next/link'
|
||||
import { explorerPublicApiLinks } from '@/data/explorerOperations'
|
||||
import FooterPublicApiLinks from '@/components/common/FooterPublicApiLinks'
|
||||
|
||||
const footerLinkClass =
|
||||
'text-gray-600 dark:text-gray-400 hover:text-primary-600 dark:hover:text-primary-400 transition-colors'
|
||||
@@ -62,16 +62,7 @@ export default function Footer() {
|
||||
<div className="mb-3 text-sm font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">
|
||||
Public APIs
|
||||
</div>
|
||||
<ul className="space-y-3 text-sm">
|
||||
{explorerPublicApiLinks.map((link) => (
|
||||
<li key={link.href}>
|
||||
<a className={footerLinkClass} href={link.href} target="_blank" rel="noopener noreferrer">
|
||||
{link.label}
|
||||
</a>
|
||||
<p className="mt-0.5 text-xs leading-5 text-gray-500 dark:text-gray-500">{link.description}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<FooterPublicApiLinks />
|
||||
<p className="mt-3 text-xs leading-5 text-gray-500 dark:text-gray-500">
|
||||
Read-only JSON endpoints on the public explorer domain. No API key required.
|
||||
</p>
|
||||
|
||||
53
frontend/src/components/common/FooterPublicApiLinks.tsx
Normal file
53
frontend/src/components/common/FooterPublicApiLinks.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { explorerPublicApiLinks } from '@/data/explorerOperations'
|
||||
|
||||
const footerLinkClass =
|
||||
'text-gray-600 dark:text-gray-400 hover:text-primary-600 dark:hover:text-primary-400 transition-colors'
|
||||
|
||||
function absoluteApiUrl(href: string): string {
|
||||
if (typeof window === 'undefined') return href
|
||||
if (href.startsWith('http://') || href.startsWith('https://')) return href
|
||||
return `${window.location.origin}${href.startsWith('/') ? href : `/${href}`}`
|
||||
}
|
||||
|
||||
export default function FooterPublicApiLinks() {
|
||||
const [copiedHref, setCopiedHref] = useState<string | null>(null)
|
||||
|
||||
const copyUrl = async (href: string) => {
|
||||
if (typeof navigator === 'undefined' || !navigator.clipboard) return
|
||||
try {
|
||||
await navigator.clipboard.writeText(absoluteApiUrl(href))
|
||||
setCopiedHref(href)
|
||||
window.setTimeout(() => setCopiedHref((current) => (current === href ? null : current)), 1500)
|
||||
} catch {
|
||||
setCopiedHref(null)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<ul className="space-y-3 text-sm">
|
||||
{explorerPublicApiLinks.map((link) => (
|
||||
<li key={link.href}>
|
||||
<div className="flex flex-wrap items-start justify-between gap-2">
|
||||
<div className="min-w-0 flex-1">
|
||||
<a className={footerLinkClass} href={link.href} target="_blank" rel="noopener noreferrer">
|
||||
{link.label}
|
||||
</a>
|
||||
<p className="mt-0.5 text-xs leading-5 text-gray-500 dark:text-gray-500">{link.description}</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void copyUrl(link.href)}
|
||||
className="shrink-0 rounded-lg border border-gray-200 px-2.5 py-1 text-xs font-medium text-gray-700 transition-colors hover:border-primary-300 hover:text-primary-700 dark:border-gray-700 dark:text-gray-200 dark:hover:border-primary-500 dark:hover:text-primary-300"
|
||||
aria-label={`Copy URL for ${link.label}`}
|
||||
>
|
||||
{copiedHref === link.href ? 'Copied' : 'Copy URL'}
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
@@ -717,7 +717,7 @@ export default function Navbar() {
|
||||
<div className="flex min-h-[60px] items-center gap-3 lg:min-h-[64px]">
|
||||
<Link
|
||||
href="/"
|
||||
className="group inline-flex min-w-0 items-center gap-2 rounded-lg py-1.5 pr-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-950"
|
||||
className="group inline-flex shrink-0 items-center gap-2 rounded-lg py-1.5 pr-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-950 max-sm:max-w-[9.5rem] sm:max-w-none"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
aria-label="Go to DBIS Explorer home"
|
||||
>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { Card } from '@/libs/frontend-ui-primitives'
|
||||
import ExplorerRetryAlert from '@/components/common/ExplorerRetryAlert'
|
||||
import { type TokenListResponse } from '@/services/api/config'
|
||||
import { tokensApi } from '@/services/api/tokens'
|
||||
import {
|
||||
@@ -79,6 +80,7 @@ export default function LiquidityOperationsPage({
|
||||
const [stats, setStats] = useState<ExplorerStats | null>(initialStats)
|
||||
const [bridgeStatus, setBridgeStatus] = useState<MissionControlBridgeStatusResponse | null>(initialBridgeStatus)
|
||||
const [loadingError, setLoadingError] = useState<string | null>(null)
|
||||
const [reloadKey, setReloadKey] = useState(0)
|
||||
const [copiedEndpoint, setCopiedEndpoint] = useState<string | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -99,6 +101,7 @@ export default function LiquidityOperationsPage({
|
||||
}
|
||||
|
||||
const load = async () => {
|
||||
setLoadingError(null)
|
||||
const [tokenListResult, routeMatrixResult, plannerCapabilitiesResult, planResult, statsResult, bridgeResult] =
|
||||
await Promise.allSettled([
|
||||
tokensApi.listForSurface('extended', 138).then(({ ok, data }) => ({ tokens: ok ? data : [] })),
|
||||
@@ -163,6 +166,7 @@ export default function LiquidityOperationsPage({
|
||||
initialStats,
|
||||
initialTokenList,
|
||||
initialTokenPoolRecords,
|
||||
reloadKey,
|
||||
])
|
||||
|
||||
const featuredTokens = useMemo(
|
||||
@@ -266,7 +270,7 @@ export default function LiquidityOperationsPage({
|
||||
<main className="container mx-auto px-4 py-6 sm:py-8">
|
||||
<div className="mb-8 max-w-4xl">
|
||||
<div className="mb-3 inline-flex rounded-full border border-amber-200 bg-amber-50 px-3 py-1 text-xs font-semibold uppercase tracking-[0.2em] text-amber-700">
|
||||
Chain 138 Liquidity Access
|
||||
Liquidity
|
||||
</div>
|
||||
<h1 className="mb-3 text-3xl font-bold text-gray-900 dark:text-white sm:text-4xl">
|
||||
Public liquidity, route discovery, and execution access points
|
||||
@@ -282,9 +286,14 @@ export default function LiquidityOperationsPage({
|
||||
<OperationsSurfaceNav />
|
||||
|
||||
{loadingError ? (
|
||||
<Card className="mb-6 border border-red-200 bg-red-50/70 dark:border-red-900/50 dark:bg-red-950/20">
|
||||
<p className="text-sm leading-6 text-red-900 dark:text-red-100">{loadingError}</p>
|
||||
</Card>
|
||||
<ExplorerRetryAlert
|
||||
className="mb-6"
|
||||
message={loadingError}
|
||||
onRetry={() => {
|
||||
setLoadingError(null)
|
||||
setReloadKey((value) => value + 1)
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<div className="mb-6">
|
||||
|
||||
@@ -650,7 +650,7 @@ export default function Home({
|
||||
href="/bridge"
|
||||
className="inline-flex items-center justify-center rounded-xl bg-gray-900 px-4 py-2.5 text-sm font-semibold text-white hover:bg-black dark:bg-white dark:text-gray-900 dark:hover:bg-gray-100"
|
||||
>
|
||||
Open bridge monitoring
|
||||
Open bridge
|
||||
</Link>
|
||||
<Link
|
||||
href="/operations"
|
||||
@@ -996,6 +996,9 @@ export default function Home({
|
||||
<Link href="/search" className="rounded-xl border border-gray-200 px-4 py-3 text-sm font-semibold text-primary-600 hover:border-primary-400 dark:border-gray-800">
|
||||
Search
|
||||
</Link>
|
||||
<Link href="/access" className="rounded-xl border border-gray-200 px-4 py-3 text-sm font-semibold text-primary-600 hover:border-primary-400 dark:border-gray-800">
|
||||
Account access
|
||||
</Link>
|
||||
<Link href="/tokens" className="rounded-xl border border-gray-200 px-4 py-3 text-sm font-semibold text-primary-600 hover:border-primary-400 dark:border-gray-800">
|
||||
Tokens
|
||||
</Link>
|
||||
|
||||
32
frontend/src/components/wallet/WalletConnectPostureNote.tsx
Normal file
32
frontend/src/components/wallet/WalletConnectPostureNote.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { getWalletConnectConfig, type WalletConnectConfigResponse } from '@/services/api/walletConnect'
|
||||
|
||||
export default function WalletConnectPostureNote() {
|
||||
const [config, setConfig] = useState<WalletConnectConfigResponse | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
getWalletConnectConfig()
|
||||
.then((value) => {
|
||||
if (!cancelled) setConfig(value)
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) setConfig(null)
|
||||
})
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (!config) return null
|
||||
|
||||
return (
|
||||
<p className="mt-3 text-sm leading-6 text-gray-600 dark:text-gray-400">
|
||||
WalletConnect v2 posture: <strong>{config.enabled ? 'enabled' : config.status}</strong>. Browser extension wallets use{' '}
|
||||
<code className="rounded bg-gray-100 px-1.5 py-0.5 text-xs dark:bg-gray-900">{config.fallbackAuth}</code> today.
|
||||
{config.message ? ` ${config.message}` : ''}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
TokenListCatalog,
|
||||
} from '@/components/wallet/AddToMetaMask'
|
||||
import { AddToMetaMask } from '@/components/wallet/AddToMetaMask'
|
||||
import WalletConnectPostureNote from '@/components/wallet/WalletConnectPostureNote'
|
||||
import Link from 'next/link'
|
||||
import { Explain, useUiMode } from '@/components/common/UiModeContext'
|
||||
import { accessApi, type WalletAccessSession } from '@/services/api/access'
|
||||
@@ -192,6 +193,7 @@ export default function WalletPage(props: WalletPageProps) {
|
||||
? 'Use the explorer-served network catalog, token list, and capability metadata to connect Chain 138 (DeFi Oracle Meta Mainnet) and Ethereum Mainnet to MetaMask and other Web3 wallets.'
|
||||
: 'Use explorer-served network and token metadata to connect Chain 138 and Ethereum Mainnet wallets.'}
|
||||
</p>
|
||||
<WalletConnectPostureNote />
|
||||
<div className="mb-6 rounded-2xl border border-sky-200 bg-sky-50/60 p-5 dark:border-sky-900/40 dark:bg-sky-950/20">
|
||||
<div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div>
|
||||
@@ -483,7 +485,7 @@ export default function WalletPage(props: WalletPageProps) {
|
||||
<>
|
||||
Need swap and liquidity discovery too? Visit the{' '}
|
||||
<Link href="/liquidity" className="font-medium text-primary-600 hover:underline dark:text-primary-400">
|
||||
Liquidity Access
|
||||
Liquidity
|
||||
</Link>{' '}
|
||||
page for live Chain 138 pools, route matrix links, partner payload templates, and the internal fallback execution plan endpoints.
|
||||
</>
|
||||
@@ -492,7 +494,7 @@ export default function WalletPage(props: WalletPageProps) {
|
||||
<>
|
||||
Liquidity and planner posture lives on the{' '}
|
||||
<Link href="/liquidity" className="font-medium text-primary-600 hover:underline dark:text-primary-400">
|
||||
Liquidity Access
|
||||
Liquidity
|
||||
</Link>{' '}
|
||||
surface.
|
||||
</>
|
||||
|
||||
@@ -21,7 +21,7 @@ const sharedOperationsNote =
|
||||
|
||||
export const explorerFeaturePages = {
|
||||
bridge: {
|
||||
eyebrow: 'Bridge Monitoring',
|
||||
eyebrow: 'Bridge',
|
||||
title: 'Bridge & Relay Monitoring',
|
||||
description:
|
||||
'Inspect the CCIP relay status, follow the live mission-control stream, trace bridge transactions, and review the managed Mainnet, BSC, Avalanche, Avalanche cW, and Avalanche to Chain 138 lanes.',
|
||||
@@ -81,7 +81,7 @@ export const explorerFeaturePages = {
|
||||
title: 'Liquidity access',
|
||||
description: 'Review the public Chain 138 PMM access points, route helpers, and fallback execution endpoints.',
|
||||
href: '/liquidity',
|
||||
label: 'Open liquidity access',
|
||||
label: 'Open liquidity',
|
||||
},
|
||||
{
|
||||
title: 'Pools inventory',
|
||||
@@ -93,7 +93,7 @@ export const explorerFeaturePages = {
|
||||
title: 'Bridge monitoring',
|
||||
description: 'Cross-check route availability with live relay and bridge health before operator actions.',
|
||||
href: '/bridge',
|
||||
label: 'Open bridge monitoring',
|
||||
label: 'Open bridge',
|
||||
},
|
||||
{
|
||||
title: 'Operations hub',
|
||||
@@ -114,7 +114,7 @@ export const explorerFeaturePages = {
|
||||
title: 'Bridge monitoring',
|
||||
description: 'Start with relay and bridge health before reviewing WETH-specific flows.',
|
||||
href: '/bridge',
|
||||
label: 'Open bridge monitoring',
|
||||
label: 'Open bridge',
|
||||
},
|
||||
{
|
||||
title: 'Visual command center',
|
||||
@@ -175,8 +175,8 @@ export const explorerFeaturePages = {
|
||||
],
|
||||
},
|
||||
operator: {
|
||||
eyebrow: 'Operator Surface',
|
||||
title: 'Operator Surface',
|
||||
eyebrow: 'Operator',
|
||||
title: 'Operator',
|
||||
description:
|
||||
'Expose the public operator surface for bridge checks, route validation, planner providers, liquidity entry points, and documentation.',
|
||||
note: sharedOperationsNote,
|
||||
@@ -188,7 +188,7 @@ export const explorerFeaturePages = {
|
||||
title: 'Bridge monitoring',
|
||||
description: 'Open relay status, queue posture, and bridge trace tools.',
|
||||
href: '/bridge',
|
||||
label: 'Open bridge monitoring',
|
||||
label: 'Open bridge',
|
||||
},
|
||||
{
|
||||
title: 'Routes',
|
||||
@@ -200,7 +200,7 @@ export const explorerFeaturePages = {
|
||||
title: 'Liquidity access',
|
||||
description: 'Open partner payload helpers, route APIs, and execution-plan endpoints.',
|
||||
href: '/liquidity',
|
||||
label: 'Open liquidity access',
|
||||
label: 'Open liquidity',
|
||||
},
|
||||
{
|
||||
title: 'Explorer docs',
|
||||
@@ -235,7 +235,7 @@ export const explorerFeaturePages = {
|
||||
title: 'Bridge monitoring',
|
||||
description: 'Correlate topology context with the live bridge and relay status surface.',
|
||||
href: '/bridge',
|
||||
label: 'Open bridge monitoring',
|
||||
label: 'Open bridge',
|
||||
},
|
||||
{
|
||||
title: 'Explorer docs',
|
||||
@@ -252,8 +252,8 @@ export const explorerFeaturePages = {
|
||||
],
|
||||
},
|
||||
operations: {
|
||||
eyebrow: 'Operations Hub',
|
||||
title: 'Operations Hub',
|
||||
eyebrow: 'Operations hub',
|
||||
title: 'Operations hub',
|
||||
description:
|
||||
'This hub exposes the public operational surfaces for bridge monitoring, routes, wrapped-asset references, analytics shortcuts, operator links, and topology views.',
|
||||
note: sharedOperationsNote,
|
||||
@@ -262,7 +262,7 @@ export const explorerFeaturePages = {
|
||||
title: 'Bridge & relay monitoring',
|
||||
description: 'Open mission-control status, SSE monitoring, and bridge trace helpers.',
|
||||
href: '/bridge',
|
||||
label: 'Open bridge monitoring',
|
||||
label: 'Open bridge',
|
||||
},
|
||||
{
|
||||
title: 'Routes & liquidity',
|
||||
|
||||
@@ -15,6 +15,11 @@ test.describe('Explorer sprint smoke', () => {
|
||||
await expect(page.getByRole('heading', { name: /Wallet Tools/i })).toBeVisible({ timeout: 10000 })
|
||||
})
|
||||
|
||||
test('wallet page shows WalletConnect posture note', async ({ page }) => {
|
||||
await page.goto(`${EXPLORER_URL}/wallet`, { waitUntil: 'domcontentloaded', timeout: 20000 })
|
||||
await expect(page.getByText(/WalletConnect v2 posture/i)).toBeVisible({ timeout: 10000 })
|
||||
})
|
||||
|
||||
test('tokens page loads', async ({ page }) => {
|
||||
await page.goto(`${EXPLORER_URL}/tokens`, { waitUntil: 'domcontentloaded', timeout: 20000 })
|
||||
await expect(page.getByRole('heading', { name: /^Tokens$/i })).toBeVisible({ timeout: 10000 })
|
||||
@@ -28,7 +33,7 @@ test.describe('Explorer sprint smoke', () => {
|
||||
|
||||
test('operations hub loads extended token list note', async ({ page }) => {
|
||||
await page.goto(`${EXPLORER_URL}/operations`, { waitUntil: 'networkidle', timeout: 30000 })
|
||||
await expect(page.getByRole('heading', { name: /Operations Hub/i })).toBeVisible({ timeout: 10000 })
|
||||
await expect(page.getByRole('heading', { name: /Operations hub/i })).toBeVisible({ timeout: 10000 })
|
||||
await expect(page.getByText(/Extended Metamask dual-chain catalog/i).first()).toBeVisible({ timeout: 10000 })
|
||||
})
|
||||
|
||||
@@ -49,9 +54,15 @@ test.describe('Explorer sprint smoke', () => {
|
||||
await expect(page.getByRole('contentinfo').getByText(/Public APIs/i)).toBeVisible({ timeout: 10000 })
|
||||
await expect(page.getByRole('contentinfo').getByRole('link', { name: /Blockscout stats/i })).toBeVisible({ timeout: 10000 })
|
||||
await expect(page.getByRole('contentinfo').getByRole('link', { name: /Wallet tools/i })).toBeVisible({ timeout: 10000 })
|
||||
await expect(page.getByRole('contentinfo').getByRole('button', { name: /Copy URL for Blockscout stats/i })).toBeVisible({ timeout: 10000 })
|
||||
await expect(page.getByRole('contentinfo').getByRole('link', { name: /Account access/i })).toBeVisible({ timeout: 10000 })
|
||||
})
|
||||
|
||||
test('homepage quick links include account access', async ({ page }) => {
|
||||
await page.goto(`${EXPLORER_URL}/`, { waitUntil: 'domcontentloaded', timeout: 20000 })
|
||||
await expect(page.getByRole('link', { name: /Account access/i }).first()).toBeVisible({ timeout: 10000 })
|
||||
})
|
||||
|
||||
test('tablet viewport exposes mobile navigation menu', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1100, height: 800 })
|
||||
await page.goto(`${EXPLORER_URL}/`, { waitUntil: 'domcontentloaded', timeout: 20000 })
|
||||
@@ -68,7 +79,7 @@ test.describe('Explorer sprint smoke', () => {
|
||||
|
||||
test('operator page shows track 4 surface note', async ({ page }) => {
|
||||
await page.goto(`${EXPLORER_URL}/operator`, { waitUntil: 'domcontentloaded', timeout: 30000 })
|
||||
await expect(page.getByRole('heading', { name: /^Operator Surface$/i })).toBeVisible({ timeout: 15000 })
|
||||
await expect(page.getByRole('heading', { name: /^Operator$/i })).toBeVisible({ timeout: 15000 })
|
||||
await expect(page.getByText(/Track 4 public surface/i).first()).toBeVisible({ timeout: 10000 })
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user