/** * Explorer Frontend E2E Tests * Tests all links and path-based routing 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'; 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); }); 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('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('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('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('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('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.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 }); } }); });