diff --git a/e2e/tests/journeys.spec.ts b/e2e/tests/journeys.spec.ts index f290eae..4ac1f72 100644 --- a/e2e/tests/journeys.spec.ts +++ b/e2e/tests/journeys.spec.ts @@ -478,3 +478,36 @@ test('rankings stay populated after picking a specific year', async ({ page }) = await expect(rows.first()).toBeVisible({ timeout: 15_000 }); expect(await rows.count()).toBeGreaterThan(5); }); + +test('compare metric-help popover stays within the mobile viewport', async ({ page }) => { + await page.setViewportSize({ width: 390, height: 844 }); + const [urn0, urn1] = await twoPrimaryUrns(page); + await page.goto(`/compare?urns=${urn0},${urn1}`); + + await expect( + page.getByRole('heading', { name: 'At a glance' }), + ).toBeVisible({ timeout: 15_000 }); + + // The metric-help triggers are the circled-"?" buttons in the row labels. + // "More information" is InfoPopover's default accessible name. + const help = page.getByRole('button', { name: 'More information' }).first(); + await expect(help).toBeVisible({ timeout: 15_000 }); + await help.click(); + + const tip = page.getByRole('tooltip'); + await expect(tip).toBeVisible(); + + // The whole bubble must sit inside the viewport — the original bug pushed it + // off the right edge with no way to scroll to it. + const box = await tip.boundingBox(); + const width = page.viewportSize()!.width; + expect(box).not.toBeNull(); + expect(box!.x).toBeGreaterThanOrEqual(0); + expect(box!.x + box!.width).toBeLessThanOrEqual(width); + + // And the page must not have gained a horizontal scrollbar from the bubble. + const bodyOverflowsX = await page + .locator('body') + .evaluate((el) => el.scrollWidth > el.clientWidth + 1); + expect(bodyOverflowsX).toBe(false); +});