import { expect, test } from '@playwright/test'; import { routeApplicationAssets } from './helpers/application'; for (const [locale, section] of [ ['pt', 'Seção'], ['en', 'Section'], ['es', 'Sección'], ]) { test(`renders and navigates explicit heading references through the app in ${locale}`, async ({ page }) => { test.setTimeout(90_000); const errors: string[] = []; page.on('pageerror', (error) => errors.push(error.message)); await routeApplicationAssets(page); const url = `http://127.0.0.1:4173/heading-references-${locale}.md`; const source = `# Heading references\n\n{@ref gates}\n\n${'Paragraph.\n\n'.repeat(60)}## 2.3 Logic gates {#gates}\n\n{@ref notes}\n\n## Notes {#notes}\n\n`; await page.route(url, (route) => route.fulfill({ contentType: 'text/markdown', body: source })); await page.goto(`/dist/${locale}/index.html?lang=${locale}`); await expect(page.locator('command-prompt .js-plotly-plot .main-svg').first()).toBeVisible({ timeout: 30_000 }); const input = page.locator('command-prompt textarea').last(); await input.fill(`markdown("${url}")`); await input.press('Enter'); const viewer = page.locator(`markdown-document[src="${url}"]`); await expect(viewer).toHaveAttribute('state', 'ready', { timeout: 60_000 }); await expect(viewer.locator('h2#gates')).toHaveText('2.3 Logic gates'); await expect(viewer.locator('h2#notes')).toHaveText('Notes'); const link = viewer.locator('[data-markdown-reference] a[href="#gates"]'); await expect(link).toHaveText(`${section} 2.3`); await expect(viewer.locator('[data-markdown-reference] a[href="#notes"]')).toHaveText('Notes'); const pageUrl = page.url(); await link.click(); // A heading may intersect the viewport transiently during smooth scrolling. // Assert the destination only after its geometry has stopped changing. await viewer.locator('h2#gates').evaluate(async (heading) => { let previous = heading.getBoundingClientRect().top; let stableFrames = 0; for (let frame = 0; frame < 300; frame++) { await new Promise((resolve) => requestAnimationFrame(() => resolve())); const top = heading.getBoundingClientRect().top; stableFrames = Math.abs(top - previous) < 0.25 ? stableFrames + 1 : 0; previous = top; if (stableFrames >= 15) return; } throw new Error('Heading geometry did not settle after fragment navigation'); }); await expect(viewer.locator('h2#gates')).toBeInViewport(); expect(page.url()).toBe(pageUrl); expect(await page.evaluate(() => (window as Window & { headingInjection?: boolean }).headingInjection)).toBeUndefined(); await expect(viewer.locator('[role="alert"]')).toHaveCount(0); expect(errors).toEqual([]); }); }