storebound/tests/browser/site-overflow.browser.ts

50 lines
2.9 KiB
TypeScript

// The site (site/index.html) never scrolls sideways: at every viewport width from 320 to 1440 px (10 px steps), in the
// light and the dark theme, document.documentElement.scrollWidth stays within innerWidth. On a failure it names the
// elements whose right edge passes the viewport outside any scroll container (pseudo-elements show as their owner).
// Uses the bundled playwright-core with its Chromium (or Google Chrome); skips when neither is installed (a launch that
// times out on a busy machine fails instead). One launch, the context closed right after each test, capped closes
// (tests/helpers/browser.ts). SLOW on purpose: 113 widths x 2 themes, each a resize and a layout read.
import { afterAll, afterEach, describe, expect, test } from 'bun:test';
import { join } from 'node:path';
import { AFTER_ALL_MS, AFTER_EACH_MS, launchTestBrowser, testScope } from '../helpers/browser';
const page = 'file://' + join(import.meta.dir, '..', '..', 'site', 'index.html');
const shared = await launchTestBrowser('playwright-core');
const { browser, skipWhy } = shared;
if (!browser) console.warn(`site overflow test skipped: no Chromium installed (${skipWhy}). Run \`bunx playwright install chromium\`.`);
const scope = testScope(() => browser);
// bounded: one context per test, each close capped (bun's 5 s default hook timeout was too tight for Chrome's slow shutdown)
afterEach(() => scope.settle(), AFTER_EACH_MS);
afterAll(() => shared.close(), AFTER_ALL_MS);
describe.skipIf(!browser)('site: no horizontal overflow', () => {
for (const scheme of ['light', 'dark'] as const) {
test(`320-1440 px, ${scheme}`, async () => {
const p = await scope.newPage({ colorScheme: scheme, viewport: { width: 1440, height: 900 } });
await p.goto(page, { waitUntil: 'load' });
const bad: string[] = [];
for (let w = 320; w <= 1440; w += 10) {
await p.setViewportSize({ width: w, height: 900 });
const r = await p.evaluate(() => {
const iw = innerWidth, sw = document.documentElement.scrollWidth;
if (sw <= iw) return null;
const els: string[] = [];
for (const el of document.querySelectorAll('body *')) {
const rc = el.getBoundingClientRect();
if (rc.width === 0 || rc.right <= iw + 0.5) continue;
let clipped = false;
for (let a = el.parentElement; a && a !== document.body; a = a.parentElement) {
if (getComputedStyle(a).overflowX !== 'visible') { clipped = true; break; }
}
if (!clipped) els.push(`${el.tagName.toLowerCase()}${el.id ? '#' + el.id : ''}${[...el.classList].map(c => '.' + c).join('')} right=${Math.round(rc.right)}`);
}
return `${iw}px: page ${sw - iw}px too wide${els.length ? ' (' + els.slice(0, 5).join(', ') + ')' : ' (a pseudo-element or transform)'}`;
});
if (r) bad.push(r);
}
expect(bad).toEqual([]);
}, 120_000);
}
});